Compare commits
823 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08bb47da1d | ||
|
|
4fad3808a2 | ||
|
|
f05a875a05 | ||
|
|
4f7f2ba361 | ||
|
|
0cfe87e65c | ||
|
|
5ec9944f2a | ||
|
|
fa339a5183 | ||
|
|
5936a06ad1 | ||
|
|
c21bbf1022 | ||
|
|
00fa17b31f | ||
|
|
6e03c8d236 | ||
|
|
7022ef5f7e | ||
|
|
54d590765f | ||
|
|
d24c572d7f | ||
|
|
010be95ed5 | ||
|
|
59ca8cb2c6 | ||
|
|
15dc0ad9ac | ||
|
|
f690ea01c4 | ||
|
|
9c09a8128a | ||
|
|
16779d496e | ||
|
|
3ab7318b25 | ||
|
|
a2b7e039d5 | ||
|
|
a68e88826f | ||
|
|
7c8545c21b | ||
|
|
d17c3b4aa3 | ||
|
|
a0529b1cdd | ||
|
|
1f0c77c3bf | ||
|
|
56acfab2b2 | ||
|
|
256cef9047 | ||
|
|
7d3750e54c | ||
|
|
f021d080af | ||
|
|
293400fd76 | ||
|
|
c495ca5256 | ||
|
|
956c7bf93a | ||
|
|
219d88851e | ||
|
|
9b9fb62596 | ||
|
|
f636e7dbd4 | ||
|
|
33f2341f7f | ||
|
|
f5bbcb2777 | ||
|
|
5a56081a29 | ||
|
|
d2b73a8942 | ||
|
|
09a35cf10a | ||
|
|
eb7ac7bfc4 | ||
|
|
a98636ed39 | ||
|
|
73a2a0a127 | ||
|
|
494dc72eb9 | ||
|
|
4665dbc372 | ||
|
|
0b6671d538 | ||
|
|
392c97f35f | ||
|
|
8629eda6d4 | ||
|
|
8507f11175 | ||
|
|
ae6d33febd | ||
|
|
54e11170fb | ||
|
|
66eecf5e37 | ||
|
|
f7b1e876ad | ||
|
|
ccff68ad18 | ||
|
|
112c140ce7 | ||
|
|
c2cd9f0865 | ||
|
|
749e89e362 | ||
|
|
7241bbbb6d | ||
|
|
edb7bef5a3 | ||
|
|
436de528e9 | ||
|
|
7f0f932aca | ||
|
|
73d2d58da9 | ||
|
|
777b201c1f | ||
|
|
11ff85c56c | ||
|
|
a9ed1c0fe8 | ||
|
|
9a06b19288 | ||
|
|
516610a820 | ||
|
|
8b989924a4 | ||
|
|
bd3e1b7592 | ||
|
|
af18b95ffa | ||
|
|
9ecec8ce17 | ||
|
|
f2f097bb7e | ||
|
|
27da1c8e49 | ||
|
|
015fd24e03 | ||
|
|
1306b78f63 | ||
|
|
ad0b342d89 | ||
|
|
6d463dcfef | ||
|
|
965923116d | ||
|
|
3281adce65 | ||
|
|
74f6be6123 | ||
|
|
74b997ba49 | ||
|
|
b7c6f3e2bb | ||
|
|
8506275f90 | ||
|
|
64c3a82189 | ||
|
|
23b1d94808 | ||
|
|
0a9712d227 | ||
|
|
6575d03f45 | ||
|
|
6037daedcd | ||
|
|
a351abd5d9 | ||
|
|
8b00bb59de | ||
|
|
910c20bd00 | ||
|
|
885318623f | ||
|
|
e1df6105ed | ||
|
|
c4879d598b | ||
|
|
cc9cfa53fa | ||
|
|
996bb206c6 | ||
|
|
df0ac3b7c8 | ||
|
|
df68f9ac25 | ||
|
|
a639ee63dd | ||
|
|
04feb3cdbe | ||
|
|
f77f0a59ec | ||
|
|
fdc5695fb4 | ||
|
|
f24477e7b4 | ||
|
|
0e561e7bc2 | ||
|
|
257cb5141b | ||
|
|
89312347d1 | ||
|
|
f1e752892d | ||
|
|
6a13b8c3c5 | ||
|
|
c43a864480 | ||
|
|
06d58547c8 | ||
|
|
173e3a3fc0 | ||
|
|
d0cf780ea2 | ||
|
|
493538ae70 | ||
|
|
955bb5c99d | ||
|
|
88fc45975f | ||
|
|
09a88b4a97 | ||
|
|
67e98ef46b | ||
|
|
85c5f16e93 | ||
|
|
7cf5345c5a | ||
|
|
4c62baa577 | ||
|
|
0f55b8ee8a | ||
|
|
ad041a5cf1 | ||
|
|
3bd22f6b78 | ||
|
|
fa15457ce4 | ||
|
|
ef434ca804 | ||
|
|
2b165a065c | ||
|
|
12b7e22c31 | ||
|
|
d77e3745bb | ||
|
|
5429e493ea | ||
|
|
b05cd2e6e4 | ||
|
|
a9564086b0 | ||
|
|
c32c6e0363 | ||
|
|
f9008df1c6 | ||
|
|
9a2fa9e2c7 | ||
|
|
dd8f7d078b | ||
|
|
b40d5a75f2 | ||
|
|
01e41ba1b2 | ||
|
|
1ed28a717c | ||
|
|
1d6d8af748 | ||
|
|
f80c5d6a46 | ||
|
|
6c7643a4c3 | ||
|
|
563ac1d2fb | ||
|
|
91511c09f3 | ||
|
|
82180a54f1 | ||
|
|
aee2863caf | ||
|
|
ac705a6900 | ||
|
|
5b4f3f5b67 | ||
|
|
94c2440832 | ||
|
|
5d2ba22988 | ||
|
|
cf9fe0c02c | ||
|
|
4c57139cdc | ||
|
|
2c0b8b5f8e | ||
|
|
2634eb10be | ||
|
|
101d7807ff | ||
|
|
d707ba3bad | ||
|
|
d35d3061e5 | ||
|
|
f93fc4cf62 | ||
|
|
870484d06a | ||
|
|
f816ad0841 | ||
|
|
a55eb11f5b | ||
|
|
dacf479838 | ||
|
|
568fd873e4 | ||
|
|
d2bf0b7749 | ||
|
|
e68bfc0557 | ||
|
|
60c82fa144 | ||
|
|
aca53be909 | ||
|
|
aefe0abede | ||
|
|
e3221aedaa | ||
|
|
505268c037 | ||
|
|
363902a588 | ||
|
|
dc236c6bf1 | ||
|
|
f16e6148b4 | ||
|
|
9e615c91cb | ||
|
|
16491d36de | ||
|
|
3e126cdfa4 | ||
|
|
51fcec3369 | ||
|
|
2077dc2eee | ||
|
|
4487d90fec | ||
|
|
f088894ee1 | ||
|
|
8c9cf86d91 | ||
|
|
f41d8ca868 | ||
|
|
048776b53a | ||
|
|
ac634708cc | ||
|
|
54e4dbc4ac | ||
|
|
254b500f03 | ||
|
|
c057c7ea24 | ||
|
|
3e27157eaa | ||
|
|
0da1f3a406 | ||
|
|
113939c273 | ||
|
|
708043dd6b | ||
|
|
bc4c59694c | ||
|
|
63b7035e74 | ||
|
|
d8d5707f33 | ||
|
|
24584cdadd | ||
|
|
bbaa48c1ec | ||
|
|
92d8878c38 | ||
|
|
282f3c47ed | ||
|
|
65e9e7a73b | ||
|
|
ab336de732 | ||
|
|
2efc549aca | ||
|
|
7275e8075b | ||
|
|
118a7aa258 | ||
|
|
6bcd532767 | ||
|
|
6eb9ae8ad5 | ||
|
|
b450cd7ee4 | ||
|
|
c8568ae15a | ||
|
|
65e1221298 | ||
|
|
b4a7c45b61 | ||
|
|
211dfa24fa | ||
|
|
1fdde89d49 | ||
|
|
94d0bf59b2 | ||
|
|
05fe9dcccf | ||
|
|
f89f27698e | ||
|
|
4931804056 | ||
|
|
67bcf91073 | ||
|
|
87be0b3d3d | ||
|
|
27585d5c93 | ||
|
|
7d003cefd7 | ||
|
|
f132de8dbb | ||
|
|
b0c22a2b2d | ||
|
|
9b8936f53c | ||
|
|
c3ecf98b62 | ||
|
|
5481c28e4b | ||
|
|
4ad4bad295 | ||
|
|
5119505475 | ||
|
|
8b7fd238d0 | ||
|
|
df21deddba | ||
|
|
91b29b893b | ||
|
|
32c7ece2ef | ||
|
|
dbbf310e83 | ||
|
|
52fe6474e2 | ||
|
|
81654c35da | ||
|
|
32d57814a9 | ||
|
|
860993663f | ||
|
|
ea7feee840 | ||
|
|
a556e6d0c2 | ||
|
|
bf686736f8 | ||
|
|
56c7ec3750 | ||
|
|
a7cd6395b7 | ||
|
|
2d6d1b2d34 | ||
|
|
a235d38240 | ||
|
|
43d95a2271 | ||
|
|
40e05a0461 | ||
|
|
b60bc1ee39 | ||
|
|
bb973ee449 | ||
|
|
e16019dcbf | ||
|
|
01a890bce9 | ||
|
|
fcdf93c5e5 | ||
|
|
a34acfd0c5 | ||
|
|
2853a4bbef | ||
|
|
8bc1df9b8e | ||
|
|
29e96ddd12 | ||
|
|
a852432c9f | ||
|
|
c7e5fc47ba | ||
|
|
19a2323880 | ||
|
|
fb4554abe3 | ||
|
|
cd2680135d | ||
|
|
8ae5dbc375 | ||
|
|
744d647704 | ||
|
|
77e51f3301 | ||
|
|
f0cfee56f2 | ||
|
|
61bf735315 | ||
|
|
b2be9dce6f | ||
|
|
8b970ed9a0 | ||
|
|
5458debe35 | ||
|
|
dd970af6b1 | ||
|
|
d9feffa630 | ||
|
|
efe21d450e | ||
|
|
7728231cad | ||
|
|
3ecf62857c | ||
|
|
b8beac37c3 | ||
|
|
aa056ff1c1 | ||
|
|
23199979e6 | ||
|
|
b3dd2ebf31 | ||
|
|
0cec1b977a | ||
|
|
3b96f85c55 | ||
|
|
8e86e4c8e8 | ||
|
|
a1568a98d4 | ||
|
|
c61d825c37 | ||
|
|
730018b45f | ||
|
|
d8af767dc4 | ||
|
|
82756a9c55 | ||
|
|
fe81a7dba9 | ||
|
|
9d085e301d | ||
|
|
5206aeead0 | ||
|
|
6a93e56747 | ||
|
|
15e5edb1a3 | ||
|
|
2b17b9b218 | ||
|
|
808e4545ae | ||
|
|
2da4c385ba | ||
|
|
b6e5f7f337 | ||
|
|
8b39da6043 | ||
|
|
9e43bbcce2 | ||
|
|
a9224b94b1 | ||
|
|
951266e529 | ||
|
|
2a51ec6d9f | ||
|
|
cc5966e90c | ||
|
|
76d2ca3e37 | ||
|
|
c4c2f1cbee | ||
|
|
f7a67ec30f | ||
|
|
60676addef | ||
|
|
c4a48f1037 | ||
|
|
217cdad775 | ||
|
|
3dac917cc1 | ||
|
|
93acb03297 | ||
|
|
7c530550f9 | ||
|
|
aafb4dc901 | ||
|
|
497a4dfde6 | ||
|
|
5070d9fe95 | ||
|
|
4b48d8a8c8 | ||
|
|
ebb17489b7 | ||
|
|
1e2797a681 | ||
|
|
2faf675c0a | ||
|
|
c8878aba57 | ||
|
|
061f86fbfa | ||
|
|
1b783fdfa0 | ||
|
|
e7a15c9242 | ||
|
|
36c984d152 | ||
|
|
bd008f42dd | ||
|
|
8690d18695 | ||
|
|
3a8b4337f0 | ||
|
|
5ef1b5b75c | ||
|
|
3726f0a376 | ||
|
|
61d7dcf61b | ||
|
|
dce58bdb2f | ||
|
|
311d87b223 | ||
|
|
2214645a96 | ||
|
|
119eb321ec | ||
|
|
5fffc5d29e | ||
|
|
9838a68ff7 | ||
|
|
64222327c9 | ||
|
|
e1423033a6 | ||
|
|
dcd148e43b | ||
|
|
daf214275b | ||
|
|
a4b89a2152 | ||
|
|
9e5a162d74 | ||
|
|
3ba279c81b | ||
|
|
836bd9ab3c | ||
|
|
12ef2f4465 | ||
|
|
4ce673acfd | ||
|
|
80f7d3120a | ||
|
|
1a04d79fb9 | ||
|
|
ee49f8d26c | ||
|
|
bbb559ad2c | ||
|
|
5dab781fbe | ||
|
|
2f3c3aeba9 | ||
|
|
7f8b49dd1d | ||
|
|
14f03e61ef | ||
|
|
bf08883f15 | ||
|
|
f1b22e7122 | ||
|
|
caf33ba87e | ||
|
|
46d5ffc05b | ||
|
|
086b2de505 | ||
|
|
908d672cf2 | ||
|
|
d13e6f1897 | ||
|
|
de480c457a | ||
|
|
493b6cd576 | ||
|
|
7296873562 | ||
|
|
85fd5cf0dc | ||
|
|
bb9db3b1ca | ||
|
|
35a0d50918 | ||
|
|
57f7621567 | ||
|
|
bf1a143f03 | ||
|
|
355688abf8 | ||
|
|
f5efb425cd | ||
|
|
cc396aecd3 | ||
|
|
faa420753f | ||
|
|
dbba0cf847 | ||
|
|
725fa31e77 | ||
|
|
bb924dc250 | ||
|
|
b0346aa340 | ||
|
|
671fca0736 | ||
|
|
322945ec99 | ||
|
|
1c6f87784b | ||
|
|
9d56af4160 | ||
|
|
fdedb1f231 | ||
|
|
161c0b7b88 | ||
|
|
f847cbe122 | ||
|
|
df48e85de5 | ||
|
|
2a818dc81d | ||
|
|
83a20bd7de | ||
|
|
2e7e40c4cc | ||
|
|
b98d9074bb | ||
|
|
5377c69b40 | ||
|
|
d24b917c17 | ||
|
|
d99ede8c05 | ||
|
|
3f727d6f71 | ||
|
|
99acc4921c | ||
|
|
db7dcba1b9 | ||
|
|
8f76d3fa58 | ||
|
|
0930b1ada8 | ||
|
|
35df7c6429 | ||
|
|
e679e8c5f4 | ||
|
|
d43a655116 | ||
|
|
56a4ca0e21 | ||
|
|
f17b126dd4 | ||
|
|
1ac144e3a7 | ||
|
|
13402a5aa5 | ||
|
|
92a0cc245f | ||
|
|
8b9a2c5f7e | ||
|
|
bc10e4304f | ||
|
|
d40de785b7 | ||
|
|
9ff02eefb8 | ||
|
|
e8837e69a0 | ||
|
|
9d02f2687d | ||
|
|
9f2ecd619f | ||
|
|
6b899b2ce0 | ||
|
|
d5cb66079b | ||
|
|
5690599241 | ||
|
|
3ee2bae78d | ||
|
|
21041fc6da | ||
|
|
2b40db285e | ||
|
|
c3a6cc133f | ||
|
|
10f6bb9cc6 | ||
|
|
5589c61423 | ||
|
|
158c34d091 | ||
|
|
99f182599a | ||
|
|
216bd4e7b4 | ||
|
|
ecfc7c84c3 | ||
|
|
d4819bcd0a | ||
|
|
b067a4723b | ||
|
|
9217ebf75e | ||
|
|
305a8f74b0 | ||
|
|
a3666b3d44 | ||
|
|
182bca9361 | ||
|
|
f0868f383b | ||
|
|
001c4818a0 | ||
|
|
0d47a1a1c2 | ||
|
|
75f4d8e09c | ||
|
|
2e5d8b330b | ||
|
|
42e0200956 | ||
|
|
780bc55a96 | ||
|
|
bc6ea34f14 | ||
|
|
ae237f1ad3 | ||
|
|
7451a0bcc4 | ||
|
|
ffed1dbc90 | ||
|
|
3ffe5559e7 | ||
|
|
2c93e2f783 | ||
|
|
7ef5919ba1 | ||
|
|
4c19a7d598 | ||
|
|
feb782270b | ||
|
|
29c6109ea3 | ||
|
|
ffe4796f2c | ||
|
|
5762cea9bd | ||
|
|
7b0a1c4266 | ||
|
|
d2086b1661 | ||
|
|
c50cbc9750 | ||
|
|
24ee395b73 | ||
|
|
48bd772171 | ||
|
|
928ace5ba6 | ||
|
|
0791d196d4 | ||
|
|
9223044967 | ||
|
|
998a925fd1 | ||
|
|
f49bfb4d74 | ||
|
|
b20cb040b0 | ||
|
|
82096f3a1e | ||
|
|
fcc6f5aa76 | ||
|
|
4c82ef89fb | ||
|
|
9b7141b1f4 | ||
|
|
965133d6e2 | ||
|
|
498b3f80bf | ||
|
|
6cf9044db6 | ||
|
|
acc80cff74 | ||
|
|
9e2407b7f1 | ||
|
|
6da9beb273 | ||
|
|
1104f22f19 | ||
|
|
cd272ab0c8 | ||
|
|
34dbec4a5c | ||
|
|
3f0608fd9c | ||
|
|
17a6b03d00 | ||
|
|
5efb921a6b | ||
|
|
01d4443a4f | ||
|
|
1591205f46 | ||
|
|
ad005d99d2 | ||
|
|
27e1541a6d | ||
|
|
9fb966f705 | ||
|
|
096b981247 | ||
|
|
cea9ac3965 | ||
|
|
f446c9acc1 | ||
|
|
870521c004 | ||
|
|
05114b6dfc | ||
|
|
945a83625d | ||
|
|
e2d7c0225e | ||
|
|
55e5444a71 | ||
|
|
a3e8a4a41f | ||
|
|
b91798e8b8 | ||
|
|
d284002803 | ||
|
|
74e6bf50b1 | ||
|
|
60ba539104 | ||
|
|
979909ad57 | ||
|
|
c4bd471516 | ||
|
|
cd8b4d0dd1 | ||
|
|
d018ebda7e | ||
|
|
5b9aeb70d9 | ||
|
|
35d551f05e | ||
|
|
a32eb24f8c | ||
|
|
5c7f3c282b | ||
|
|
c19c399508 | ||
|
|
4d24d49a0b | ||
|
|
cbe214113c | ||
|
|
fdc2d2cb17 | ||
|
|
eae1d22a2f | ||
|
|
3d66a7deb9 | ||
|
|
a4022dc70c | ||
|
|
f82c0cdb4f | ||
|
|
6e208c3766 | ||
|
|
777fe9b42a | ||
|
|
afbf21c47f | ||
|
|
461c30580f | ||
|
|
82cdcc1cce | ||
|
|
3efcaf4921 | ||
|
|
2be4b672d5 | ||
|
|
2b4d743d79 | ||
|
|
c80634b026 | ||
|
|
0028d03e1b | ||
|
|
d687c9f36d | ||
|
|
5fbf4879ae | ||
|
|
a719dff1f7 | ||
|
|
d99170f0d9 | ||
|
|
7d5a8499c3 | ||
|
|
1cfaf0bd57 | ||
|
|
bc3c608277 | ||
|
|
2f09995306 | ||
|
|
72acdeaab9 | ||
|
|
0bfc0256a6 | ||
|
|
c1a733af53 | ||
|
|
c3544076ba | ||
|
|
ae528f6302 | ||
|
|
6a63325f10 | ||
|
|
395d244e04 | ||
|
|
d9f0e61375 | ||
|
|
b3751ec4bf | ||
|
|
a11a24bfe2 | ||
|
|
b31de72ccc | ||
|
|
fa4c08dcc5 | ||
|
|
d0aa75c792 | ||
|
|
492a5bd5e9 | ||
|
|
772fdd5e87 | ||
|
|
c4116d6819 | ||
|
|
050a53af0d | ||
|
|
a42136c419 | ||
|
|
c5d5c15b11 | ||
|
|
2360803e44 | ||
|
|
3f5752247b | ||
|
|
1e85a649db | ||
|
|
84eed2aab2 | ||
|
|
32f5e784e8 | ||
|
|
53e391982f | ||
|
|
0f8adf13ae | ||
|
|
a91e265097 | ||
|
|
b7fc72004f | ||
|
|
9927239ec9 | ||
|
|
cff94a1672 | ||
|
|
a0191bafe7 | ||
|
|
f2262c0e19 | ||
|
|
068d251b64 | ||
|
|
1872dacb5e | ||
|
|
ef162dd963 | ||
|
|
2a19285d3c | ||
|
|
38a9a75b2d | ||
|
|
6959a4510e | ||
|
|
fe383d32a7 | ||
|
|
560bdb139a | ||
|
|
fe14286bc8 | ||
|
|
7584b489ac | ||
|
|
84461ffa35 | ||
|
|
d89c652622 | ||
|
|
9d19d26d8e | ||
|
|
e307d51f24 | ||
|
|
4632e281a6 | ||
|
|
ab4f961795 | ||
|
|
9859a34e90 | ||
|
|
b10a209daf | ||
|
|
2a81c2611b | ||
|
|
8d98c8ad16 | ||
|
|
1c12c66b31 | ||
|
|
cb79567942 | ||
|
|
c35a9c64f6 | ||
|
|
95832eb657 | ||
|
|
ee53c12c35 | ||
|
|
c3d2d928b3 | ||
|
|
87e598b5f0 | ||
|
|
f61cbba8cb | ||
|
|
a4089efe85 | ||
|
|
781d8e3b68 | ||
|
|
cf81a970cc | ||
|
|
367d73ef23 | ||
|
|
314084d092 | ||
|
|
e4441d129c | ||
|
|
292550f6b5 | ||
|
|
93364afc98 | ||
|
|
558b961afa | ||
|
|
d99b5cf525 | ||
|
|
c75e4a52eb | ||
|
|
0ac01fe8e5 | ||
|
|
13615e06e9 | ||
|
|
623b177eb0 | ||
|
|
a74522c465 | ||
|
|
4f5788fe31 | ||
|
|
d739af3d4b | ||
|
|
3a2284cf99 | ||
|
|
9019babf6d | ||
|
|
5382cd8d5f | ||
|
|
37c41086d2 | ||
|
|
2697cce6dd | ||
|
|
294b26787b | ||
|
|
b67de81616 | ||
|
|
36568d5720 | ||
|
|
dcf1966719 | ||
|
|
33cbc9b525 | ||
|
|
d2115ab004 | ||
|
|
9cbd6ea7b7 | ||
|
|
ded69f979e | ||
|
|
7fab6f4732 | ||
|
|
9ad822577b | ||
|
|
bdaac40435 | ||
|
|
19b43e152a | ||
|
|
c4c320da83 | ||
|
|
43cd6504b6 | ||
|
|
cb8734bba7 | ||
|
|
57860c17d9 | ||
|
|
c56c162045 | ||
|
|
249407403d | ||
|
|
1aa5ec6aa4 | ||
|
|
f129ead9a0 | ||
|
|
321a45615a | ||
|
|
dd280732d1 | ||
|
|
8966e6fd55 | ||
|
|
3523b5f2c7 | ||
|
|
2f727b1a1e | ||
|
|
6e537eed58 | ||
|
|
ffb8fd0172 | ||
|
|
ba1410c7f4 | ||
|
|
61f8e36383 | ||
|
|
20a7094d33 | ||
|
|
06d7534462 | ||
|
|
b3680224cc | ||
|
|
113bf14718 | ||
|
|
937df4486e | ||
|
|
5f79ca2872 | ||
|
|
e999d7428a | ||
|
|
146df237f2 | ||
|
|
d802945ec7 | ||
|
|
122782f244 | ||
|
|
ff006a254a | ||
|
|
c1d11e7489 | ||
|
|
7d78d34de0 | ||
|
|
5f97f49f28 | ||
|
|
f63bb11cc3 | ||
|
|
b23f0a9c16 | ||
|
|
e609300533 | ||
|
|
f623b31220 | ||
|
|
a17239ca31 | ||
|
|
f2a35a7716 | ||
|
|
0383403cce | ||
|
|
57b71ce4ea | ||
|
|
f6752e9743 | ||
|
|
45fa84d5ae | ||
|
|
873b97b052 | ||
|
|
7a25f4c13c | ||
|
|
48e9171153 | ||
|
|
1dbea4d39a | ||
|
|
edba562a99 | ||
|
|
60368341ed | ||
|
|
ab24faaa55 | ||
|
|
e79b113907 | ||
|
|
fb69fcee3c | ||
|
|
41ff83821b | ||
|
|
463ef82255 | ||
|
|
5d011b09ae | ||
|
|
638f3f1a05 | ||
|
|
879fc58d9c | ||
|
|
2c139c2a65 | ||
|
|
4c9c9a2240 | ||
|
|
354ad3983f | ||
|
|
8d99d4aa99 | ||
|
|
1ab2de9c69 | ||
|
|
6a938cdcca | ||
|
|
0c705bfa6d | ||
|
|
6169f1c150 | ||
|
|
3639ed8f70 | ||
|
|
2083e02698 | ||
|
|
a7d483ea1b | ||
|
|
a0dd02ec07 | ||
|
|
929d955237 | ||
|
|
f61eeebc8f | ||
|
|
f8c3d1e6db | ||
|
|
38513d3eed | ||
|
|
051259419c | ||
|
|
fad28ee40b | ||
|
|
1d9778226c | ||
|
|
f1dab84571 | ||
|
|
c413317970 | ||
|
|
631c8b625b | ||
|
|
2dd9683eb1 | ||
|
|
f7faac1afc | ||
|
|
91d0422f53 | ||
|
|
a22090e3df | ||
|
|
62f52a0be1 | ||
|
|
eca1dc8e66 | ||
|
|
8e3542863a | ||
|
|
34be6ce795 | ||
|
|
2d995b87b1 | ||
|
|
edbb81d089 | ||
|
|
6c4fbb501c | ||
|
|
5f3ca632cb | ||
|
|
3db28e4f22 | ||
|
|
79a7b3c985 | ||
|
|
109d57b4b4 | ||
|
|
71779d560a | ||
|
|
967bf49db0 | ||
|
|
3cedd48503 | ||
|
|
0c303b63bd | ||
|
|
ef070dbad7 | ||
|
|
d88e777f80 | ||
|
|
cffdd56522 | ||
|
|
d44b821a04 | ||
|
|
0c6a59282e | ||
|
|
3ba2aa922f | ||
|
|
ce8e2c6684 | ||
|
|
d3aa8e03a2 | ||
|
|
3ae9c49029 | ||
|
|
bac0db10a6 | ||
|
|
a40d67138b | ||
|
|
a8f6df16a8 | ||
|
|
63f23118b6 | ||
|
|
71de9cfec5 | ||
|
|
5f8729536c | ||
|
|
461dd21806 | ||
|
|
d5a44fdec4 | ||
|
|
0fd96aa92e | ||
|
|
3ba6de6f34 | ||
|
|
d214be027d | ||
|
|
af7273e7eb | ||
|
|
69971198af | ||
|
|
23a681f214 | ||
|
|
7516524d38 | ||
|
|
474711431e | ||
|
|
3cea9de8fe | ||
|
|
9235b0ffb6 | ||
|
|
a159275dba | ||
|
|
105005aa07 | ||
|
|
40a8d45ab0 | ||
|
|
009eae83a6 | ||
|
|
c146879fd3 | ||
|
|
00c6e22861 | ||
|
|
939a74b7be | ||
|
|
a53db858e3 | ||
|
|
38da938ec1 | ||
|
|
8cf1a37633 | ||
|
|
9de8c4cf04 | ||
|
|
40be148c9b | ||
|
|
dfbb563b6f | ||
|
|
ab76afb322 | ||
|
|
4b3593d081 | ||
|
|
210e182d7e | ||
|
|
6f1154a1f8 | ||
|
|
d935ff4b3e | ||
|
|
cd92d87c5d | ||
|
|
e8ac8ac0ab | ||
|
|
7ae7bdd670 | ||
|
|
1dbb89df34 | ||
|
|
b9908f68f9 | ||
|
|
b839e8183f | ||
|
|
51b370efa6 | ||
|
|
f65e67d86a | ||
|
|
653e42c2b9 | ||
|
|
3b6c8247c5 | ||
|
|
f457cc5107 | ||
|
|
f9e62f3237 | ||
|
|
4ca78a2fe5 | ||
|
|
179010ddbf | ||
|
|
4cca931d9d | ||
|
|
3c2743510b | ||
|
|
716434d59e | ||
|
|
4accbda497 | ||
|
|
9f0512e81e | ||
|
|
10b699a6f7 | ||
|
|
1a9b4cdbf5 | ||
|
|
ee8f133ebb | ||
|
|
b193625fb6 | ||
|
|
ca3abed605 | ||
|
|
3b2fd26bd9 | ||
|
|
1b21187397 | ||
|
|
c2519bdb7d | ||
|
|
797edc50f4 | ||
|
|
6ffbd88d92 | ||
|
|
377cd2ba1f | ||
|
|
14a67fa698 | ||
|
|
efe1c7ba45 | ||
|
|
96cf5f43d2 | ||
|
|
4ed461bc69 | ||
|
|
78fd2d5399 | ||
|
|
cf1d07e7a6 | ||
|
|
f71b940197 | ||
|
|
aae182a9f8 | ||
|
|
2162f52e00 | ||
|
|
7a88d2d08c | ||
|
|
896c4e7561 | ||
|
|
c537477d3a | ||
|
|
2a9adfd180 | ||
|
|
7a60cc25e9 | ||
|
|
73255c9c75 | ||
|
|
840af00d6d | ||
|
|
c3a58eec8d | ||
|
|
e18b8f6bb6 | ||
|
|
48d4e69fc8 | ||
|
|
c388eddc67 | ||
|
|
dcf5c2a0d6 | ||
|
|
7d070810d5 | ||
|
|
5923c88a94 | ||
|
|
a45770119c | ||
|
|
d3d3cda758 | ||
|
|
8ae157a272 | ||
|
|
8398db90ef | ||
|
|
177585c998 | ||
|
|
7ac9c34820 | ||
|
|
5e424bb64d | ||
|
|
d290b25f8a | ||
|
|
90c3ca47b9 | ||
|
|
924a931568 |
@@ -52,15 +52,6 @@
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "terranblake",
|
||||
"name": "Terran Blake",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/8795767?v=4",
|
||||
"profile": "https://www.lumahealth.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "AndrewBastin",
|
||||
"name": "Andrew Bastin",
|
||||
|
||||
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,4 +1,3 @@
|
||||
ko_fi: liyasthomas
|
||||
open_collective: liyasthomas
|
||||
open_collective: postwoman
|
||||
patreon: liyasthomas
|
||||
custom: https://www.paypal.me/liyascthomas
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -98,3 +98,9 @@ sw.*
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
|
||||
# Tests screenshots
|
||||
tests/*/screenshots
|
||||
|
||||
# Tests videos
|
||||
tests/*/videos
|
||||
|
||||
@@ -15,6 +15,8 @@ language: node_js
|
||||
node_js:
|
||||
- "12"
|
||||
|
||||
os: linux
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -38,6 +40,7 @@ install:
|
||||
- "npm install"
|
||||
|
||||
before_script:
|
||||
- "npm run build" # use nuxt build and start to run tests
|
||||
- "npm run test"
|
||||
|
||||
script:
|
||||
|
||||
456
CHANGELOG.md
456
CHANGELOG.md
@@ -1,3 +1,457 @@
|
||||
# Changelog
|
||||
|
||||
* [v0.1.0](https://github.com/liyasthomas/postwoman/releases/tag/v0.1.0) - Initial 🎉 Initial public release
|
||||
## [v1.9.0](https://github.com/liyasthomas/postwoman/tree/v1.9.0) (2020-02-24)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.8.0...v1.9.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Disable SSL cert for websockets [\#557](https://github.com/liyasthomas/postwoman/issues/557)
|
||||
- Feature request: Keyboard shortcuts for folder creation [\#539](https://github.com/liyasthomas/postwoman/issues/539)
|
||||
- Friendly minded GraphQL [\#468](https://github.com/liyasthomas/postwoman/issues/468)
|
||||
- multipart/form-data support [\#434](https://github.com/liyasthomas/postwoman/issues/434)
|
||||
- Implement pre-request and post-request scripts \(and request chaining\) [\#218](https://github.com/liyasthomas/postwoman/issues/218)
|
||||
- Environment management and configuration [\#147](https://github.com/liyasthomas/postwoman/issues/147)
|
||||
- POST request body editor reacts to the content type [\#594](https://github.com/liyasthomas/postwoman/pull/594) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Environment Mangement [\#591](https://github.com/liyasthomas/postwoman/pull/591) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||
- GraphQL Query Autocompletion [\#590](https://github.com/liyasthomas/postwoman/pull/590) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Postman collection parsing [\#574](https://github.com/liyasthomas/postwoman/pull/574) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||
- Added toggle to decide whether extensions should be used [\#551](https://github.com/liyasthomas/postwoman/pull/551) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Show Ctrl instead of Command for shortcuts non-Apple platforms [\#549](https://github.com/liyasthomas/postwoman/pull/549) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Updated GraphQL Query Variable Editor [\#534](https://github.com/liyasthomas/postwoman/pull/534) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Auto Theme Selection is kinda difficult to see [\#563](https://github.com/liyasthomas/postwoman/issues/563)
|
||||
- Can't send request to localhost via Chrome extention [\#560](https://github.com/liyasthomas/postwoman/issues/560)
|
||||
- Validation for duplicate collection ignores letter case [\#547](https://github.com/liyasthomas/postwoman/issues/547)
|
||||
- Build failed [\#327](https://github.com/liyasthomas/postwoman/issues/327)
|
||||
- Fixed typo in translation file for Auto theme [\#556](https://github.com/liyasthomas/postwoman/pull/556) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- don't run [\#577](https://github.com/liyasthomas/postwoman/issues/577)
|
||||
- Get correct response data but occurs with error "Cannot read property 'value' of undefined" [\#575](https://github.com/liyasthomas/postwoman/issues/575)
|
||||
- firebase\_app\_\_WEBPACK\_IMPORTED\_MODULE\_2\_\_\_default.a.firestore is not a function [\#558](https://github.com/liyasthomas/postwoman/issues/558)
|
||||
- relative module not found during start [\#552](https://github.com/liyasthomas/postwoman/issues/552)
|
||||
- Feature Request: Subfolders [\#540](https://github.com/liyasthomas/postwoman/issues/540)
|
||||
- Add max-height and overflow: auto to "parameter list" textarea [\#532](https://github.com/liyasthomas/postwoman/issues/532)
|
||||
- IE Support [\#386](https://github.com/liyasthomas/postwoman/issues/386)
|
||||
- ⏬ Import a Postman's Collection [\#333](https://github.com/liyasthomas/postwoman/issues/333)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix variablesJSONString store default for GraphQL page [\#593](https://github.com/liyasthomas/postwoman/pull/593) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||
- Refactor/lint [\#589](https://github.com/liyasthomas/postwoman/pull/589) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Even [\#588](https://github.com/liyasthomas/postwoman/pull/588) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump firebase from 7.9.0 to 7.9.1 [\#587](https://github.com/liyasthomas/postwoman/pull/587) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Even [\#586](https://github.com/liyasthomas/postwoman/pull/586) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump firebase from 7.8.2 to 7.9.0 [\#585](https://github.com/liyasthomas/postwoman/pull/585) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump vue-virtual-scroll-list from 1.4.5 to 1.4.6 [\#584](https://github.com/liyasthomas/postwoman/pull/584) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Adapt extension check to new extensions [\#583](https://github.com/liyasthomas/postwoman/pull/583) ([levrik](https://github.com/levrik))
|
||||
- Update link to extension repo in README [\#582](https://github.com/liyasthomas/postwoman/pull/582) ([levrik](https://github.com/levrik))
|
||||
- Even [\#579](https://github.com/liyasthomas/postwoman/pull/579) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Refactor/lint [\#578](https://github.com/liyasthomas/postwoman/pull/578) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump vue-virtual-scroll-list from 1.4.4 to 1.4.5 [\#576](https://github.com/liyasthomas/postwoman/pull/576) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Unify Chrome and Firefox extensions [\#573](https://github.com/liyasthomas/postwoman/pull/573) ([levrik](https://github.com/levrik))
|
||||
- fix: drop the toast which doesn't show up [\#572](https://github.com/liyasthomas/postwoman/pull/572) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- :sparkles: Native share + updated meta description [\#571](https://github.com/liyasthomas/postwoman/pull/571) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump firebase from 7.8.1 to 7.8.2 [\#570](https://github.com/liyasthomas/postwoman/pull/570) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 4.0.1 to 4.0.2 [\#569](https://github.com/liyasthomas/postwoman/pull/569) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Added create collection and save request syncs [\#568](https://github.com/liyasthomas/postwoman/pull/568) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||
- Moved common headers to a separate file [\#566](https://github.com/liyasthomas/postwoman/pull/566) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- chore\(deps-dev\): bump cypress from 4.0.0 to 4.0.1 [\#565](https://github.com/liyasthomas/postwoman/pull/565) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump yargs-parser from 16.1.0 to 17.0.0 [\#564](https://github.com/liyasthomas/postwoman/pull/564) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 3.8.3 to 4.0.0 [\#562](https://github.com/liyasthomas/postwoman/pull/562) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.8.0 to 7.8.1 [\#561](https://github.com/liyasthomas/postwoman/pull/561) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore: use typeof as an operator and make use of localizable strings [\#559](https://github.com/liyasthomas/postwoman/pull/559) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Support for Formdata [\#555](https://github.com/liyasthomas/postwoman/pull/555) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump @nuxtjs/pwa from 3.0.0-beta.19 to 3.0.0-beta.20 [\#554](https://github.com/liyasthomas/postwoman/pull/554) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.4 to 5.9.5 [\#553](https://github.com/liyasthomas/postwoman/pull/553) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- fix\(chore\): Take letter casing into account while checking for duplicate collection [\#548](https://github.com/liyasthomas/postwoman/pull/548) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- update e2e tests [\#546](https://github.com/liyasthomas/postwoman/pull/546) ([yubathom](https://github.com/yubathom))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.3 to 5.9.4 [\#545](https://github.com/liyasthomas/postwoman/pull/545) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Refactor [\#543](https://github.com/liyasthomas/postwoman/pull/543) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump firebase from 7.7.0 to 7.8.0 [\#542](https://github.com/liyasthomas/postwoman/pull/542) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump graphql from 14.5.8 to 14.6.0 [\#541](https://github.com/liyasthomas/postwoman/pull/541) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- i18n [\#538](https://github.com/liyasthomas/postwoman/pull/538) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Modification of French translations [\#537](https://github.com/liyasthomas/postwoman/pull/537) ([thomasbnt](https://github.com/thomasbnt))
|
||||
- Even [\#535](https://github.com/liyasthomas/postwoman/pull/535) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Updating spanish translation [\#529](https://github.com/liyasthomas/postwoman/pull/529) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.4.1 to 6.5.0 [\#522](https://github.com/liyasthomas/postwoman/pull/522) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
|
||||
## [v1.8.0](https://github.com/liyasthomas/postwoman/tree/v1.8.0) (2020-01-28)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.5.0...v1.8.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Sync collection with a cloud storage \(e.g: Google drive\) [\#507](https://github.com/liyasthomas/postwoman/issues/507)
|
||||
- Application contains many hard-coded strings that aren't translatable [\#488](https://github.com/liyasthomas/postwoman/issues/488)
|
||||
- ULR parsing and var auto creation [\#469](https://github.com/liyasthomas/postwoman/issues/469)
|
||||
- What about additional loaders: + Pug, TypeScript, SASS, material-vue ? [\#467](https://github.com/liyasthomas/postwoman/issues/467)
|
||||
- \[suggestion\] - Tests tab [\#465](https://github.com/liyasthomas/postwoman/issues/465)
|
||||
- cookie not found support [\#443](https://github.com/liyasthomas/postwoman/issues/443)
|
||||
- Feature Request: Consumer Driven Contract Testing [\#420](https://github.com/liyasthomas/postwoman/issues/420)
|
||||
- Feature Request: Support OAuth2/OIDC [\#337](https://github.com/liyasthomas/postwoman/issues/337)
|
||||
- Add DB cache [\#26](https://github.com/liyasthomas/postwoman/issues/26)
|
||||
- Update asset/\*. js to asset/\*. ts. [\#517](https://github.com/liyasthomas/postwoman/pull/517) ([Sn005](https://github.com/Sn005))
|
||||
- Auth [\#513](https://github.com/liyasthomas/postwoman/pull/513) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Support for Google Chrome Extension [\#512](https://github.com/liyasthomas/postwoman/pull/512) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- GraphQL query validation based on schema [\#508](https://github.com/liyasthomas/postwoman/pull/508) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Syntax Error marking in GraphQL query editor [\#505](https://github.com/liyasthomas/postwoman/pull/505) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Refactoring proxy handling to be done in strategies [\#500](https://github.com/liyasthomas/postwoman/pull/500) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Firefox Extension compatibility [\#494](https://github.com/liyasthomas/postwoman/pull/494) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Network Strategies [\#487](https://github.com/liyasthomas/postwoman/pull/487) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- chore\(oauth\): Added method signatures as per JSDoc conventions [\#486](https://github.com/liyasthomas/postwoman/pull/486) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- GraphQL Type Highlight and Links [\#479](https://github.com/liyasthomas/postwoman/pull/479) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Warn the user if name field was left blank while creating a new collection [\#515](https://github.com/liyasthomas/postwoman/issues/515)
|
||||
- Multiple collections with the same name shouldn't exist [\#509](https://github.com/liyasthomas/postwoman/issues/509)
|
||||
- GraphQL String variables are null [\#497](https://github.com/liyasthomas/postwoman/issues/497)
|
||||
- Post request body is empty [\#473](https://github.com/liyasthomas/postwoman/issues/473)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Allow importing Postman collections [\#524](https://github.com/liyasthomas/postwoman/issues/524)
|
||||
- Request descriptions [\#511](https://github.com/liyasthomas/postwoman/issues/511)
|
||||
- Ability to run all requests of a folder/collection [\#498](https://github.com/liyasthomas/postwoman/issues/498)
|
||||
- Change import/export collection on requests page icon [\#495](https://github.com/liyasthomas/postwoman/issues/495)
|
||||
- import cURL error [\#477](https://github.com/liyasthomas/postwoman/issues/477)
|
||||
- move to postwoman org [\#475](https://github.com/liyasthomas/postwoman/issues/475)
|
||||
- Create standalone vue components of the request builder. [\#474](https://github.com/liyasthomas/postwoman/issues/474)
|
||||
- Enable running proxy as a backend for Request Capture [\#325](https://github.com/liyasthomas/postwoman/issues/325)
|
||||
- Label doesn't change when switching between collection requests [\#291](https://github.com/liyasthomas/postwoman/issues/291)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Enhancements [\#531](https://github.com/liyasthomas/postwoman/pull/531) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Merge pull request \#530 from liyasthomas/feature/post-request-tests [\#530](https://github.com/liyasthomas/postwoman/pull/530) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- even merge [\#528](https://github.com/liyasthomas/postwoman/pull/528) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Refactor [\#523](https://github.com/liyasthomas/postwoman/pull/523) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump v-tooltip from 2.0.2 to 2.0.3 [\#521](https://github.com/liyasthomas/postwoman/pull/521) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 3.8.2 to 3.8.3 [\#520](https://github.com/liyasthomas/postwoman/pull/520) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Feature/post request tests [\#518](https://github.com/liyasthomas/postwoman/pull/518) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||
- Validations for edit and create collections activity [\#516](https://github.com/liyasthomas/postwoman/pull/516) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Validate duplicate collections [\#510](https://github.com/liyasthomas/postwoman/pull/510) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Lint and refactor [\#506](https://github.com/liyasthomas/postwoman/pull/506) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Merge pull request \#504 from liyasthomas/dependabot/npm\_and\_yarn/node-sass-4.13.1 [\#504](https://github.com/liyasthomas/postwoman/pull/504) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.2 to 5.9.3 [\#503](https://github.com/liyasthomas/postwoman/pull/503) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump sass-loader from 8.0.1 to 8.0.2 [\#502](https://github.com/liyasthomas/postwoman/pull/502) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump ace-builds from 1.4.7 to 1.4.8 [\#501](https://github.com/liyasthomas/postwoman/pull/501) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- 💚 Fixed \#497 [\#499](https://github.com/liyasthomas/postwoman/pull/499) ([pushrbx](https://github.com/pushrbx))
|
||||
- Feat/firefox strategy [\#496](https://github.com/liyasthomas/postwoman/pull/496) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- i18n Japanese: Added new translations [\#492](https://github.com/liyasthomas/postwoman/pull/492) ([reefqi037](https://github.com/reefqi037))
|
||||
- Merge pull request \#491 from liyasthomas/i18n [\#491](https://github.com/liyasthomas/postwoman/pull/491) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Replaced hard-coded strings with localizable strings [\#490](https://github.com/liyasthomas/postwoman/pull/490) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore: Minor tweaks [\#485](https://github.com/liyasthomas/postwoman/pull/485) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Porting \(most of\) code to typescript [\#484](https://github.com/liyasthomas/postwoman/pull/484) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- ⬆️ Bump cypress from 3.8.1 to 3.8.2 [\#483](https://github.com/liyasthomas/postwoman/pull/483) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump sass-loader from 8.0.0 to 8.0.1 [\#482](https://github.com/liyasthomas/postwoman/pull/482) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.2 to 2.2.3 [\#481](https://github.com/liyasthomas/postwoman/pull/481) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- OAuth 2.0/OIDC Access Token Retrieval Support [\#476](https://github.com/liyasthomas/postwoman/pull/476) ([reefqi037](https://github.com/reefqi037))
|
||||
|
||||
## [v1.5.0](https://github.com/liyasthomas/postwoman/tree/v1.5.0) (2020-01-04)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.0.0...v1.5.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Can WSDL be implemented, similar to SoapUI? [\#461](https://github.com/liyasthomas/postwoman/issues/461)
|
||||
- Raw Request Body should be supported to format the JSON string [\#446](https://github.com/liyasthomas/postwoman/issues/446)
|
||||
- Ability to send Binary data using Postwoman [\#415](https://github.com/liyasthomas/postwoman/issues/415)
|
||||
- Custom request method [\#398](https://github.com/liyasthomas/postwoman/issues/398)
|
||||
- \[request\]: CLI possibilities [\#363](https://github.com/liyasthomas/postwoman/issues/363)
|
||||
- Feature request: OAuth header support/integration [\#358](https://github.com/liyasthomas/postwoman/issues/358)
|
||||
- i18n support [\#348](https://github.com/liyasthomas/postwoman/issues/348)
|
||||
- Separate layers in dockerfile to improve image build [\#339](https://github.com/liyasthomas/postwoman/issues/339)
|
||||
- Internal server environment usage requirements [\#336](https://github.com/liyasthomas/postwoman/issues/336)
|
||||
- Server Sent Events [\#329](https://github.com/liyasthomas/postwoman/issues/329)
|
||||
- Generate API documentation [\#326](https://github.com/liyasthomas/postwoman/issues/326)
|
||||
- Auth info on WebSocket connections [\#321](https://github.com/liyasthomas/postwoman/issues/321)
|
||||
- Set response panel to fullscreen [\#320](https://github.com/liyasthomas/postwoman/issues/320)
|
||||
- Graphql support [\#312](https://github.com/liyasthomas/postwoman/issues/312)
|
||||
- Keyboard shortcuts [\#302](https://github.com/liyasthomas/postwoman/issues/302)
|
||||
- File/binary request body support [\#298](https://github.com/liyasthomas/postwoman/issues/298)
|
||||
- It's possible to tab into read only and non-form elements [\#287](https://github.com/liyasthomas/postwoman/issues/287)
|
||||
- Change cursor to disabled on disabled inputs [\#286](https://github.com/liyasthomas/postwoman/issues/286)
|
||||
- Hover Styling on Inputs [\#285](https://github.com/liyasthomas/postwoman/issues/285)
|
||||
- Focus Styles on Buttons [\#284](https://github.com/liyasthomas/postwoman/issues/284)
|
||||
- Missing Focus on Inputs [\#279](https://github.com/liyasthomas/postwoman/issues/279)
|
||||
- Download the request result into a file. [\#278](https://github.com/liyasthomas/postwoman/issues/278)
|
||||
- Improve UI Contrast [\#277](https://github.com/liyasthomas/postwoman/issues/277)
|
||||
- \[UI\] \[UX\] Allow app to take width of browser [\#236](https://github.com/liyasthomas/postwoman/issues/236)
|
||||
- Extend syntax highlighting with ACE for pre-request script textarea [\#235](https://github.com/liyasthomas/postwoman/issues/235)
|
||||
- Store pre-request scripts in history [\#233](https://github.com/liyasthomas/postwoman/issues/233)
|
||||
- Store the time spent on fetching a response [\#225](https://github.com/liyasthomas/postwoman/issues/225)
|
||||
- Cache view [\#188](https://github.com/liyasthomas/postwoman/issues/188)
|
||||
- chore: stick to Vue.js best practices [\#432](https://github.com/liyasthomas/postwoman/pull/432) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Header key autocompletion [\#421](https://github.com/liyasthomas/postwoman/pull/421) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Custom methods support [\#400](https://github.com/liyasthomas/postwoman/pull/400) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Two Way Data Binding \(v-model\) to Ace Editor component [\#379](https://github.com/liyasthomas/postwoman/pull/379) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Basic i18n support [\#351](https://github.com/liyasthomas/postwoman/pull/351) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Undo header/param/body param deletion [\#350](https://github.com/liyasthomas/postwoman/pull/350) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- refactor: minor improvements [\#343](https://github.com/liyasthomas/postwoman/pull/343) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- WebSocket page freezes when pasting long URL [\#471](https://github.com/liyasthomas/postwoman/issues/471)
|
||||
- API Documentation won't be generated [\#456](https://github.com/liyasthomas/postwoman/issues/456)
|
||||
- Sharing Requests via link is not working [\#435](https://github.com/liyasthomas/postwoman/issues/435)
|
||||
- URL input text is so stutters [\#412](https://github.com/liyasthomas/postwoman/issues/412)
|
||||
- Save to collections after deleting all the collections causes an error page [\#390](https://github.com/liyasthomas/postwoman/issues/390)
|
||||
- Bearer token doesn’t work with CORS when only authorization header is allowed [\#353](https://github.com/liyasthomas/postwoman/issues/353)
|
||||
- Make the UI more compact [\#314](https://github.com/liyasthomas/postwoman/issues/314)
|
||||
- Allow reserved characters on websocket URI [\#289](https://github.com/liyasthomas/postwoman/issues/289)
|
||||
- Unable to edit or delete row in history table [\#281](https://github.com/liyasthomas/postwoman/issues/281)
|
||||
- Allow url request with `/` at eol [\#275](https://github.com/liyasthomas/postwoman/issues/275)
|
||||
- \[request\] localhost support [\#274](https://github.com/liyasthomas/postwoman/issues/274)
|
||||
- Code generation for Fetch request type of some methods \(POST, PUT, PATCH\) won't be shown [\#268](https://github.com/liyasthomas/postwoman/issues/268)
|
||||
- \[BUG\] \[UI\] \[Mobile\] Get results not scrollable. [\#266](https://github.com/liyasthomas/postwoman/issues/266)
|
||||
- POSTing large raw JSON packets [\#265](https://github.com/liyasthomas/postwoman/issues/265)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Module not found: Error: Can't resolve '../.postwoman/version.json' [\#457](https://github.com/liyasthomas/postwoman/issues/457)
|
||||
- \* ../.postwoman/version.json in ./node\_modules/babel-loader/lib??ref--2-0!./node\_modules/vue-loader/lib??vue-loader-options!./layouts/default.vue?vue&type=script&lang=js& friendly-errors 11:12:37 [\#448](https://github.com/liyasthomas/postwoman/issues/448)
|
||||
- npm run dev module was not found: ../.postwoman/version.json [\#442](https://github.com/liyasthomas/postwoman/issues/442)
|
||||
- graphql and websocket work, but http and https do not [\#441](https://github.com/liyasthomas/postwoman/issues/441)
|
||||
- Can I test localhost? [\#433](https://github.com/liyasthomas/postwoman/issues/433)
|
||||
- No 'Access-Control-Allow-Origin' [\#426](https://github.com/liyasthomas/postwoman/issues/426)
|
||||
- When uninstall the PWA the "install PWA" link in postwoman.io isn't appear anymore [\#419](https://github.com/liyasthomas/postwoman/issues/419)
|
||||
- Toggling options will reset the UI to English [\#417](https://github.com/liyasthomas/postwoman/issues/417)
|
||||
- Oh my dear god why don't we just wrap it in electron [\#413](https://github.com/liyasthomas/postwoman/issues/413)
|
||||
- UI improvement suggestion for request method drop down [\#409](https://github.com/liyasthomas/postwoman/issues/409)
|
||||
- Can I share a request with my team? [\#408](https://github.com/liyasthomas/postwoman/issues/408)
|
||||
- Does it not support the post method? [\#403](https://github.com/liyasthomas/postwoman/issues/403)
|
||||
- Post can't send request [\#401](https://github.com/liyasthomas/postwoman/issues/401)
|
||||
- \[Bug\] fix header icons overlap [\#399](https://github.com/liyasthomas/postwoman/issues/399)
|
||||
- Improve translate for pt-BR \(i18n\) [\#395](https://github.com/liyasthomas/postwoman/issues/395)
|
||||
- Raw input disabled is not working properly [\#394](https://github.com/liyasthomas/postwoman/issues/394)
|
||||
- Input area is not clearly to identify for users because the dark mode [\#393](https://github.com/liyasthomas/postwoman/issues/393)
|
||||
- \[UX\] Setting to make sidebar buttons small [\#389](https://github.com/liyasthomas/postwoman/issues/389)
|
||||
- \[UX\] Improve responsive breaking points [\#388](https://github.com/liyasthomas/postwoman/issues/388)
|
||||
- \[UX\] Hide history/collections [\#387](https://github.com/liyasthomas/postwoman/issues/387)
|
||||
- Clearing shortcut overrides browser default [\#374](https://github.com/liyasthomas/postwoman/issues/374)
|
||||
- Proxy server default configuration [\#373](https://github.com/liyasthomas/postwoman/issues/373)
|
||||
- Intent to translate [\#367](https://github.com/liyasthomas/postwoman/issues/367)
|
||||
- Static builds on releases [\#352](https://github.com/liyasthomas/postwoman/issues/352)
|
||||
- fix:SSE onclose handle [\#349](https://github.com/liyasthomas/postwoman/issues/349)
|
||||
- \[Request\] Use responses for next request? [\#324](https://github.com/liyasthomas/postwoman/issues/324)
|
||||
- Make response body area expandable [\#294](https://github.com/liyasthomas/postwoman/issues/294)
|
||||
- Mobile can't see console for request errors [\#283](https://github.com/liyasthomas/postwoman/issues/283)
|
||||
- Duplicated query string in generated code [\#272](https://github.com/liyasthomas/postwoman/issues/272)
|
||||
- Query parameters are duplicated [\#271](https://github.com/liyasthomas/postwoman/issues/271)
|
||||
- Generated code is incorrect [\#269](https://github.com/liyasthomas/postwoman/issues/269)
|
||||
- Lacking documentation and wiki [\#232](https://github.com/liyasthomas/postwoman/issues/232)
|
||||
- I can't send POST method [\#210](https://github.com/liyasthomas/postwoman/issues/210)
|
||||
- Handling request failures when build number is obtained from GitHub [\#122](https://github.com/liyasthomas/postwoman/issues/122)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- ⬆️ Bump @nuxtjs/axios from 5.9.0 to 5.9.2 [\#472](https://github.com/liyasthomas/postwoman/pull/472) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Added variables to graphql page. [\#464](https://github.com/liyasthomas/postwoman/pull/464) ([pushrbx](https://github.com/pushrbx))
|
||||
- i18n [\#463](https://github.com/liyasthomas/postwoman/pull/463) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- ⬆️ Bump cypress from 3.8.0 to 3.8.1 [\#460](https://github.com/liyasthomas/postwoman/pull/460) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- i18n\(de-DE\): improve some translations [\#459](https://github.com/liyasthomas/postwoman/pull/459) ([gabschne](https://github.com/gabschne))
|
||||
- bn-BD i18n [\#455](https://github.com/liyasthomas/postwoman/pull/455) ([hmtanbir](https://github.com/hmtanbir))
|
||||
- API documentation page [\#451](https://github.com/liyasthomas/postwoman/pull/451) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- ⬆️ Bump @nuxtjs/axios from 5.8.0 to 5.9.0 [\#450](https://github.com/liyasthomas/postwoman/pull/450) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump nuxt from 2.10.2 to 2.11.0 [\#449](https://github.com/liyasthomas/postwoman/pull/449) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Various UI tweaks [\#439](https://github.com/liyasthomas/postwoman/pull/439) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- i18n [\#438](https://github.com/liyasthomas/postwoman/pull/438) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Burmese translation added [\#437](https://github.com/liyasthomas/postwoman/pull/437) ([ZattWine](https://github.com/ZattWine))
|
||||
- Styled select input [\#431](https://github.com/liyasthomas/postwoman/pull/431) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Bumped dependencies and Improved UI contrast [\#430](https://github.com/liyasthomas/postwoman/pull/430) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- ⬆️ Bump cypress from 3.7.0 to 3.8.0 [\#429](https://github.com/liyasthomas/postwoman/pull/429) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- I18n [\#428](https://github.com/liyasthomas/postwoman/pull/428) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- I18n Japanese translation added [\#427](https://github.com/liyasthomas/postwoman/pull/427) ([reefqi037](https://github.com/reefqi037))
|
||||
- Even [\#424](https://github.com/liyasthomas/postwoman/pull/424) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- I18n [\#423](https://github.com/liyasthomas/postwoman/pull/423) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- I18n German translation added [\#422](https://github.com/liyasthomas/postwoman/pull/422) ([NJannasch](https://github.com/NJannasch))
|
||||
- Update id-ID.js [\#416](https://github.com/liyasthomas/postwoman/pull/416) ([williamsp](https://github.com/williamsp))
|
||||
- Improving translation for id-ID [\#414](https://github.com/liyasthomas/postwoman/pull/414) ([williamsp](https://github.com/williamsp))
|
||||
- Fixing bug on request saving [\#410](https://github.com/liyasthomas/postwoman/pull/410) ([adevr](https://github.com/adevr))
|
||||
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.1 to 2.2.2 [\#407](https://github.com/liyasthomas/postwoman/pull/407) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump vue-virtual-scroll-list from 1.4.3 to 1.4.4 [\#406](https://github.com/liyasthomas/postwoman/pull/406) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump nuxt-i18n from 6.4.0 to 6.4.1 [\#405](https://github.com/liyasthomas/postwoman/pull/405) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- I18n [\#404](https://github.com/liyasthomas/postwoman/pull/404) ([yubathom](https://github.com/yubathom))
|
||||
- App UI [\#391](https://github.com/liyasthomas/postwoman/pull/391) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- i18n [\#383](https://github.com/liyasthomas/postwoman/pull/383) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Added Turkish Language Support [\#382](https://github.com/liyasthomas/postwoman/pull/382) ([AliAnilKocak](https://github.com/AliAnilKocak))
|
||||
- Translated new words to Farsi lang [\#380](https://github.com/liyasthomas/postwoman/pull/380) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||
- fix: twitter summary card image url [\#378](https://github.com/liyasthomas/postwoman/pull/378) ([peterpeterparker](https://github.com/peterpeterparker))
|
||||
- Added nav shortcuts to GraphQL query and response, updated GraphQL shortcut icons [\#377](https://github.com/liyasthomas/postwoman/pull/377) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Bump cypress from 3.6.1 to 3.7.0 [\#376](https://github.com/liyasthomas/postwoman/pull/376) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Bump vuex-persist from 2.1.1 to 2.2.0 [\#375](https://github.com/liyasthomas/postwoman/pull/375) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- I18n [\#372](https://github.com/liyasthomas/postwoman/pull/372) ([EdikWang](https://github.com/EdikWang))
|
||||
- Use GraphQL logo for GraphQL tab [\#371](https://github.com/liyasthomas/postwoman/pull/371) ([NBTX](https://github.com/NBTX))
|
||||
- Update i18n keywords for Bahasa Indonesia [\#369](https://github.com/liyasthomas/postwoman/pull/369) ([wahwahid](https://github.com/wahwahid))
|
||||
- Intent to translate to Spanish on I18n [\#368](https://github.com/liyasthomas/postwoman/pull/368) ([adlpaf](https://github.com/adlpaf))
|
||||
- I18n [\#366](https://github.com/liyasthomas/postwoman/pull/366) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Add translations for FR/EN catalogues [\#364](https://github.com/liyasthomas/postwoman/pull/364) ([LaurentBrieu](https://github.com/LaurentBrieu))
|
||||
- Added Bahasa Indonesia language support [\#362](https://github.com/liyasthomas/postwoman/pull/362) ([wahwahid](https://github.com/wahwahid))
|
||||
- i18n [\#361](https://github.com/liyasthomas/postwoman/pull/361) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Add Simplified Chinese language [\#360](https://github.com/liyasthomas/postwoman/pull/360) ([EdikWang](https://github.com/EdikWang))
|
||||
- Added Brazilian Portuguese language support [\#359](https://github.com/liyasthomas/postwoman/pull/359) ([tetri](https://github.com/tetri))
|
||||
- Added Farsi language support [\#357](https://github.com/liyasthomas/postwoman/pull/357) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||
- Adding french language basic [\#355](https://github.com/liyasthomas/postwoman/pull/355) ([thomasbnt](https://github.com/thomasbnt))
|
||||
- Add Proxy URL option [\#345](https://github.com/liyasthomas/postwoman/pull/345) ([NBTX](https://github.com/NBTX))
|
||||
|
||||
## [v1.0.0](https://github.com/liyasthomas/postwoman/tree/v1.0.0) (2019-11-04)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v0.1.0...v1.0.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Chain requests. Execute a bunch of requests one by one and produce results [\#196](https://github.com/liyasthomas/postwoman/issues/196)
|
||||
- Allow User to Choose Whether to Include Authentication in Permalink [\#178](https://github.com/liyasthomas/postwoman/issues/178)
|
||||
- Allow HTTP \(not HTTPS\) on postwoman.io [\#175](https://github.com/liyasthomas/postwoman/issues/175)
|
||||
- Docker-compose in development [\#168](https://github.com/liyasthomas/postwoman/issues/168)
|
||||
- Add Docker [\#164](https://github.com/liyasthomas/postwoman/issues/164)
|
||||
- Clear Input [\#155](https://github.com/liyasthomas/postwoman/issues/155)
|
||||
- introduce some script language to parse the response and pass environment variable as request parameter [\#139](https://github.com/liyasthomas/postwoman/issues/139)
|
||||
- Add links to the footer version and commit sha [\#134](https://github.com/liyasthomas/postwoman/issues/134)
|
||||
- Please add a label for each request. It will be helpful. [\#133](https://github.com/liyasthomas/postwoman/issues/133)
|
||||
- Use 'icon buttons' instead of 'text buttons' [\#130](https://github.com/liyasthomas/postwoman/issues/130)
|
||||
- Change .editorconfig [\#115](https://github.com/liyasthomas/postwoman/issues/115)
|
||||
- \[UX\] Provide Focus State for Buttons, etc. [\#112](https://github.com/liyasthomas/postwoman/issues/112)
|
||||
- Add linter semistandard [\#98](https://github.com/liyasthomas/postwoman/issues/98)
|
||||
- Show "Send" button all over the page or enable hotkeys [\#94](https://github.com/liyasthomas/postwoman/issues/94)
|
||||
- Import request from cURL [\#93](https://github.com/liyasthomas/postwoman/issues/93)
|
||||
- Search on History [\#92](https://github.com/liyasthomas/postwoman/issues/92)
|
||||
- Add support for "application/hal+json" Content-Type [\#88](https://github.com/liyasthomas/postwoman/issues/88)
|
||||
- The query string is built incorrectly when the path contains a parameter [\#87](https://github.com/liyasthomas/postwoman/issues/87)
|
||||
- Option to Copy request as Fetch or XHR Or CURL [\#76](https://github.com/liyasthomas/postwoman/issues/76)
|
||||
- Add Tests [\#65](https://github.com/liyasthomas/postwoman/issues/65)
|
||||
- Request Headers [\#57](https://github.com/liyasthomas/postwoman/issues/57)
|
||||
- Colored response codes based on status code [\#46](https://github.com/liyasthomas/postwoman/issues/46)
|
||||
- Improve SEO [\#45](https://github.com/liyasthomas/postwoman/issues/45)
|
||||
- Add html preview to response section [\#41](https://github.com/liyasthomas/postwoman/issues/41)
|
||||
- websocket support [\#40](https://github.com/liyasthomas/postwoman/issues/40)
|
||||
- Raw request body for POST requests and Authorization key/value in Header [\#36](https://github.com/liyasthomas/postwoman/issues/36)
|
||||
- Code highlight on response body [\#33](https://github.com/liyasthomas/postwoman/issues/33)
|
||||
- Template selector [\#32](https://github.com/liyasthomas/postwoman/issues/32)
|
||||
- Vue template [\#31](https://github.com/liyasthomas/postwoman/issues/31)
|
||||
- Add copy response to clipboard button [\#30](https://github.com/liyasthomas/postwoman/issues/30)
|
||||
- Ability to store/share/create collections [\#29](https://github.com/liyasthomas/postwoman/issues/29)
|
||||
- Send request on Enter Key press [\#17](https://github.com/liyasthomas/postwoman/issues/17)
|
||||
- Readable [\#5](https://github.com/liyasthomas/postwoman/issues/5)
|
||||
- Serialize a request into JSON? [\#4](https://github.com/liyasthomas/postwoman/issues/4)
|
||||
- Add brand new logo to the project [\#244](https://github.com/liyasthomas/postwoman/pull/244) ([caneco](https://github.com/caneco))
|
||||
- Feature/pre request script [\#231](https://github.com/liyasthomas/postwoman/pull/231) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||
- Add the ApolloTV proxy server [\#217](https://github.com/liyasthomas/postwoman/pull/217) ([NBTX](https://github.com/NBTX))
|
||||
- bug: keeping information on page change [\#211](https://github.com/liyasthomas/postwoman/pull/211) ([breno-pereira](https://github.com/breno-pereira))
|
||||
- Work in Progress: feature/allow-collections-importing [\#209](https://github.com/liyasthomas/postwoman/pull/209) ([vlad0337187](https://github.com/vlad0337187))
|
||||
- Feature/log errors [\#207](https://github.com/liyasthomas/postwoman/pull/207) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||
- Use returned value from toggle component on change event [\#205](https://github.com/liyasthomas/postwoman/pull/205) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||
- Fix CORS and mixed content issue [\#199](https://github.com/liyasthomas/postwoman/pull/199) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||
- Added Tooltips [\#197](https://github.com/liyasthomas/postwoman/pull/197) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Added auto theme [\#185](https://github.com/liyasthomas/postwoman/pull/185) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Add Request name label for every requests [\#184](https://github.com/liyasthomas/postwoman/pull/184) ([sharath2106](https://github.com/sharath2106))
|
||||
- Collections [\#176](https://github.com/liyasthomas/postwoman/pull/176) ([TheHollidayInn](https://github.com/TheHollidayInn))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Bearer Token value still left even after being cleared [\#212](https://github.com/liyasthomas/postwoman/issues/212)
|
||||
- All changes in input fields lost when you switch to another page [\#203](https://github.com/liyasthomas/postwoman/issues/203)
|
||||
- POST request json bodies aren't sent [\#180](https://github.com/liyasthomas/postwoman/issues/180)
|
||||
- Headers turn into 0 : \[Object object\] [\#166](https://github.com/liyasthomas/postwoman/issues/166)
|
||||
- Send Again Button Constantly Flickering [\#157](https://github.com/liyasthomas/postwoman/issues/157)
|
||||
- There are cross-domain problems [\#128](https://github.com/liyasthomas/postwoman/issues/128)
|
||||
- Raw requests are not being sent [\#124](https://github.com/liyasthomas/postwoman/issues/124)
|
||||
- Request Body Is Not Sent [\#113](https://github.com/liyasthomas/postwoman/issues/113)
|
||||
- default menu option - 'Http' is not highlighted when launched from installed pwa app \(UI bug\) [\#100](https://github.com/liyasthomas/postwoman/issues/100)
|
||||
- App is broken with old history in localStorage [\#74](https://github.com/liyasthomas/postwoman/issues/74)
|
||||
- Last added history entry is removed automatically after refresh [\#66](https://github.com/liyasthomas/postwoman/issues/66)
|
||||
- Cannot use localhost as base url [\#56](https://github.com/liyasthomas/postwoman/issues/56)
|
||||
- \[CORS\] No 'Access-Control-Allow-Origin' header is present on the requested resource [\#2](https://github.com/liyasthomas/postwoman/issues/2)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Section labels don't display properly in Firefox [\#237](https://github.com/liyasthomas/postwoman/issues/237)
|
||||
- Unsupported URLs \[BUG\]? [\#229](https://github.com/liyasthomas/postwoman/issues/229)
|
||||
- Credentials are still being included in Permalink even when "Include in URL" is turned off [\#227](https://github.com/liyasthomas/postwoman/issues/227)
|
||||
- Display sendRequest runtime errors in the console [\#206](https://github.com/liyasthomas/postwoman/issues/206)
|
||||
- Missing "Landing/start page" [\#162](https://github.com/liyasthomas/postwoman/issues/162)
|
||||
- Response with content-type "application/hal+json" shows as \[Object object\] [\#158](https://github.com/liyasthomas/postwoman/issues/158)
|
||||
- A place to discuss [\#149](https://github.com/liyasthomas/postwoman/issues/149)
|
||||
- Inconsistent version name [\#141](https://github.com/liyasthomas/postwoman/issues/141)
|
||||
- Autoresize the textarea [\#102](https://github.com/liyasthomas/postwoman/issues/102)
|
||||
- Content-Type revamping [\#99](https://github.com/liyasthomas/postwoman/issues/99)
|
||||
- Add version number in footer [\#97](https://github.com/liyasthomas/postwoman/issues/97)
|
||||
- The history doesn't show a date with the timestamp. [\#81](https://github.com/liyasthomas/postwoman/issues/81)
|
||||
- Not working on Brave Browser anymore [\#71](https://github.com/liyasthomas/postwoman/issues/71)
|
||||
- Why da fuq is your name plastered all over the README? [\#70](https://github.com/liyasthomas/postwoman/issues/70)
|
||||
- Comparison with Postman is missing [\#69](https://github.com/liyasthomas/postwoman/issues/69)
|
||||
- HTTP request with different library [\#61](https://github.com/liyasthomas/postwoman/issues/61)
|
||||
- Editorconfig file [\#60](https://github.com/liyasthomas/postwoman/issues/60)
|
||||
- 500 this.isValidURL is not a function [\#58](https://github.com/liyasthomas/postwoman/issues/58)
|
||||
- Styling with Tailwindcss [\#38](https://github.com/liyasthomas/postwoman/issues/38)
|
||||
- Not Working in IE 11 [\#37](https://github.com/liyasthomas/postwoman/issues/37)
|
||||
- PWA not installable [\#19](https://github.com/liyasthomas/postwoman/issues/19)
|
||||
- Simple Misspelling [\#8](https://github.com/liyasthomas/postwoman/issues/8)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- docs: add liyasthomas as a contributor [\#264](https://github.com/liyasthomas/postwoman/pull/264) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add jamesgeorge007 as a contributor [\#263](https://github.com/liyasthomas/postwoman/pull/263) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add NBTX as a contributor [\#262](https://github.com/liyasthomas/postwoman/pull/262) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- Fix .all-contributorsrc badge template. [\#260](https://github.com/liyasthomas/postwoman/pull/260) ([NBTX](https://github.com/NBTX))
|
||||
- docs: add hosseinnedaee as a contributor [\#259](https://github.com/liyasthomas/postwoman/pull/259) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add nityanandagohain as a contributor [\#257](https://github.com/liyasthomas/postwoman/pull/257) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add JacobAnavisca as a contributor [\#256](https://github.com/liyasthomas/postwoman/pull/256) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add izerozlu as a contributor [\#255](https://github.com/liyasthomas/postwoman/pull/255) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add vlad0337187 as a contributor [\#254](https://github.com/liyasthomas/postwoman/pull/254) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add AndrewBastin as a contributor [\#253](https://github.com/liyasthomas/postwoman/pull/253) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add terranblake as a contributor [\#252](https://github.com/liyasthomas/postwoman/pull/252) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add nickpalenchar as a contributor [\#251](https://github.com/liyasthomas/postwoman/pull/251) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add yubathom as a contributor [\#250](https://github.com/liyasthomas/postwoman/pull/250) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add larouxn as a contributor [\#249](https://github.com/liyasthomas/postwoman/pull/249) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add NBTX as a contributor [\#248](https://github.com/liyasthomas/postwoman/pull/248) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add liyasthomas as a contributor [\#247](https://github.com/liyasthomas/postwoman/pull/247) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- Make page changes more fluid [\#246](https://github.com/liyasthomas/postwoman/pull/246) ([NBTX](https://github.com/NBTX))
|
||||
- Minor tweaks [\#245](https://github.com/liyasthomas/postwoman/pull/245) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- ⬆️ Bump @nuxtjs/google-tag-manager from 2.3.0 to 2.3.1 [\#243](https://github.com/liyasthomas/postwoman/pull/243) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump yargs-parser from 15.0.0 to 16.1.0 [\#242](https://github.com/liyasthomas/postwoman/pull/242) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/toast from 3.2.1 to 3.3.0 [\#241](https://github.com/liyasthomas/postwoman/pull/241) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump highlight.js from 9.15.10 to 9.16.2 [\#240](https://github.com/liyasthomas/postwoman/pull/240) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump cypress from 3.5.0 to 3.6.0 [\#239](https://github.com/liyasthomas/postwoman/pull/239) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Fix legend labels in Firefox, fix colored labels slider [\#238](https://github.com/liyasthomas/postwoman/pull/238) ([NBTX](https://github.com/NBTX))
|
||||
- Documentation Cleanup [\#230](https://github.com/liyasthomas/postwoman/pull/230) ([amitdash291](https://github.com/amitdash291))
|
||||
- Fix \#227 Exclude credentials from permalink [\#228](https://github.com/liyasthomas/postwoman/pull/228) ([reefqi037](https://github.com/reefqi037))
|
||||
- ⬆️ Bump cypress from 3.4.1 to 3.5.0 [\#224](https://github.com/liyasthomas/postwoman/pull/224) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/axios from 5.6.0 to 5.8.0 [\#223](https://github.com/liyasthomas/postwoman/pull/223) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump node-sass from 4.12.0 to 4.13.0 [\#222](https://github.com/liyasthomas/postwoman/pull/222) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump nuxt from 2.10.1 to 2.10.2 [\#221](https://github.com/liyasthomas/postwoman/pull/221) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.0 to 2.2.1 [\#220](https://github.com/liyasthomas/postwoman/pull/220) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump vuex-persist from 2.1.0 to 2.1.1 [\#219](https://github.com/liyasthomas/postwoman/pull/219) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Fixed frame colors toggle [\#216](https://github.com/liyasthomas/postwoman/pull/216) ([mateusppereira](https://github.com/mateusppereira))
|
||||
- Re-order sections and add toggle for including authentication in URL [\#215](https://github.com/liyasthomas/postwoman/pull/215) ([NBTX](https://github.com/NBTX))
|
||||
- chore: minor code refactor [\#214](https://github.com/liyasthomas/postwoman/pull/214) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Fix \#212 Clear bearer token value [\#213](https://github.com/liyasthomas/postwoman/pull/213) ([reefqi037](https://github.com/reefqi037))
|
||||
- fix: don't display 'Collection is empty' label if collection has any … [\#208](https://github.com/liyasthomas/postwoman/pull/208) ([vlad0337187](https://github.com/vlad0337187))
|
||||
- Fix proxy URL [\#201](https://github.com/liyasthomas/postwoman/pull/201) ([NBTX](https://github.com/NBTX))
|
||||
- Fix CORS and Mixed-Content issue & Bug Fixes [\#200](https://github.com/liyasthomas/postwoman/pull/200) ([NBTX](https://github.com/NBTX))
|
||||
- ⬆️ Bump start-server-and-test from 1.10.5 to 1.10.6 [\#198](https://github.com/liyasthomas/postwoman/pull/198) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump start-server-and-test from 1.10.3 to 1.10.5 [\#194](https://github.com/liyasthomas/postwoman/pull/194) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/google-tag-manager from 2.2.1 to 2.3.0 [\#193](https://github.com/liyasthomas/postwoman/pull/193) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump nuxt from 2.10.0 to 2.10.1 [\#192](https://github.com/liyasthomas/postwoman/pull/192) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump yargs-parser from 14.0.0 to 15.0.0 [\#191](https://github.com/liyasthomas/postwoman/pull/191) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Add quotation marks for generated code [\#187](https://github.com/liyasthomas/postwoman/pull/187) ([johnhenry](https://github.com/johnhenry))
|
||||
- updated threshold and rootMargin for IntersectionObserver [\#182](https://github.com/liyasthomas/postwoman/pull/182) ([edisonaugusthy](https://github.com/edisonaugusthy))
|
||||
- Add basic e2e tests [\#181](https://github.com/liyasthomas/postwoman/pull/181) ([yubathom](https://github.com/yubathom))
|
||||
- ⬆️ Bump nuxt from 2.9.2 to 2.10.0 [\#179](https://github.com/liyasthomas/postwoman/pull/179) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- 🐛 Fixed sitemap configuration [\#177](https://github.com/liyasthomas/postwoman/pull/177) ([NicoPennec](https://github.com/NicoPennec))
|
||||
- Code Refactoring [\#173](https://github.com/liyasthomas/postwoman/pull/173) ([edisonaugusthy](https://github.com/edisonaugusthy))
|
||||
- Added Black Theme [\#172](https://github.com/liyasthomas/postwoman/pull/172) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
## [v0.1.0](https://github.com/liyasthomas/postwoman/tree/v0.1.0) (2019-08-22)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/91c08a5e6305cc95a0df46a33fdd0013bf7339b4...v0.1.0)
|
||||
|
||||
|
||||
|
||||
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
||||
|
||||
@@ -34,12 +34,12 @@ Examples of unacceptable behavior by participants include:
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
behavior. Maintainers are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
that are not aligned with our Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ Please note we have a code of conduct, please follow it in all your interactions
|
||||
variables, exposed ports, useful file locations and container parameters.
|
||||
3. Increase the version numbers in any examples files and the README.md to the new version that this
|
||||
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
|
||||
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
|
||||
do not have permission to do that, you may request the second reviewer to merge it for you.
|
||||
4. You may merge the Pull Request once you have the sign-off of two other developers, or if you
|
||||
do not have permission to do that, you may request the second reviewer merge it for you.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
@@ -52,12 +52,12 @@ advances
|
||||
### Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
behavior. Maintainers are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
that are not aligned with our Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
|
||||
11
Dockerfile
11
Dockerfile
@@ -1,12 +1,19 @@
|
||||
FROM node:12.10.0-buster
|
||||
FROM node:12.10.0-alpine
|
||||
|
||||
LABEL maintainer="Liyas Thomas (liyascthomas@gmail.com)"
|
||||
|
||||
# Add git as the prebuild target requires it to parse version information
|
||||
RUN apk add --update --no-cache \
|
||||
git
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
214
README.md
214
README.md
@@ -3,14 +3,14 @@
|
||||
<br>
|
||||
<br>
|
||||
<p>
|
||||
API request builder - A free, fast, and beautiful alternative to Postman
|
||||
<b>A free, fast and beautiful API request builder</b>
|
||||
</p>
|
||||
<p>
|
||||
Helps you create your requests faster, saving you precious time on your development - <a href="https://postwoman.launchaco.com">Subscribe</a>
|
||||
<i>Web alternative to Postman - Helps you create requests faster, saving precious time on development - <a href="https://postwoman.launchaco.com">Subscribe</a></i>
|
||||
</p>
|
||||
<p>
|
||||
|
||||
[](https://travis-ci.com/liyasthomas/postwoman) [](https://github.com/liyasthomas/postwoman/releases/latest) [](https://postwoman.io) [](CONTRIBUTING.md) [](#contributors) [](https://opencollective.com/postwoman) [](https://www.paypal.me/liyascthomas) [](https://t.me/postwoman_app) [](https://discord.gg/GAMWxmR) [](https://twitter.com/intent/tweet?url=https%3A%2F%2Fpostwoman.io&text=%F0%9F%91%BD%20Postwoman%20%E2%80%A2%20API%20request%20builder%20-%20Helps%20you%20create%20your%20requests%20faster%2C%20saving%20you%20precious%20time%20on%20your%20development&original_referer=https%3A%2F%2Ftwitter.com%2Fshare%3Ftext%3D%25F0%259F%2591%25BD%2520Postwoman%2520%25E2%2580%25A2%2520API%2520request%2520builder%2520-%2520Helps%2520you%2520create%2520your%2520requests%2520faster%2C%2520saving%2520you%2520precious%2520time%2520on%2520your%2520development%26url%3Dhttps%3A%2F%2Fpostwoman.io%26hashtags%3Dpostwoman%26via%3Dliyasthomas&via=liyasthomas&hashtags=postwoman)
|
||||
[](https://travis-ci.com/liyasthomas/postwoman) [](https://github.com/liyasthomas/postwoman/releases/latest) [](https://postwoman.io) [](CONTRIBUTING.md) [](https://opencollective.com/postwoman) [](https://www.paypal.me/liyascthomas) [](https://t.me/postwoman_app) [](https://discord.gg/GAMWxmR) [](https://twitter.com/intent/tweet?url=https%3A%2F%2Fpostwoman.io&text=%F0%9F%91%BD%20Postwoman%20%E2%80%A2%20API%20request%20builder%20-%20Helps%20you%20create%20your%20requests%20faster%2C%20saving%20you%20precious%20time%20on%20your%20development&original_referer=https%3A%2F%2Ftwitter.com%2Fshare%3Ftext%3D%25F0%259F%2591%25BD%2520Postwoman%2520%25E2%2580%25A2%2520API%2520request%2520builder%2520-%2520Helps%2520you%2520create%2520your%2520requests%2520faster%2C%2520saving%2520you%2520precious%2520time%2520on%2520your%2520development%26url%3Dhttps%3A%2F%2Fpostwoman.io%26hashtags%3Dpostwoman%26via%3Dliyasthomas&via=liyasthomas&hashtags=postwoman)
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -23,26 +23,23 @@
|
||||
|
||||
---
|
||||
|
||||
**Start here: _[Story behind Postwoman](https://dev.to/liyasthomas/i-created-postwoman-an-online-open-source-api-request-builder-41md)_**
|
||||
**Read: _[Story behind Postwoman](https://dev.to/liyasthomas/i-created-postwoman-an-online-open-source-api-request-builder-41md), [Postwoman v1.0](https://dev.to/liyasthomas/postwoman-v1-0-a-free-fast-beautiful-alternative-to-postman-mn0)_**
|
||||
|
||||
**Chat here: _[Telegram](https://t.me/postwoman_app), [Discord](https://discord.gg/GAMWxmR)_**
|
||||
**Chat: _[Telegram](https://t.me/postwoman_app), [Discord](https://discord.gg/GAMWxmR)_**
|
||||
|
||||
**Donate here: _[PayPal](https://www.paypal.me/liyascthomas), [Open Collective](https://opencollective.com/postwoman), [Patreon](https://www.patreon.com/liyasthomas)_**
|
||||
**Donate: _[PayPal](https://www.paypal.me/liyascthomas), [Open Collective](https://opencollective.com/postwoman), [Patreon](https://www.patreon.com/liyasthomas)_**
|
||||
|
||||
<div align="center">
|
||||
<br>
|
||||
<img src="static/images/screenshot1.png" alt="Screenshot1" width="100%">
|
||||
<img src="static/images/screenshot2.png" alt="Screenshot2" width="100%">
|
||||
<br>
|
||||
</div>
|
||||
|
||||
### Features ✨
|
||||
|
||||
❤️ **Lightweight**: Crafted with minimalistic UI design. Simple design is the best design.
|
||||
❤️ **Lightweight**: Crafted with minimalistic UI design - simple design is the best design.
|
||||
|
||||
- Faster, lighter, cleaner, minimal & responsive
|
||||
|
||||
⚡️ **Fast**: Send requests and get/copy responses in real-time! Fast software is the best software.
|
||||
⚡️ **Fast**: Send requests and get/copy responses in real-time - fast software is the best software.
|
||||
|
||||
**Methods:**
|
||||
- `GET` - Retrieve information about the REST API resource
|
||||
@@ -50,19 +47,20 @@
|
||||
- `POST` - Create a REST API resource
|
||||
- `PUT` - Update a REST API resource
|
||||
- `DELETE` - Delete a REST API resource or related component
|
||||
- `CONNECT` - Establishes a tunnel to the server identified by the target resource
|
||||
- `OPTIONS` - Describe the communication options for the target resource
|
||||
- `TRACE` - Performs a message loop-back test along the path to the target resource
|
||||
- `PATCH` - Apply partial modifications to a REST API resource
|
||||
- `<custom>` - Some APIs use custom request methods such as `LIST`. Type in your custom methods.
|
||||
|
||||
_History entries are synced with local session storage_
|
||||
|
||||
🌈 **Make it yours**: Customizable combinations for background, foreground and accent colors: because customization === freedom. [Customize now ✨](https://postwoman.io/settings).
|
||||
🌈 **Make it yours**: Customizable combinations for background, foreground and accent colors: because customization is freedom. [Customize now ✨](https://postwoman.io/settings).
|
||||
|
||||
**Customizations:**
|
||||
- Choose theme: Kinda Dark (default), Clearly White, Just Black and System theme
|
||||
- Choose accent color: Green (default), Yellow, Pink, Red, Purple, Orange, Cyan and Blue
|
||||
- Toggle multi-colored frames
|
||||
- Toggle multi-colored headings
|
||||
|
||||
_Customized themes are also synced with local session storage_
|
||||
_Customized themes are synced with local session storage_
|
||||
|
||||
🔥 **PWA**: Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
|
||||
|
||||
@@ -70,33 +68,46 @@ _Customized themes are also synced with local session storage_
|
||||
- Instant loading with [Service Workers](https://developers.google.com/web/fundamentals/primers/service-workers)
|
||||
- Offline support
|
||||
- Low RAM/memory and CPU usage
|
||||
- [Add to Home Screen](https://developers.google.com/web/fundamentals/app-install-banners) (button in footer)
|
||||
- [Desktop PWA](https://developers.google.com/web/progressive-web-apps/desktop) support (button in footer)
|
||||
- Add to Home Screen
|
||||
- Desktop PWA
|
||||
- ([full features](https://developers.google.com/web/progressive-web-apps))
|
||||
|
||||
🚀 **Request**: Retrieve data from a URL without having to do a full page refresh.
|
||||
🚀 **Request**: Retrieve response from endpoint instantly.
|
||||
|
||||
- Choose `method`
|
||||
- Enter `URL`
|
||||
- Enter `Path`
|
||||
- Enter `URL` and `Path`
|
||||
- Send
|
||||
|
||||
**Features:**
|
||||
- Copy/share public "Share URL"
|
||||
- Generate request code for JavaScript XHR, Fetch and cURL
|
||||
- Generate request code for `JavaScript XHR`, `Fetch` and `cURL`
|
||||
- Copy generated request code to clipboard
|
||||
- Import cURL
|
||||
- Import `cURL`
|
||||
- Label requests
|
||||
|
||||
🔌 **Web Socket**: Establish full-duplex communication channels over a single TCP connection.
|
||||
🔌 **WebSocket**: Establish full-duplex communication channels over a single TCP connection.
|
||||
|
||||
- Send and receive data
|
||||
- Basic and Bearer Token authentication
|
||||
|
||||
🔐 **Authentication**: Allows to identity the end user.
|
||||
📡 **Server Sent Events**: Receive a stream of updates from a server over a HTTP connection without resorting to polling.
|
||||
|
||||
🔮 **GraphQL**: GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.
|
||||
|
||||
- Set endpoint and get schemas
|
||||
- Multi-column docs
|
||||
- Set custom request headers
|
||||
- Query schema
|
||||
- Get query response
|
||||
|
||||
🔐 **Authentication**: Allows to identify the end user.
|
||||
|
||||
**Types:**
|
||||
- None
|
||||
- Basic authentication using username and password
|
||||
- Token based authentication
|
||||
- Basic
|
||||
- Bearer Token
|
||||
- OAuth 2.0
|
||||
- OIDC Access Token/PKCE (Proof Key for Code Exchange)
|
||||
|
||||
📢 **Headers**: Describes the format the body of your request is being sent as.
|
||||
|
||||
@@ -107,30 +118,32 @@ _Customized themes are also synced with local session storage_
|
||||
📃 **Request Body**: Used to send and receive data via the REST API.
|
||||
|
||||
**Options:**
|
||||
- Set Content Type
|
||||
- Set `Content Type`
|
||||
- Add or remove Parameter list
|
||||
- Toggle between key-value and RAW input Parameter list
|
||||
|
||||
👋 **Responses**: Contains the status line, headers and the message/response body.
|
||||
|
||||
- Copy response to clipboard
|
||||
- View preview for HTML responses
|
||||
|
||||
_HTML responses have "Preview HTML" feature_
|
||||
- Download response to as a file
|
||||
- View preview of HTML responses
|
||||
|
||||
⏰ **History**: Request entries are synced with local session storage to reuse with a single click.
|
||||
|
||||
**Fields:**
|
||||
- Star
|
||||
- Label
|
||||
- Timestamp
|
||||
- Method
|
||||
- Status code
|
||||
- URL
|
||||
- Path
|
||||
- Timestamp
|
||||
- Duration
|
||||
- Pre-request script
|
||||
|
||||
_History entries can be sorted by any fields_
|
||||
|
||||
_Histories can deleted one-by-one or all together_
|
||||
_Histories can be deleted one-by-one or all together_
|
||||
|
||||
📁 **Collections**: Keep your API requests organized with collections and folders. Reuse them with a single click.
|
||||
|
||||
@@ -138,8 +151,6 @@ _Histories can deleted one-by-one or all together_
|
||||
- Create infinite collections, folders and requests
|
||||
- Edit, delete, move, export, import and replace
|
||||
|
||||
_Export, import and replace collections with JSON files_
|
||||
|
||||
_Collections are synced with local session storage_
|
||||
|
||||
🌐 **Proxy**: Enable Proxy Mode from Settings to access blocked APIs.
|
||||
@@ -147,33 +158,91 @@ _Collections are synced with local session storage_
|
||||
**Features:**
|
||||
- Hide your IP address
|
||||
- Fixes [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross Origin Resource Sharing) issues
|
||||
- Access APIs served in non-HTTPS (`http://`, `localhost`, etc.)
|
||||
- Access APIs served in non-HTTPS (`http://`)
|
||||
- Use custom Proxy URL
|
||||
|
||||
_Proxy is hosted by ApolloTV - [Privacy policy](https://apollotv.xyz/legal)_
|
||||
_Official Postwoman Proxy is hosted by ApolloTV - **[Privacy policy](https://apollotv.xyz/legal)**_
|
||||
|
||||
📜 **Pre-Request Scripts β**: Snippets of code associated with a request that are executed before the request is sent.
|
||||
|
||||
**Use-cases:**
|
||||
- Including the timestamp in the request headers
|
||||
- Sending a random alphanumeric string in the URL parameters
|
||||
|
||||
_Pre-Request Scripts is an experimental feature and is in Public Beta testing_
|
||||
- Include timestamp in the request headers
|
||||
- Send a random alphanumeric string in the URL parameters
|
||||
|
||||
_Requests with Pre-Request Scripts are indicated in History entries_
|
||||
|
||||
📄 **API Documentation**: Create and share dynamic API documentation easily, quickly.
|
||||
|
||||
**Usage:**
|
||||
1. Add your requests to Collections and Folders
|
||||
2. Export Collections and easily share your APIs with the rest of your team
|
||||
3. Import Collections and Generate Documentation on-the-go
|
||||
|
||||
⌨️ **Keyboard Shortcuts**: Optimized for efficiency.
|
||||
|
||||
**Shortcuts:**
|
||||
- Send Request <kbd>Ctrl</kbd> + <kbd>G</kbd>
|
||||
- Save to Collections <kbd>Ctrl</kbd> + <kbd>S</kbd>
|
||||
- Copy Request Link <kbd>Ctrl</kbd> + <kbd>K</kbd>
|
||||
- Reset Request <kbd>Ctrl</kbd> + <kbd>L</kbd>
|
||||
|
||||
🌎 **i18n β**: Experience the app in your own language.
|
||||
|
||||
1. Scroll down to the footer
|
||||
2. Click "Choose Language" icon button
|
||||
3. Select your language from the menu
|
||||
|
||||
_Keep in mind: Translations aren't available for all source and target language combinations_
|
||||
|
||||
**To provide a localized experience for users around the world, you can add you own translations.**
|
||||
|
||||
_**All `i18n` contributions are welcome to `i18n` [branch](https://github.com/liyasthomas/postwoman/tree/i18n) only!**_
|
||||
|
||||
📦 **Add-ons**: Official add-ons for Postwoman.
|
||||
|
||||
- **[Proxy β](https://github.com/postwoman-io/postwoman-proxy)** - A simple proxy server created for Postwoman
|
||||
- **[CLI β](https://github.com/postwoman-io/postwoman-cli)** - A CLI solution for Postwoman
|
||||
- **Browser Extensions** - Browser extensions that simplifies access to Postwoman
|
||||
|
||||
[ **Firefox**](https://addons.mozilla.org/en-US/firefox/addon/postwoman) | [ **Chrome**](https://chrome.google.com/webstore/detail/postwoman-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld) ([GitHub](https://github.com/AndrewBastin/postwoman-extension))
|
||||
|
||||
>**Extensions fixes `CORS` issues.**
|
||||
|
||||
_Add-ons are developed and maintained under **[Official Postwoman Organization](https://github.com/postwoman-io)**._
|
||||
|
||||
☁️ **Auth + Sync**: Sign in and sync in real-time.
|
||||
|
||||
**Sign in with:**
|
||||
- Google
|
||||
- GitHub
|
||||
|
||||
**Sync:**
|
||||
- History
|
||||
- Collections
|
||||
|
||||
✅ **Post-Request Tests β**: Write tests associated with a request that are executed after the request response.
|
||||
|
||||
**Use-cases:**
|
||||
- Check the status code as an integer
|
||||
- Filter response headers
|
||||
- Parse the response data
|
||||
|
||||
**To find out more, please check out [Postwoman Wiki](https://github.com/liyasthomas/postwoman/wiki).**
|
||||
|
||||
## Demo 🚀 [](https://postwoman.io)
|
||||
## Demo 🚀 [](https://postwoman.io)
|
||||
|
||||
[https://postwoman.io](https://postwoman.io)
|
||||
[postwoman.io](https://postwoman.io)
|
||||
|
||||
<a href="https://www.netlify.com">
|
||||
<img src="https://www.netlify.com/img/global/badges/netlify-light.svg"/>
|
||||
</a>
|
||||
|
||||
## Usage 💡
|
||||
|
||||
1. Specify your request method
|
||||
2. Type in your API URL
|
||||
3. Add API path
|
||||
4. Send request
|
||||
5. Get response!
|
||||
1. Specify your request `method`
|
||||
2. Type in your API `URL` and `path`
|
||||
3. Send request
|
||||
4. Get response
|
||||
|
||||
You're done!
|
||||
|
||||
@@ -188,6 +257,12 @@ You're done!
|
||||
|
||||
## Developing 👷
|
||||
|
||||
#### Use a browser based development environment:
|
||||
|
||||
[](https://gitpod.io/#https://github.com/liyasthomas/postwoman)
|
||||
|
||||
#### Or, with local development environment:
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
2. Install dependencies by running `npm install` within the directory that you cloned (probably `postwoman`).
|
||||
3. Start the development server with `npm run dev`.
|
||||
@@ -240,7 +315,10 @@ See the [CHANGELOG](CHANGELOG.md) file for details.
|
||||
### Lead Developers
|
||||
|
||||
* **[Liyas Thomas](https://github.com/liyasthomas)** - *Author*
|
||||
* **[Caneco](https://twitter.com/caneco)** - *Designer*
|
||||
* **[John Harker](https://github.com/NBTX)** - *Lead developer*
|
||||
* **[Andrew Bastin](https://github.com/andrewbastin)** - *Lead developer*
|
||||
* **[James George](https://github.com/jamesgeorge007)** - *Lead maintainer*
|
||||
* **[Caneco](https://twitter.com/caneco)** - *Logo and banner designer*
|
||||
|
||||
### Testing and Debugging
|
||||
|
||||
@@ -258,11 +336,10 @@ See the [CHANGELOG](CHANGELOG.md) file for details.
|
||||
<td align="center"><a href="https://nicholaslaroux.com"><img src="https://avatars0.githubusercontent.com/u/1557529?v=4" width="100px;" alt="Nicholas La Roux"/><br /><sub><b>Nicholas La Roux</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=larouxn" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/yubathom"><img src="https://avatars3.githubusercontent.com/u/4117768?v=4" width="100px;" alt="Thomas Yuba"/><br /><sub><b>Thomas Yuba</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=yubathom" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://www.linkedin.com/in/nickpalenchar"><img src="https://avatars1.githubusercontent.com/u/7539781?v=4" width="100px;" alt="Nick Palenchar"/><br /><sub><b>Nick Palenchar</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=nickpalenchar" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.lumahealth.io/"><img src="https://avatars3.githubusercontent.com/u/8795767?v=4" width="100px;" alt="Terran Blake"/><br /><sub><b>Terran Blake</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=terranblake" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/AndrewBastin"><img src="https://avatars2.githubusercontent.com/u/9131943?v=4" width="100px;" alt="Andrew Bastin"/><br /><sub><b>Andrew Bastin</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=AndrewBastin" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/vlad0337187"><img src="https://avatars1.githubusercontent.com/u/12682937?v=4" width="100px;" alt="Vladislav"/><br /><sub><b>Vladislav</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=vlad0337187" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/vlad0337187"><img src="https://avatars1.githubusercontent.com/u/12682937?v=4" width="100px;" alt="Vladislav"/><br /><sub><b>Vladislav</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=vlad0337187" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/izerozlu"><img src="https://avatars3.githubusercontent.com/u/17386157?v=4" width="100px;" alt="izerozlu"/><br /><sub><b>izerozlu</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=izerozlu" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/JacobAnavisca"><img src="https://avatars2.githubusercontent.com/u/21232366?v=4" width="100px;" alt="Jacob Anavisca"/><br /><sub><b>Jacob Anavisca</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=JacobAnavisca" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://nityanandagohain.github.io"><img src="https://avatars3.githubusercontent.com/u/26831659?v=4" width="100px;" alt="Nityananda Gohain"/><br /><sub><b>Nityananda Gohain</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=nityanandagohain" title="Code">💻</a></td>
|
||||
@@ -320,13 +397,34 @@ This project is licensed under the [MIT License](https://opensource.org/licenses
|
||||
|
||||
## Badges
|
||||
|
||||
| Status | Preview | Markdown Code (copy & paste into `readme.md`) |
|
||||
| ----------- | ----------- | ----------- |
|
||||
| **Default** | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
| **Success** | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
| **Critical** | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
| **Custom** | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
| Make your own badge! | [](https://postwoman.io) | `[](https://postwoman.io)` |
|
||||
<table>
|
||||
<tr>
|
||||
<th>Preview</th>
|
||||
<th>Markdown code</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/Tested_on-Postwoman-202124?logo=Postwoman"/></a><br/><sub>Default<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/Tested_on-Postwoman-success?logo=Postwoman"/></a><br/><sub>Success<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/Tested_on-Postwoman-critical?logo=Postwoman"/></a><br/><sub>Critical<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/Tested_on-Postwoman-blueviolet?logo=Postwoman"/></a><br/><sub>Custom<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/your_text-Postwoman-hex_color_code?logo=Postwoman"/></a><br/><sub>Customize<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div align="center">
|
||||
<br>
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
// @import url("https://fonts.googleapis.com/css?family=Poppins:500,700|Roboto+Mono:400&display=swap");
|
||||
// @import url("https://fonts.googleapis.com/icon?family=Material+Icons&display=swap");
|
||||
|
||||
/* Material Design Icons */
|
||||
@font-face {
|
||||
font-family: "Material Icons";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(~@/assets/fonts/material-icons-v48.woff2) format("woff2");
|
||||
font-display: swap;
|
||||
src: url("~static/fonts/material-icons-v48.woff2") format("woff2");
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: "Material Icons";
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
@@ -21,43 +26,45 @@
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: "liga";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-feature-settings: "liga";
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Roboto Mono 400 */
|
||||
@font-face {
|
||||
font-family: "Roboto Mono";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Mono"), local("RobotoMono-Regular"),
|
||||
url("~@/assets/fonts/roboto-mono-v7-latin-regular.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~@/assets/fonts/roboto-mono-v7-latin-regular.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* Poppins 500 */
|
||||
/* poppins-500 - latin */
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: local("Poppins Medium"), local("Poppins-Medium"),
|
||||
url("~@/assets/fonts/poppins-v9-latin-500.woff2") format("woff2"),
|
||||
url("~static/fonts/poppins-v9-latin-500.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~@/assets/fonts/poppins-v9-latin-500.woff") format("woff");
|
||||
url("~static/fonts/poppins-v9-latin-500.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* poppins-700 - latin */
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: local("Poppins Bold"), local("Poppins-Bold"),
|
||||
url("~@/assets/fonts/poppins-v9-latin-700.woff2") format("woff2"),
|
||||
url("~static/fonts/poppins-v9-latin-700.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~@/assets/fonts/poppins-v9-latin-700.woff") format("woff");
|
||||
url("~static/fonts/poppins-v9-latin-700.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* roboto-mono-regular - latin */
|
||||
@font-face {
|
||||
font-family: "Roboto Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local("Roboto Mono"), local("RobotoMono-Regular"),
|
||||
url("~static/fonts/roboto-mono-v7-latin-regular.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~static/fonts/roboto-mono-v7-latin-regular.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
@@ -1,27 +1,36 @@
|
||||
$responsiveWidth: 720px;
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
.page-enter-active,
|
||||
.page-leave-active,
|
||||
.layout-enter-active,
|
||||
.layout-leave-active {
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.page-enter,
|
||||
.page-leave-active,
|
||||
.layout-enter,
|
||||
.layout-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
$responsiveWidth: 768px;
|
||||
|
||||
::selection {
|
||||
background-color: var(--ac-sel-color);
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background-color: var(--bg-dark-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--fg-light-color);
|
||||
border-radius: 8px;
|
||||
border: 2px solid var(--bg-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
&:hover {
|
||||
background-color: var(--fg-color);
|
||||
}
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
@@ -35,11 +44,12 @@ html {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--fg-color);
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
||||
line-height: 1.5;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
@@ -47,15 +57,12 @@ body {
|
||||
user-select: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
scroll-behavior: smooth;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
// Make theme transition smoother.
|
||||
body.afterLoad {
|
||||
|
||||
&,
|
||||
& * {
|
||||
transition: background-color 0.2s ease-in-out, border 0.2s ease-in-out;
|
||||
}
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
body.sticky-footer footer {
|
||||
@@ -66,14 +73,18 @@ a {
|
||||
display: inline-flex;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&.link {
|
||||
color: var(--ac-color);
|
||||
}
|
||||
}
|
||||
|
||||
header,
|
||||
footer {
|
||||
& > div {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
padding: 16px 8px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -83,11 +94,11 @@ footer {
|
||||
.wrapper {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
.wrapper .content {
|
||||
min-height: 100vh;
|
||||
.wrapper .page {
|
||||
min-height: calc(100vh - 153px);
|
||||
}
|
||||
|
||||
.header,
|
||||
@@ -117,45 +128,55 @@ footer {
|
||||
|
||||
.logo {
|
||||
font-size: 22px;
|
||||
|
||||
&:hover {
|
||||
color: var(--ac-color);
|
||||
}
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.nav-first {
|
||||
.nav-first,
|
||||
.sticky-inner {
|
||||
display: flex;
|
||||
order: 1;
|
||||
flex-flow: column;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.nav-first {
|
||||
z-index: 1;
|
||||
height: 100vh;
|
||||
padding: 0 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex: 1;
|
||||
order: 2;
|
||||
position: relative;
|
||||
padding: 0 16px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.nav-second {
|
||||
display: flex;
|
||||
width: 10%;
|
||||
order: 3;
|
||||
// comment this to display
|
||||
// comment below this to display
|
||||
display: none;
|
||||
}
|
||||
|
||||
nav.primary-nav {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
border-bottom: 1px solid var(--brd-color);
|
||||
|
||||
svg {
|
||||
fill: var(--fg-light-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -163,15 +184,16 @@ nav.primary-nav {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
padding: 14px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--brd-color);
|
||||
background-color: var(--bg-light-color);
|
||||
color: var(--fg-light-color);
|
||||
margin: 8px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
fill: var(--fg-light-color);
|
||||
margin: 8px 0;
|
||||
|
||||
&:hover {
|
||||
color: var(--fg-color);
|
||||
fill: var(--fg-color);
|
||||
|
||||
svg {
|
||||
fill: var(--fg-color);
|
||||
@@ -181,6 +203,7 @@ nav.primary-nav {
|
||||
&.nuxt-link-exact-active {
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
fill: var(--act-color);
|
||||
border-radius: 16px;
|
||||
|
||||
svg {
|
||||
@@ -192,6 +215,7 @@ nav.primary-nav {
|
||||
|
||||
nav.secondary-nav {
|
||||
display: flex;
|
||||
border-top: 1px dashed var(--brd-color);
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
@@ -205,11 +229,11 @@ nav.secondary-nav {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
padding: 14px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-light-color);
|
||||
margin: 8px;
|
||||
margin: 8px 0;
|
||||
|
||||
&:hover {
|
||||
color: var(--fg-color);
|
||||
@@ -225,51 +249,42 @@ nav.secondary-nav {
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
$responsiveWidth: 720px;
|
||||
|
||||
@media (max-width: $responsiveWidth) {
|
||||
.columns {
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.nav-first {
|
||||
width: 100%;
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
nav.primary-nav {
|
||||
flex-flow: row;
|
||||
}
|
||||
|
||||
nav.secondary-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 0 8px;
|
||||
}
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
h3,
|
||||
h4 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-bottom: 1px dashed var(--brd-color);
|
||||
}
|
||||
|
||||
p {
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
$bgcolor: var(--tt-color);
|
||||
$fgcolor: var(--fg-color);
|
||||
display: block !important;
|
||||
z-index: 10000;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
.tooltip-inner {
|
||||
background: black;
|
||||
color: white;
|
||||
background: $bgcolor;
|
||||
color: $fgcolor;
|
||||
border-radius: 8px;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4px 24px rgba(black, 0.1);
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
@@ -278,7 +293,7 @@ h3 {
|
||||
border-style: solid;
|
||||
position: absolute;
|
||||
margin: 5px;
|
||||
border-color: black;
|
||||
border-color: $bgcolor;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -343,18 +358,22 @@ h3 {
|
||||
}
|
||||
|
||||
&.popover {
|
||||
$color: #f9f9f9;
|
||||
.wrapper {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.popover-inner {
|
||||
background: $color;
|
||||
color: black;
|
||||
padding: 24px;
|
||||
border-radius: 5px;
|
||||
background: $bgcolor;
|
||||
color: $fgcolor;
|
||||
padding: 4px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 5px 30px rgba(black, 0.1);
|
||||
max-height: 256px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.popover-arrow {
|
||||
border-color: $color;
|
||||
border-color: $bgcolor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,21 +394,29 @@ h3.title {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-left: 4px;
|
||||
color: var(--fg-light-color);
|
||||
}
|
||||
|
||||
.bg-color {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 4px;
|
||||
padding: 0 16px;
|
||||
padding: 6px 16px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
||||
font-weight: 700;
|
||||
transition: all 0.2s ease-in-out;
|
||||
fill: var(--act-color);
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
@@ -397,28 +424,73 @@ button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&.icon {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--fg-light-color);
|
||||
fill: var(--fg-light-color);
|
||||
|
||||
&:not([disabled]):hover {
|
||||
color: var(--fg-color);
|
||||
fill: var(--fg-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
&:not([disabled]):hover,
|
||||
&:not([disabled]):active,
|
||||
&:not([disabled]):focus {
|
||||
color: var(--act-color);
|
||||
fill: var(--act-color);
|
||||
box-shadow: inset 0 0 0 2px var(--fg-color);
|
||||
}
|
||||
|
||||
&.icon {
|
||||
background-color: transparent;
|
||||
color: var(--fg-light-color);
|
||||
fill: var(--fg-light-color);
|
||||
border-radius: 8px;
|
||||
|
||||
&:not([disabled]):hover,
|
||||
&:not([disabled]):active,
|
||||
&:not([disabled]):focus {
|
||||
color: var(--fg-color);
|
||||
fill: var(--fg-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.primary {
|
||||
color: var(--ac-color);
|
||||
|
||||
&:not([disabled]):hover,
|
||||
&:not([disabled]):active,
|
||||
&:not([disabled]):focus {
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes beat {
|
||||
30% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.material-icons:active {
|
||||
animation: beat 0.5s forwards 1;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 16px 0;
|
||||
border: 1px solid var(--brd-color);
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-color);
|
||||
border-radius: 16px;
|
||||
background-color: var(--bg-dark-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
fieldset:target,
|
||||
section:target {
|
||||
animation: highlight 2s ease;
|
||||
}
|
||||
|
||||
@keyframes highlight {
|
||||
50% {
|
||||
box-shadow: 0 0 0 2px var(--ac-color);
|
||||
}
|
||||
}
|
||||
|
||||
legend {
|
||||
@@ -428,6 +500,7 @@ legend {
|
||||
color: var(--fg-color);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
* {
|
||||
vertical-align: middle;
|
||||
@@ -474,31 +547,52 @@ fieldset.yellow legend {
|
||||
color: #f1fa8c;
|
||||
}
|
||||
|
||||
input[type="file"],
|
||||
input[type="radio"],
|
||||
.tab,
|
||||
.hide-on-large-screen,
|
||||
#installPWA,
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.method,
|
||||
kbd,
|
||||
select,
|
||||
input,
|
||||
option,
|
||||
textarea,
|
||||
pre {
|
||||
pre,
|
||||
code {
|
||||
display: inline-flex;
|
||||
margin: 4px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
font-size: 16px;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
transition: all 0.2s ease-in-out;
|
||||
user-select: text;
|
||||
width: calc(100% - 8px);
|
||||
min-height: 40px;
|
||||
resize: vertical;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:not([readonly]):hover {
|
||||
background-color: var(--bg-dark-color);
|
||||
&:not([readonly]):not(.ace_editor):hover,
|
||||
&:not([readonly]):not(.ace_editor):active,
|
||||
&:not([readonly]):not(.ace_editor):focus {
|
||||
box-shadow: inset 0 0 0 2px var(--fg-light-color);
|
||||
}
|
||||
}
|
||||
|
||||
.method {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
box-shadow: inset 0 0 0 2px var(--fg-light-color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,22 +600,54 @@ pre {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
code {
|
||||
height: 336px;
|
||||
border-radius: 8px;
|
||||
pre.ace_editor {
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-weight: 400;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
background-color: var(--bg-dark-color) !important;
|
||||
color: var(--fg-color) !important;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
kbd,
|
||||
code,
|
||||
pre {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.trigger {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
content: "\e313";
|
||||
font-family: "Material Icons";
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
height: 37px;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
|
||||
&::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
select,
|
||||
input,
|
||||
option {
|
||||
height: 40px;
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
@@ -554,23 +680,24 @@ input[type="checkbox"] {
|
||||
}
|
||||
}
|
||||
|
||||
.error,
|
||||
.disabled,
|
||||
.error:not(input),
|
||||
.disabled:not(input),
|
||||
[disabled] {
|
||||
background-color: var(--err-color);
|
||||
color: var(--fg-light-color);
|
||||
fill: var(--fg-light-color);
|
||||
cursor: default;
|
||||
cursor: not-allowed;
|
||||
|
||||
&.icon {
|
||||
color: var(--bg-color);
|
||||
fill: var(--bg-color);
|
||||
color: var(--err-color);
|
||||
fill: var(--err-color);
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 4px;
|
||||
color: var(--fg-light-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
ul,
|
||||
@@ -579,48 +706,43 @@ ol {
|
||||
margin: 4px 0 4px;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ul li,
|
||||
ol li {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
flex-flow: column nowrap;
|
||||
flex-grow: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-grow: 1;
|
||||
flex-direction: row;
|
||||
|
||||
* {
|
||||
display: inline-flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.show-on-small-screen {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media (max-width: $responsiveWidth) {
|
||||
|
||||
ul,
|
||||
ol {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ul li,
|
||||
ol li {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hide-on-small-screen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.show-on-small-screen {
|
||||
.show-on-large-screen {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
#installPWA {
|
||||
display: none;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.info-response {
|
||||
@@ -647,23 +769,6 @@ ol li {
|
||||
background-color: var(--err-color);
|
||||
}
|
||||
|
||||
.virtual-list::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
fieldset#history {
|
||||
.method-list-item {
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
@@ -676,6 +781,11 @@ fieldset#history {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
#response-details-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -684,6 +794,7 @@ fieldset#history {
|
||||
textarea {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.covers-response {
|
||||
@@ -699,67 +810,165 @@ fieldset#history {
|
||||
}
|
||||
|
||||
#send {
|
||||
#hidden-message {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.show {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
z-index: 1;
|
||||
|
||||
#hidden-message {
|
||||
display: block;
|
||||
margin-left: 4px;
|
||||
}
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
div.tab {
|
||||
.tab {
|
||||
width: 100%;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
div.tab {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="radio"] + label {
|
||||
padding: 8px 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
border-color: var(--brd-color);
|
||||
}
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label {
|
||||
border-color: var(--fg-color);
|
||||
color: var(--fg-color);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label + div.tab {
|
||||
input[type="radio"]:checked + label + .tab {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.toasted-container .toasted {
|
||||
justify-content: start !important;
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
|
||||
.toasted.info {
|
||||
background-color: var(--ac-color) !important;
|
||||
color: var(--act-color) !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.toasted.bubble .action {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.toasted .action {
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
.page-columns {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.inner-left {
|
||||
display: flex;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.inner-right {
|
||||
display: flex;
|
||||
width: 30%;
|
||||
order: 2;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: $responsiveWidth) {
|
||||
.content,
|
||||
.columns {
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.nav-first {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
height: auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
background-color: var(--bg-color);
|
||||
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
nav.primary-nav {
|
||||
flex-flow: row nowrap;
|
||||
overflow: auto;
|
||||
justify-content: space-between;
|
||||
background-color: var(--bg-dark-color);
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
margin: 8px;
|
||||
flex: 1;
|
||||
|
||||
&.nuxt-link-exact-active {
|
||||
background-color: transparent;
|
||||
color: var(--ac-color);
|
||||
fill: var(--ac-color);
|
||||
|
||||
svg {
|
||||
fill: var(--ac-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nav.secondary-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 0 8px 68px;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
ul li,
|
||||
ol li {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hide-on-small-screen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hide-on-large-screen,
|
||||
.show-on-small-screen {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.sticky-inner {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inner-left {
|
||||
order: 0;
|
||||
}
|
||||
|
||||
.inner-right {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.toasted-container {
|
||||
margin-bottom: 68px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,91 +8,100 @@
|
||||
|
||||
// Dark is the default theme variant.
|
||||
@mixin darkTheme {
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgb(41, 42, 45);
|
||||
// Background color
|
||||
--bg-color: rgb(37, 38, 40);
|
||||
// Auto-complete color
|
||||
--atc-color: rgb(49, 49, 55);
|
||||
--bg-color: rgba(32, 33, 36, 1);
|
||||
// Light Background color
|
||||
--bg-light-color: rgba(255, 255, 255, 0.02);
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgba(0, 0, 0, 0.1);
|
||||
// Text color
|
||||
--fg-color: rgb(247, 248, 248);
|
||||
--fg-color: rgba(255, 255, 255, 0.9);
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(150, 155, 160);
|
||||
--fg-light-color: rgba(255, 255, 255, 0.5);
|
||||
// Border color
|
||||
--brd-color: rgb(48, 47, 55);
|
||||
--brd-color: rgba(255, 255, 255, 0.05);
|
||||
// Error color
|
||||
--err-color: rgb(41, 42, 45);
|
||||
--err-color: rgba(255, 255, 255, 0.05);
|
||||
// Acent color
|
||||
--ac-color: #50fa7b;
|
||||
--ac-sel-color: rgb(80, 250, 123, 0.8);
|
||||
--ac-color: rgba(80, 250, 123, 1);
|
||||
// Active text color
|
||||
--act-color: rgb(37, 38, 40);
|
||||
--act-color: rgba(32, 33, 36, 1);
|
||||
// Auto-complete color
|
||||
--atc-color: rgba(32, 33, 36, 1);
|
||||
// Tooltip color
|
||||
--tt-color: rgba(53, 53, 53, 1);
|
||||
}
|
||||
|
||||
@mixin lightTheme {
|
||||
// Background color
|
||||
--bg-color: rgba(255, 255, 255, 1);
|
||||
// Light Background color
|
||||
--bg-light-color: rgba(0, 0, 0, 0.02);
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgba(0, 0, 0, 0.02);
|
||||
// Text color
|
||||
--fg-color: rgba(0, 0, 0, 0.9);
|
||||
// Light Text color
|
||||
--fg-light-color: rgba(0, 0, 0, 0.6);
|
||||
// Border color
|
||||
--brd-color: rgba(0, 0, 0, 0.1);
|
||||
// Error color
|
||||
--err-color: rgba(0, 0, 0, 0.1);
|
||||
// Acent color
|
||||
--ac-color: rgba(80, 250, 123, 1);
|
||||
// Active text color
|
||||
--act-color: rgba(255, 255, 255, 1);
|
||||
// Auto-complete color
|
||||
--atc-color: rgba(255, 255, 255, 1);
|
||||
// Tooltip color
|
||||
--tt-color: rgba(220, 220, 220, 1);
|
||||
}
|
||||
|
||||
@mixin blackTheme {
|
||||
// Background color
|
||||
--bg-color: rgba(0, 0, 0, 1);
|
||||
// Light Background color
|
||||
--bg-light-color: rgba(255, 255, 255, 0.02);
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgba(255, 255, 255, 0.02);
|
||||
// Text color
|
||||
--fg-color: rgba(255, 255, 255, 0.9);
|
||||
// Light Text color
|
||||
--fg-light-color: rgba(255, 255, 255, 0.5);
|
||||
// Border color
|
||||
--brd-color: rgba(255, 255, 255, 0.05);
|
||||
// Error color
|
||||
--err-color: rgba(255, 255, 255, 0.05);
|
||||
// Acent color
|
||||
--ac-color: rgba(80, 250, 123, 1);
|
||||
// Active text color
|
||||
--act-color: rgba(0, 0, 0, 1);
|
||||
// Auto-complete color
|
||||
--atc-color: rgba(0, 0, 0, 1);
|
||||
// Tooltip color
|
||||
--tt-color: rgba(18, 18, 18, 1);
|
||||
}
|
||||
|
||||
:root {
|
||||
@include darkTheme;
|
||||
}
|
||||
|
||||
@media(prefers-color-scheme: dark) {
|
||||
:root.auto {
|
||||
@include darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lightTheme {
|
||||
// Dark Background color
|
||||
--bg-dark-color: #f6f6f6;
|
||||
// Background color
|
||||
--bg-color: #ffffff;
|
||||
// Auto-complete color
|
||||
--atc-color: #ebebeb;
|
||||
// Text color
|
||||
--fg-color: #525252;
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(150, 155, 160);
|
||||
// Border color
|
||||
--brd-color: #eeeeed;
|
||||
// Error color
|
||||
--err-color: #f6f6f6;
|
||||
// Acent color
|
||||
--ac-color: #57b5f9;
|
||||
--ac-sel-color: #57b5f9;
|
||||
// Active text color
|
||||
--act-color: #ffffff;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
@include lightTheme;
|
||||
}
|
||||
|
||||
@media(prefers-color-scheme: light) {
|
||||
:root.auto {
|
||||
@include lightTheme;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin blackTheme {
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgb(8, 8, 8);
|
||||
// Background color
|
||||
--bg-color: #000000;
|
||||
// Auto-complete color
|
||||
--atc-color: rgb(18, 18, 18);
|
||||
// Text color
|
||||
--fg-color: rgb(250, 250, 250);
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(100, 100, 100);
|
||||
// Border color
|
||||
--brd-color: rgb(16, 16, 16);
|
||||
// Error color
|
||||
--err-color: rgb(8, 8, 8);
|
||||
// Acent color
|
||||
--ac-color: #50fa7b;
|
||||
--ac-sel-color: rgb(80, 250, 123, 0.8);
|
||||
// Active text color
|
||||
--act-color: #000000;
|
||||
}
|
||||
|
||||
:root.black {
|
||||
@include blackTheme;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root.auto {
|
||||
@include darkTheme;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root.auto {
|
||||
@include lightTheme;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,23 @@ import * as querystring from "querystring";
|
||||
* output this: 'msg1=value1&msg2=value2'
|
||||
* @param dataArguments
|
||||
*/
|
||||
function joinDataArguments(dataArguments) {
|
||||
const joinDataArguments = dataArguments => {
|
||||
let data = "";
|
||||
dataArguments.forEach(function (argument, i) {
|
||||
dataArguments.forEach((argument, i) => {
|
||||
if (i === 0) {
|
||||
data += argument;
|
||||
} else {
|
||||
data += "&" + argument;
|
||||
data += `&${argument}`;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
function parseCurlCommand(curlCommand) {
|
||||
const parseCurlCommand = curlCommand => {
|
||||
let newlineFound = /\r|\n/.exec(curlCommand);
|
||||
if (newlineFound) {
|
||||
// remove newlines
|
||||
curlCommand = curlCommand.replace(/\\\r|\\\n/g, "");
|
||||
curlCommand = curlCommand.replace(/\r|\n/g, "");
|
||||
}
|
||||
// yargs parses -XPOST as separate arguments. just prescreen for it.
|
||||
curlCommand = curlCommand.replace(/ -XPOST/, " -X POST");
|
||||
@@ -47,7 +47,7 @@ function parseCurlCommand(curlCommand) {
|
||||
}
|
||||
let headers;
|
||||
|
||||
let parseHeaders = function (headerFieldName) {
|
||||
const parseHeaders = headerFieldName => {
|
||||
if (parsedArguments[headerFieldName]) {
|
||||
if (!headers) {
|
||||
headers = {};
|
||||
@@ -55,7 +55,7 @@ function parseCurlCommand(curlCommand) {
|
||||
if (!Array.isArray(parsedArguments[headerFieldName])) {
|
||||
parsedArguments[headerFieldName] = [parsedArguments[headerFieldName]];
|
||||
}
|
||||
parsedArguments[headerFieldName].forEach(function (header) {
|
||||
parsedArguments[headerFieldName].forEach(header => {
|
||||
if (header.includes("Cookie")) {
|
||||
// stupid javascript tricks: closure
|
||||
cookieString = header;
|
||||
@@ -95,17 +95,15 @@ function parseCurlCommand(curlCommand) {
|
||||
if (!Array.isArray(parsedArguments.F)) {
|
||||
parsedArguments.F = [parsedArguments.F];
|
||||
}
|
||||
parsedArguments.F.forEach(function (multipartArgument) {
|
||||
parsedArguments.F.forEach(multipartArgument => {
|
||||
// input looks like key=value. value could be json or a file path prepended with an @
|
||||
const [key, value] = multipartArgument.split("=", 2);
|
||||
multipartUploads[key] = value;
|
||||
});
|
||||
}
|
||||
if (cookieString) {
|
||||
let cookieParseOptions = {
|
||||
decode: function (s) {
|
||||
return s;
|
||||
}
|
||||
const cookieParseOptions = {
|
||||
decode: s => s
|
||||
};
|
||||
// separate out cookie headers into separate data structure
|
||||
// note: cookie is case insensitive
|
||||
@@ -174,8 +172,8 @@ function parseCurlCommand(curlCommand) {
|
||||
});
|
||||
|
||||
urlObject.search = null; // Clean out the search/query portion.
|
||||
let request = {
|
||||
url: url,
|
||||
const request = {
|
||||
url,
|
||||
urlWithoutQuery: URL.format(urlObject)
|
||||
};
|
||||
if (compressed) {
|
||||
@@ -223,6 +221,6 @@ function parseCurlCommand(curlCommand) {
|
||||
request.insecure = true;
|
||||
}
|
||||
return request;
|
||||
}
|
||||
};
|
||||
|
||||
export default parseCurlCommand;
|
||||
|
||||
238
assets/js/oauth.js
Normal file
238
assets/js/oauth.js
Normal file
@@ -0,0 +1,238 @@
|
||||
const redirectUri = `${window.location.origin}/`;
|
||||
|
||||
// GENERAL HELPER FUNCTIONS
|
||||
|
||||
/**
|
||||
* Makes a POST request and parse the response as JSON
|
||||
*
|
||||
* @param {String} url - The resource
|
||||
* @param {Object} params - Configuration options
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const sendPostRequest = async (url, params) => {
|
||||
const body = Object.keys(params)
|
||||
.map(key => `${key}=${params[key]}`)
|
||||
.join("&");
|
||||
const options = {
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
|
||||
},
|
||||
body
|
||||
};
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (err) {
|
||||
console.error("Request failed", err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a query string into an object
|
||||
*
|
||||
* @param {String} searchQuery - The search query params
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const parseQueryString = searchQuery => {
|
||||
if (searchQuery === "") {
|
||||
return {};
|
||||
}
|
||||
const segments = searchQuery.split("&").map(s => s.split("="));
|
||||
const queryString = segments.reduce(
|
||||
(obj, el) => ({ ...obj, [el[0]]: el[1] }),
|
||||
{}
|
||||
);
|
||||
return queryString;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get OAuth configuration from OpenID Discovery endpoint
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const getTokenConfiguration = async endpoint => {
|
||||
const options = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-type": "application/json"
|
||||
}
|
||||
};
|
||||
try {
|
||||
const response = await fetch(endpoint, options);
|
||||
const config = await response.json();
|
||||
return config;
|
||||
} catch (err) {
|
||||
console.error("Request failed", err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
// PKCE HELPER FUNCTIONS
|
||||
|
||||
/**
|
||||
* Generates a secure random string using the browser crypto functions
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const generateRandomString = () => {
|
||||
const array = new Uint32Array(28);
|
||||
window.crypto.getRandomValues(array);
|
||||
return Array.from(array, dec => `0${dec.toString(16)}`.substr(-2)).join("");
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the SHA256 hash of the input text
|
||||
*
|
||||
* @returns {Promise<ArrayBuffer>}
|
||||
*/
|
||||
|
||||
const sha256 = plain => {
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(plain);
|
||||
return window.crypto.subtle.digest("SHA-256", data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes the input string into Base64 format
|
||||
*
|
||||
* @param {String} str - The string to be converted
|
||||
* @returns {Promise<ArrayBuffer>}
|
||||
*/
|
||||
|
||||
const base64urlencode = (
|
||||
str // Converts the ArrayBuffer to string using Uint8 array to convert to what btoa accepts.
|
||||
) =>
|
||||
// btoa accepts chars only within ascii 0-255 and base64 encodes them.
|
||||
// Then convert the base64 encoded to base64url encoded
|
||||
// (replace + with -, replace / with _, trim trailing =)
|
||||
btoa(String.fromCharCode.apply(null, new Uint8Array(str)))
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=+$/, "");
|
||||
|
||||
/**
|
||||
* Return the base64-urlencoded sha256 hash for the PKCE challenge
|
||||
*
|
||||
* @param {String} v - The randomly generated string
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
const pkceChallengeFromVerifier = async v => {
|
||||
const hashed = await sha256(v);
|
||||
return base64urlencode(hashed);
|
||||
};
|
||||
|
||||
// OAUTH REQUEST
|
||||
|
||||
/**
|
||||
* Initiates PKCE Auth Code flow when requested
|
||||
*
|
||||
* @param {Object} - The necessary params
|
||||
* @returns {Void}
|
||||
*/
|
||||
|
||||
const tokenRequest = async ({
|
||||
oidcDiscoveryUrl,
|
||||
grantType,
|
||||
authUrl,
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
scope
|
||||
}) => {
|
||||
// Check oauth configuration
|
||||
if (oidcDiscoveryUrl !== "") {
|
||||
const {
|
||||
authorization_endpoint,
|
||||
token_endpoint
|
||||
} = await getTokenConfiguration(oidcDiscoveryUrl);
|
||||
authUrl = authorization_endpoint;
|
||||
accessTokenUrl = token_endpoint;
|
||||
}
|
||||
|
||||
// Store oauth information
|
||||
localStorage.setItem("token_endpoint", accessTokenUrl);
|
||||
localStorage.setItem("client_id", clientId);
|
||||
|
||||
// Create and store a random state value
|
||||
const state = generateRandomString();
|
||||
localStorage.setItem("pkce_state", state);
|
||||
|
||||
// Create and store a new PKCE code_verifier (the plaintext random secret)
|
||||
const code_verifier = generateRandomString();
|
||||
localStorage.setItem("pkce_code_verifier", code_verifier);
|
||||
|
||||
// Hash and base64-urlencode the secret to use as the challenge
|
||||
const code_challenge = await pkceChallengeFromVerifier(code_verifier);
|
||||
|
||||
// Build the authorization URL
|
||||
const buildUrl = () =>
|
||||
`${authUrl + `?response_type=${grantType}`}&client_id=${encodeURIComponent(
|
||||
clientId
|
||||
)}&state=${encodeURIComponent(state)}&scope=${encodeURIComponent(
|
||||
scope
|
||||
)}&redirect_uri=${encodeURIComponent(
|
||||
redirectUri
|
||||
)}&code_challenge=${encodeURIComponent(
|
||||
code_challenge
|
||||
)}&code_challenge_method=S256`;
|
||||
|
||||
// Redirect to the authorization server
|
||||
window.location = buildUrl();
|
||||
};
|
||||
|
||||
// OAUTH REDIRECT HANDLING
|
||||
|
||||
/**
|
||||
* Handle the redirect back from the authorization server and
|
||||
* get an access token from the token endpoint
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const oauthRedirect = async () => {
|
||||
let tokenResponse = "";
|
||||
let q = parseQueryString(window.location.search.substring(1));
|
||||
// Check if the server returned an error string
|
||||
if (q.error) {
|
||||
alert(`Error returned from authorization server: ${q.error}`);
|
||||
}
|
||||
// If the server returned an authorization code, attempt to exchange it for an access token
|
||||
if (q.code) {
|
||||
// Verify state matches what we set at the beginning
|
||||
if (localStorage.getItem("pkce_state") != q.state) {
|
||||
alert("Invalid state");
|
||||
} else {
|
||||
try {
|
||||
// Exchange the authorization code for an access token
|
||||
tokenResponse = await sendPostRequest(
|
||||
localStorage.getItem("token_endpoint"),
|
||||
{
|
||||
grant_type: "authorization_code",
|
||||
code: q.code,
|
||||
client_id: localStorage.getItem("client_id"),
|
||||
redirect_uri: redirectUri,
|
||||
code_verifier: localStorage.getItem("pkce_code_verifier")
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(`${error.error}\n\n${error.error_description}`);
|
||||
}
|
||||
}
|
||||
// Clean these up since we don't need them anymore
|
||||
localStorage.removeItem("pkce_state");
|
||||
localStorage.removeItem("pkce_code_verifier");
|
||||
localStorage.removeItem("token_endpoint");
|
||||
localStorage.removeItem("client_id");
|
||||
return tokenResponse;
|
||||
}
|
||||
return tokenResponse;
|
||||
};
|
||||
|
||||
export { tokenRequest, oauthRedirect };
|
||||
@@ -2,36 +2,39 @@ export default () => {
|
||||
//*** Determine whether or not the PWA has been installed. ***//
|
||||
|
||||
// Step 1: Check local storage
|
||||
let pwaInstalled = localStorage.getItem('pwaInstalled') === 'yes';
|
||||
let pwaInstalled = localStorage.getItem("pwaInstalled") === "yes";
|
||||
|
||||
// Step 2: Check if the display-mode is standalone. (Only permitted for PWAs.)
|
||||
if (!pwaInstalled && window.matchMedia('(display-mode: standalone)').matches) {
|
||||
localStorage.setItem('pwaInstalled', 'yes');
|
||||
if (
|
||||
!pwaInstalled &&
|
||||
window.matchMedia("(display-mode: standalone)").matches
|
||||
) {
|
||||
localStorage.setItem("pwaInstalled", "yes");
|
||||
pwaInstalled = true;
|
||||
}
|
||||
|
||||
// Step 3: Check if the navigator is in standalone mode. (Again, only permitted for PWAs.)
|
||||
if (!pwaInstalled && window.navigator.standalone === true) {
|
||||
localStorage.setItem('pwaInstalled', 'yes');
|
||||
localStorage.setItem("pwaInstalled", "yes");
|
||||
pwaInstalled = true;
|
||||
}
|
||||
|
||||
//*** If the PWA has not been installed, show the install PWA prompt.. ***//
|
||||
let deferredPrompt = null;
|
||||
window.addEventListener('beforeinstallprompt', (event) => {
|
||||
window.addEventListener("beforeinstallprompt", event => {
|
||||
deferredPrompt = event;
|
||||
|
||||
// Show the install button if the prompt appeared.
|
||||
if (!pwaInstalled) {
|
||||
document.querySelector('#installPWA').style.display = 'inline-flex';
|
||||
document.querySelector("#installPWA").style.display = "inline-flex";
|
||||
}
|
||||
});
|
||||
|
||||
// When the app is installed, remove install prompts.
|
||||
window.addEventListener('appinstalled', (event) => {
|
||||
localStorage.setItem('pwaInstalled', 'yes');
|
||||
window.addEventListener("appinstalled", event => {
|
||||
localStorage.setItem("pwaInstalled", "yes");
|
||||
pwaInstalled = true;
|
||||
document.getElementById('installPWA').style.display = 'none';
|
||||
document.getElementById("installPWA").style.display = "none";
|
||||
});
|
||||
|
||||
// When the app is uninstalled, add the prompts back
|
||||
@@ -40,13 +43,14 @@ export default () => {
|
||||
deferredPrompt.prompt();
|
||||
let outcome = await deferredPrompt.userChoice;
|
||||
|
||||
if (outcome === 'accepted') {
|
||||
console.log('Postwoman was installed successfully.')
|
||||
if (outcome === "accepted") {
|
||||
console.log("Postwoman was installed successfully.");
|
||||
} else {
|
||||
console.log('Postwoman could not be installed. (Installation rejected by user.)')
|
||||
console.log(
|
||||
"Postwoman could not be installed. (Installation rejected by user.)"
|
||||
);
|
||||
}
|
||||
deferredPrompt = null;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
25
build.js
25
build.js
@@ -1,8 +1,6 @@
|
||||
const axios = require("axios");
|
||||
const fs = require("fs");
|
||||
const {
|
||||
spawnSync
|
||||
} = require("child_process");
|
||||
const { spawnSync } = require("child_process");
|
||||
const runCommand = (command, args) =>
|
||||
spawnSync(command, args)
|
||||
.stdout.toString()
|
||||
@@ -21,18 +19,24 @@ try {
|
||||
|
||||
let version = {};
|
||||
// Get the current version name as the tag from Git.
|
||||
version.name = process.env.TRAVIS_TAG || runCommand("git", ["tag"]);
|
||||
version.name =
|
||||
process.env.TRAVIS_TAG ||
|
||||
runCommand("git", ["tag --sort=committerdate | tail -1"]);
|
||||
|
||||
// FALLBACK: If version.name was unset, let's grab it from GitHub.
|
||||
if (!version.name) {
|
||||
version.name = (await axios
|
||||
version.name = (
|
||||
await axios
|
||||
.get("https://api.github.com/repos/liyasthomas/postwoman/releases")
|
||||
// If we can't get it from GitHub, we'll resort to getting it from package.json
|
||||
.catch(ex => ({
|
||||
data: [{
|
||||
data: [
|
||||
{
|
||||
tag_name: require("./package.json").version
|
||||
}]
|
||||
}))).data[0]["tag_name"];
|
||||
}
|
||||
]
|
||||
}))
|
||||
).data[0]["tag_name"];
|
||||
}
|
||||
|
||||
// Get the current version hash as the short hash from Git.
|
||||
@@ -43,12 +47,13 @@ try {
|
||||
runCommand("git", ["branch"])
|
||||
.split("* ")[1]
|
||||
.split(" ")[0] + (IS_DEV_MODE ? " - DEV MODE" : "");
|
||||
if (["", "master"].includes(version.variant))
|
||||
if (["", "master"].includes(version.variant)) {
|
||||
delete version.variant;
|
||||
}
|
||||
|
||||
// Write version data into a file
|
||||
fs.writeFileSync(
|
||||
PW_BUILD_DATA_DIR + "/version.json",
|
||||
`${PW_BUILD_DATA_DIR}/version.json`,
|
||||
JSON.stringify(version)
|
||||
);
|
||||
})();
|
||||
|
||||
91
components/ace-editor.vue
Normal file
91
components/ace-editor.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<pre ref="editor"></pre>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const DEFAULT_THEME = "twilight";
|
||||
|
||||
import ace from "ace-builds";
|
||||
import "ace-builds/webpack-resolver";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
lang: {
|
||||
type: String,
|
||||
default: "json"
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
cacheValue: ""
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(value) {
|
||||
if (value !== this.cacheValue) {
|
||||
this.editor.session.setValue(value, 1);
|
||||
this.cacheValue = value;
|
||||
}
|
||||
},
|
||||
theme() {
|
||||
this.editor.setTheme("ace/theme/" + this.defineTheme());
|
||||
},
|
||||
lang(value) {
|
||||
this.editor.getSession().setMode("ace/mode/" + value);
|
||||
},
|
||||
options(value) {
|
||||
this.editor.setOptions(value);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const editor = ace.edit(this.$refs.editor, {
|
||||
theme: `ace/theme/${this.defineTheme()}`,
|
||||
mode: `ace/mode/${this.lang}`,
|
||||
...this.options
|
||||
});
|
||||
|
||||
if (this.value) editor.setValue(this.value, 1);
|
||||
|
||||
this.editor = editor;
|
||||
this.cacheValue = this.value;
|
||||
|
||||
editor.on("change", () => {
|
||||
const content = editor.getValue();
|
||||
this.$emit("input", content);
|
||||
this.cacheValue = content;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
defineTheme() {
|
||||
if (this.theme) {
|
||||
return this.theme;
|
||||
}
|
||||
return (
|
||||
this.$store.state.postwoman.settings.THEME_ACE_EDITOR || DEFAULT_THEME
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy();
|
||||
this.editor.container.remove();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -3,7 +3,7 @@
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="placeholder"
|
||||
v-model="value"
|
||||
v-model="text"
|
||||
@input="updateSuggestions"
|
||||
@keyup="updateSuggestions"
|
||||
@click="updateSuggestions"
|
||||
@@ -23,12 +23,14 @@
|
||||
@click.prevent="forceSuggestion(suggestion)"
|
||||
:class="{ active: currentSuggestionIndex === index }"
|
||||
:key="index"
|
||||
>{{ suggestion }}</li>
|
||||
>
|
||||
{{ suggestion }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style scoped lang="scss">
|
||||
.autocomplete-wrapper {
|
||||
position: relative;
|
||||
|
||||
@@ -45,20 +47,21 @@
|
||||
margin: 0 4px;
|
||||
left: 0;
|
||||
padding: 0;
|
||||
border-radius: 0 0 4px 4px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
z-index: 9999;
|
||||
transition: transform 200ms ease-out;
|
||||
transition: transform 0.2s ease-out;
|
||||
box-shadow: 0 5px 30px rgba(black, 0.1);
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 8px 16px;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
white-space: pre-wrap;
|
||||
font-weight: 400;
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 4px 4px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
@@ -89,25 +92,31 @@ export default {
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "Start typing...",
|
||||
default: "",
|
||||
required: false
|
||||
},
|
||||
|
||||
source: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value() {
|
||||
this.$emit("input", this.value);
|
||||
text() {
|
||||
this.$emit("input", this.text);
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
value: "application/json",
|
||||
text: this.value,
|
||||
selectionStart: 0,
|
||||
suggestionsOffsetLeft: 0,
|
||||
currentSuggestionIndex: -1,
|
||||
@@ -133,10 +142,10 @@ export default {
|
||||
},
|
||||
|
||||
forceSuggestion(text) {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
this.value = input + text;
|
||||
let input = this.text.substring(0, this.selectionStart);
|
||||
this.text = input + text;
|
||||
|
||||
this.selectionStart = this.value.length;
|
||||
this.selectionStart = this.text.length;
|
||||
this.suggestionsVisible = true;
|
||||
this.currentSuggestionIndex = -1;
|
||||
},
|
||||
@@ -165,8 +174,8 @@ export default {
|
||||
this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0
|
||||
];
|
||||
if (activeSuggestion) {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
this.value = input + activeSuggestion;
|
||||
let input = this.text.substring(0, this.selectionStart);
|
||||
this.text = input + activeSuggestion;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -183,7 +192,7 @@ export default {
|
||||
* @returns {default.props.source|{type, required}}
|
||||
*/
|
||||
suggestions() {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
let input = this.text.substring(0, this.selectionStart);
|
||||
|
||||
return (
|
||||
this.source
|
||||
@@ -195,8 +204,8 @@ export default {
|
||||
})
|
||||
// Cut off the part that's already been typed.
|
||||
.map(entry => entry.substring(this.selectionStart))
|
||||
// We only want the top 3 suggestions.
|
||||
.slice(0, 3)
|
||||
// We only want the top 6 suggestions.
|
||||
.slice(0, 6)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">New Collection</h3>
|
||||
<h3 class="title">{{ $t("new_collection") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
@@ -17,32 +17,40 @@
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input type="text" v-model="name" placeholder="My New Collection" />
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
:placeholder="$t('my_new_collection')"
|
||||
@keyup.enter="addNewCollection"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addNewCollection">
|
||||
<i class="material-icons">add</i>
|
||||
<span>Create</span>
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="icon primary" @click="addNewCollection">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -50,11 +58,25 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections))
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
addNewCollection() {
|
||||
if (!this.$data.name) {
|
||||
this.$toast.info($t("invalid_collection_name"));
|
||||
return;
|
||||
}
|
||||
this.$store.commit("postwoman/addNewCollection", {
|
||||
name: this.$data.name
|
||||
});
|
||||
this.$emit("hide-modal");
|
||||
this.syncCollections();
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">New Folder</h3>
|
||||
<h3 class="title">{{ $t("new_folder") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
@@ -17,26 +17,32 @@
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input type="text" v-model="name" placeholder="My New Folder" />
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
:placeholder="$t('my_new_folder')"
|
||||
@keyup.enter="addNewFolder"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addNewFolder">
|
||||
<i class="material-icons">add</i>
|
||||
<span>Create</span>
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="icon primary" @click="addNewFolder">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
@@ -44,7 +50,7 @@ export default {
|
||||
collectionIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -6,53 +6,82 @@
|
||||
<i class="material-icons" v-show="!showChildren">arrow_right</i>
|
||||
<i class="material-icons" v-show="showChildren">arrow_drop_down</i>
|
||||
<i class="material-icons">folder</i>
|
||||
<span>{{collection.name}}</span>
|
||||
<span>{{ collection.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('more')">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="$emit('add-folder')" v-close-popover>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("new_folder") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeCollection" v-tooltip="'Delete collection'">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
<button class="icon" @click="$emit('edit-collection')" v-tooltip="'Edit collection'">
|
||||
<button
|
||||
class="icon"
|
||||
@click="$emit('edit-collection')"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">create</i>
|
||||
</button>
|
||||
<button class="icon" @click="$emit('add-folder')" v-tooltip="'New Folder'">
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeCollection" v-close-popover>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
|
||||
<div v-show="showChildren">
|
||||
<ul>
|
||||
<li v-for="(folder, index) in collection.folders" :key="folder.name">
|
||||
<folder
|
||||
v-bind:folder="folder"
|
||||
v-bind:folderIndex="index"
|
||||
v-bind:collection-index="collectionIndex"
|
||||
v-on:edit-folder="editFolder(collectionIndex, folder, index)"
|
||||
v-on:edit-request="$emit('edit-request', $event)"
|
||||
:folder="folder"
|
||||
:folderIndex="index"
|
||||
:collection-index="collectionIndex"
|
||||
@edit-folder="editFolder(collectionIndex, folder, index)"
|
||||
@edit-request="$emit('edit-request', $event)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="(collection.folders.length === 0) && (collection.requests.length === 0)">
|
||||
<label>Collection is empty</label>
|
||||
<li
|
||||
v-if="
|
||||
collection.folders.length === 0 && collection.requests.length === 0
|
||||
"
|
||||
>
|
||||
<label>{{ $t("collection_empty") }}</label>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li v-for="(request, index) in collection.requests" :key="index">
|
||||
<request
|
||||
v-bind:request="request"
|
||||
v-bind:collection-index="collectionIndex"
|
||||
v-bind:folder-index="-1"
|
||||
v-bind:request-index="index"
|
||||
v-on:edit-request="$emit('edit-request', { request, collectionIndex, folderIndex: undefined, requestIndex: index })"
|
||||
></request>
|
||||
:request="request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="-1"
|
||||
:request-index="index"
|
||||
@edit-request="
|
||||
$emit('edit-request', {
|
||||
request,
|
||||
collectionIndex,
|
||||
folderIndex: undefined,
|
||||
requestIndex: index
|
||||
})
|
||||
"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -66,13 +95,10 @@ ul li {
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import folder from "./folder";
|
||||
import request from "./request";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
folder,
|
||||
request
|
||||
folder: () => import("./folder"),
|
||||
request: () => import("./request")
|
||||
},
|
||||
props: {
|
||||
collectionIndex: Number,
|
||||
@@ -89,7 +115,7 @@ export default {
|
||||
this.showChildren = !this.showChildren;
|
||||
},
|
||||
removeCollection() {
|
||||
if (!confirm("Are you sure you want to remove this collection?")) return;
|
||||
if (!confirm("Are you sure you want to remove this Collection?")) return;
|
||||
this.$store.commit("postwoman/removeCollection", {
|
||||
collectionIndex: this.collectionIndex
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModel">
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Edit Collection</h3>
|
||||
<h3 class="title">{{ $t("edit_collection") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModel">
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -17,26 +17,32 @@
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input type="text" v-model="name" v-bind:placeholder="editingCollection.name" />
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
:placeholder="editingCollection.name"
|
||||
@keyup.enter="saveCollection"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="saveCollection">
|
||||
<i class="material-icons">save</i>
|
||||
<span>Save</span>
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="icon primary" @click="saveCollection">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
@@ -44,7 +50,7 @@ export default {
|
||||
editingCollectionIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -53,6 +59,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
saveCollection() {
|
||||
if (!this.$data.name) {
|
||||
this.$toast.info($t("invalid_collection_name"));
|
||||
return;
|
||||
}
|
||||
const collectionUpdated = {
|
||||
...this.$props.editingCollection,
|
||||
name: this.$data.name
|
||||
@@ -63,7 +73,7 @@ export default {
|
||||
});
|
||||
this.$emit("hide-modal");
|
||||
},
|
||||
hideModel() {
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Edit Folder</h3>
|
||||
<h3 class="title">{{ $t("edit_folder") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
@@ -17,26 +17,32 @@
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input type="text" v-model="name" v-bind:placeholder="folder.name" />
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
:placeholder="folder.name"
|
||||
@keyup.enter="editFolder"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="editFolder">
|
||||
<i class="material-icons">add</i>
|
||||
<span>Save</span>
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="icon primary" @click="editFolder">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
@@ -46,7 +52,7 @@ export default {
|
||||
folderIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Edit Request</h3>
|
||||
<h3 class="title">{{ $t("edit_request") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
@@ -17,41 +17,76 @@
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="selectLabel">Label</label>
|
||||
<input type="text" id="selectLabel" v-model="requestUpdateData.name" :placeholder="request.name" />
|
||||
<label for="selectCollection">Collection</label>
|
||||
<select type="text" id="selectCollection" v-model="requestUpdateData.collectionIndex">
|
||||
<option :key="undefined" :value="undefined" hidden disabled selected>Current Collection</option>
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="requestUpdateData.name"
|
||||
@keyup.enter="saveRequest"
|
||||
:placeholder="request.name"
|
||||
/>
|
||||
<label for="selectCollection">{{ $t("collection") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
type="text"
|
||||
id="selectCollection"
|
||||
v-model="requestUpdateData.collectionIndex"
|
||||
>
|
||||
<option
|
||||
v-for="(collection, index) in $store.state.postwoman.collections"
|
||||
:key="undefined"
|
||||
:value="undefined"
|
||||
hidden
|
||||
disabled
|
||||
selected
|
||||
>{{ $t("current_collection") }}</option
|
||||
>
|
||||
<option
|
||||
v-for="(collection, index) in $store.state.postwoman
|
||||
.collections"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>{{ collection.name }}</option>
|
||||
>
|
||||
{{ collection.name }}
|
||||
</option>
|
||||
</select>
|
||||
<label for="selectFolder">Folder</label>
|
||||
<select type="text" id="selectFolder" v-model="requestUpdateData.folderIndex">
|
||||
</span>
|
||||
<label for="selectFolder">{{ $t("folder") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
type="text"
|
||||
id="selectFolder"
|
||||
v-model="requestUpdateData.folderIndex"
|
||||
>
|
||||
<option :key="undefined" :value="undefined">/</option>
|
||||
<option v-for="(folder, index) in folders" :key="index" :value="index">{{ folder.name }}</option>
|
||||
<option
|
||||
v-for="(folder, index) in folders"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>
|
||||
{{ folder.name }}
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="saveRequest">
|
||||
<i class="material-icons">save</i>
|
||||
<span>Save</span>
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="icon primary" @click="saveRequest">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
@@ -61,7 +96,7 @@ export default {
|
||||
requestIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -107,7 +142,6 @@ export default {
|
||||
// pass data separately to don't depend on request's collection, folder fields
|
||||
// probably, they should be deprecated because they don't describe request itself
|
||||
this.$store.commit("postwoman/editRequest", {
|
||||
requestOld: this.$props.request,
|
||||
requestOldCollectionIndex: this.$props.collectionIndex,
|
||||
requestOldFolderIndex: this.$props.folderIndex,
|
||||
requestOldIndex: this.$props.requestIndex,
|
||||
|
||||
@@ -6,39 +6,57 @@
|
||||
<i class="material-icons" v-show="!showChildren">arrow_right</i>
|
||||
<i class="material-icons" v-show="showChildren">arrow_drop_down</i>
|
||||
<i class="material-icons">folder_open</i>
|
||||
<span>{{folder.name}}</span>
|
||||
<span>{{ folder.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('more')">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="editFolder" v-close-popover>
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeFolder" v-tooltip="'Delete folder'">
|
||||
<button class="icon" @click="removeFolder" v-close-popover>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
<button class="icon" @click="editFolder" v-tooltip="'Edit folder'">
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
|
||||
<div v-show="showChildren">
|
||||
<ul>
|
||||
<li v-for="(request, index) in folder.requests" :key="index">
|
||||
<request
|
||||
v-bind:request="request"
|
||||
v-bind:collection-index="collectionIndex"
|
||||
v-bind:folder-index="folderIndex"
|
||||
v-bind:request-index="index"
|
||||
v-on:edit-request="$emit('edit-request', { request, collectionIndex, folderIndex, requestIndex: index })"
|
||||
></request>
|
||||
:request="request"
|
||||
:collection-index="collectionIndex"
|
||||
:folder-index="folderIndex"
|
||||
:request-index="index"
|
||||
@edit-request="
|
||||
$emit('edit-request', {
|
||||
request,
|
||||
collectionIndex,
|
||||
folderIndex,
|
||||
requestIndex: index
|
||||
})
|
||||
"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="folder.requests.length === 0">
|
||||
<label>Folder is empty</label>
|
||||
<label>{{ $t("folder_empty") }}</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -52,8 +70,6 @@ ul li {
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import request from "./request";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
folder: Object,
|
||||
@@ -61,7 +77,7 @@ export default {
|
||||
folderIndex: Number
|
||||
},
|
||||
components: {
|
||||
request
|
||||
request: () => import("./request")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,16 +1,64 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModel">
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Import / Export Collections</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModel">
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-wrap">
|
||||
<span
|
||||
v-tooltip="{
|
||||
content: !fb.currentUser
|
||||
? $t('login_first')
|
||||
: $t('replace_current')
|
||||
}"
|
||||
>
|
||||
<button
|
||||
:disabled="!fb.currentUser"
|
||||
class="icon"
|
||||
@click="syncCollections"
|
||||
>
|
||||
<i class="material-icons">folder_shared</i>
|
||||
<span>{{ $t("import_from_sync") }}</span>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToReplaceWith"
|
||||
v-tooltip="$t('replace_current')"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("replace_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="replaceWithJSON"
|
||||
style="display: none;"
|
||||
ref="inputChooseFileToReplaceWith"
|
||||
accept="application/json"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToImportFrom"
|
||||
v-tooltip="$t('preserve_current')"
|
||||
>
|
||||
<i class="material-icons">folder_special</i>
|
||||
<span>{{ $t("import_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="importFromJSON"
|
||||
style="display: none;"
|
||||
ref="inputChooseFileToImportFrom"
|
||||
accept="application/json"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -18,59 +66,39 @@
|
||||
<textarea v-model="collectionJson" rows="8"></textarea>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToReplaceWith"
|
||||
v-tooltip="'Replace current'"
|
||||
class="icon primary"
|
||||
@click="exportJSON"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>Replace with JSON</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="replaceWithJSON"
|
||||
style="display: none;"
|
||||
ref="inputChooseFileToReplaceWith"
|
||||
/>
|
||||
{{ $t("export") }}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToImportFrom"
|
||||
v-tooltip="'Preserve current'"
|
||||
>
|
||||
<i class="material-icons">folder_shared</i>
|
||||
<span>Import from JSON</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="importFromJSON"
|
||||
style="display: none;"
|
||||
ref="inputChooseFileToImportFrom"
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="icon" @click="exportJSON" v-tooltip="'Download file'">
|
||||
<i class="material-icons">get_app</i>
|
||||
<span>Export to JSON</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
fb
|
||||
};
|
||||
},
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
computed: {
|
||||
collectionJson() {
|
||||
@@ -78,7 +106,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideModel() {
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
},
|
||||
openDialogChooseFileToReplaceWith() {
|
||||
@@ -92,7 +120,25 @@ export default {
|
||||
reader.onload = event => {
|
||||
let content = event.target.result;
|
||||
let collections = JSON.parse(content);
|
||||
this.$store.commit("postwoman/replaceCollections", collections);
|
||||
if (collections[0]) {
|
||||
let [name, folders, requests] = Object.keys(collections[0]);
|
||||
if (
|
||||
name === "name" &&
|
||||
folders === "folders" &&
|
||||
requests === "requests"
|
||||
) {
|
||||
// Do nothing
|
||||
}
|
||||
} else if (
|
||||
collections.info &&
|
||||
collections.info.schema.includes("v2.1.0")
|
||||
) {
|
||||
collections = this.parsePostmanCollection(collections);
|
||||
} else {
|
||||
return this.failedImport();
|
||||
}
|
||||
this.$store.commit("postwoman/importCollections", collections);
|
||||
this.fileImported();
|
||||
};
|
||||
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0]);
|
||||
},
|
||||
@@ -101,7 +147,25 @@ export default {
|
||||
reader.onload = event => {
|
||||
let content = event.target.result;
|
||||
let collections = JSON.parse(content);
|
||||
if (collections[0]) {
|
||||
let [name, folders, requests] = Object.keys(collections[0]);
|
||||
if (
|
||||
name === "name" &&
|
||||
folders === "folders" &&
|
||||
requests === "requests"
|
||||
) {
|
||||
// Do nothing
|
||||
}
|
||||
} else if (
|
||||
collections.info &&
|
||||
collections.info.schema.includes("v2.1.0")
|
||||
) {
|
||||
collections = this.parsePostmanCollection(collections);
|
||||
} else {
|
||||
return this.failedImport();
|
||||
}
|
||||
this.$store.commit("postwoman/importCollections", collections);
|
||||
this.fileImported();
|
||||
};
|
||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0]);
|
||||
},
|
||||
@@ -119,6 +183,141 @@ export default {
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done"
|
||||
});
|
||||
},
|
||||
syncCollections() {
|
||||
this.$store.commit("postwoman/replaceCollections", fb.currentCollections);
|
||||
this.fileImported();
|
||||
},
|
||||
fileImported() {
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
icon: "folder_shared"
|
||||
});
|
||||
},
|
||||
failedImport() {
|
||||
this.$toast.error(this.$t("import_failed"), {
|
||||
icon: "error"
|
||||
});
|
||||
},
|
||||
parsePostmanCollection(collection, folders = true) {
|
||||
let postwomanCollection = folders
|
||||
? [
|
||||
{
|
||||
name: "",
|
||||
folders: [],
|
||||
requests: []
|
||||
}
|
||||
]
|
||||
: {
|
||||
name: "",
|
||||
requests: []
|
||||
};
|
||||
for (let collectionItem of collection.item) {
|
||||
if (collectionItem.request) {
|
||||
if (postwomanCollection[0]) {
|
||||
postwomanCollection[0].name = collection.info
|
||||
? collection.info.name
|
||||
: "";
|
||||
postwomanCollection[0].requests.push(
|
||||
this.parsePostmanRequest(collectionItem)
|
||||
);
|
||||
} else {
|
||||
postwomanCollection.name = collection.name ? collection.name : "";
|
||||
postwomanCollection.requests.push(
|
||||
this.parsePostmanRequest(collectionItem)
|
||||
);
|
||||
}
|
||||
} else if (collectionItem.item) {
|
||||
if (collectionItem.item[0]) {
|
||||
postwomanCollection[0].folders.push(
|
||||
this.parsePostmanCollection(collectionItem, false)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return postwomanCollection;
|
||||
},
|
||||
parsePostmanRequest(requestObject) {
|
||||
let pwRequest = {
|
||||
url: "",
|
||||
path: "",
|
||||
method: "",
|
||||
auth: "",
|
||||
httpUser: "",
|
||||
httpPassword: "",
|
||||
passwordFieldType: "password",
|
||||
bearerToken: "",
|
||||
headers: [],
|
||||
params: [],
|
||||
bodyParams: [],
|
||||
rawParams: "",
|
||||
rawInput: false,
|
||||
contentType: "",
|
||||
requestType: "",
|
||||
name: ""
|
||||
};
|
||||
|
||||
pwRequest.name = requestObject.name;
|
||||
let requestObjectUrl = requestObject.request.url.raw.match(
|
||||
/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/
|
||||
);
|
||||
pwRequest.url = requestObjectUrl[1];
|
||||
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : "";
|
||||
pwRequest.method = requestObject.request.method;
|
||||
let itemAuth = requestObject.request.auth
|
||||
? requestObject.request.auth
|
||||
: "";
|
||||
let authType = itemAuth ? itemAuth.type : "";
|
||||
if (authType === "basic") {
|
||||
pwRequest.auth = "Basic Auth";
|
||||
pwRequest.httpUser =
|
||||
itemAuth.basic[0].key === "username"
|
||||
? itemAuth.basic[0].value
|
||||
: itemAuth.basic[1].value;
|
||||
pwRequest.httpPassword =
|
||||
itemAuth.basic[0].key === "password"
|
||||
? itemAuth.basic[0].value
|
||||
: itemAuth.basic[1].value;
|
||||
} else if (authType === "oauth2") {
|
||||
pwRequest.auth = "OAuth 2.0";
|
||||
pwRequest.bearerToken =
|
||||
itemAuth.oauth2[0].key === "accessToken"
|
||||
? itemAuth.oauth2[0].value
|
||||
: itemAuth.oauth2[1].value;
|
||||
} else if (authType === "bearer") {
|
||||
pwRequest.auth = "Bearer Token";
|
||||
pwRequest.bearerToken = itemAuth.bearer[0].value;
|
||||
}
|
||||
let requestObjectHeaders = requestObject.request.header;
|
||||
if (requestObjectHeaders) {
|
||||
pwRequest.headers = requestObjectHeaders;
|
||||
for (let header of pwRequest.headers) {
|
||||
delete header.name;
|
||||
delete header.type;
|
||||
}
|
||||
}
|
||||
let requestObjectParams = requestObject.request.url.query;
|
||||
if (requestObjectParams) {
|
||||
pwRequest.params = requestObjectParams;
|
||||
for (let param of pwRequest.params) {
|
||||
delete param.disabled;
|
||||
}
|
||||
}
|
||||
if (requestObject.request.body) {
|
||||
if (requestObject.request.body.mode === "urlencoded") {
|
||||
let params = requestObject.request.body.urlencoded;
|
||||
pwRequest.bodyParams = params ? params : [];
|
||||
for (let param of pwRequest.bodyParams) {
|
||||
delete param.type;
|
||||
}
|
||||
} else if (requestObject.request.body.mode === "raw") {
|
||||
pwRequest.rawInput = true;
|
||||
pwRequest.rawParams = requestObject.request.body.raw;
|
||||
}
|
||||
}
|
||||
return pwRequest;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,75 +4,102 @@ TODO:
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="collections-wrapper">
|
||||
<addCollection v-bind:show="showModalAdd" v-on:hide-modal="displayModalAdd(false)"></addCollection>
|
||||
<pw-section class="yellow" :label="$t('collections')" ref="collections">
|
||||
<addCollection :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
|
||||
<editCollection
|
||||
v-bind:show="showModalEdit"
|
||||
v-bind:editingCollection="editingCollection"
|
||||
v-bind:editingCollectionIndex="editingCollectionIndex"
|
||||
v-on:hide-modal="displayModalEdit(false)"
|
||||
></editCollection>
|
||||
:show="showModalEdit"
|
||||
:editingCollection="editingCollection"
|
||||
:editingCollectionIndex="editingCollectionIndex"
|
||||
@hide-modal="displayModalEdit(false)"
|
||||
/>
|
||||
<addFolder
|
||||
v-bind:show="showModalAddFolder"
|
||||
v-bind:collection="editingCollection"
|
||||
v-bind:collectionIndex="editingCollectionIndex"
|
||||
v-on:hide-modal="displayModalAddFolder(false)"
|
||||
></addFolder>
|
||||
:show="showModalAddFolder"
|
||||
:collection="editingCollection"
|
||||
:collectionIndex="editingCollectionIndex"
|
||||
@hide-modal="displayModalAddFolder(false)"
|
||||
/>
|
||||
<editFolder
|
||||
v-bind:show="showModalEditFolder"
|
||||
v-bind:collection="editingCollection"
|
||||
v-bind:collectionIndex="editingCollectionIndex"
|
||||
v-bind:folder="editingFolder"
|
||||
v-bind:folderIndex="editingFolderIndex"
|
||||
v-on:hide-modal="displayModalEditFolder(false)"
|
||||
></editFolder>
|
||||
:show="showModalEditFolder"
|
||||
:collection="editingCollection"
|
||||
:collectionIndex="editingCollectionIndex"
|
||||
:folder="editingFolder"
|
||||
:folderIndex="editingFolderIndex"
|
||||
@hide-modal="displayModalEditFolder(false)"
|
||||
/>
|
||||
<editRequest
|
||||
v-bind:show="showModalEditRequest"
|
||||
v-bind:collectionIndex="editingCollectionIndex"
|
||||
v-bind:folderIndex="editingFolderIndex"
|
||||
v-bind:request="editingRequest"
|
||||
v-bind:requestIndex="editingRequestIndex"
|
||||
v-on:hide-modal="displayModalEditRequest(false)"
|
||||
></editRequest>
|
||||
:show="showModalEditRequest"
|
||||
:collectionIndex="editingCollectionIndex"
|
||||
:folderIndex="editingFolderIndex"
|
||||
:request="editingRequest"
|
||||
:requestIndex="editingRequestIndex"
|
||||
@hide-modal="displayModalEditRequest(false)"
|
||||
/>
|
||||
<importExportCollections
|
||||
v-bind:show="showModalImportExport"
|
||||
v-on:hide-modal="displayModalImportExport(false)"
|
||||
></importExportCollections>
|
||||
:show="showModalImportExport"
|
||||
@hide-modal="displayModalImportExport(false)"
|
||||
/>
|
||||
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="displayModalAdd(true)">
|
||||
<i class="material-icons">add</i>
|
||||
<span>New</span>
|
||||
<span>{{ $t("new") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="displayModalImportExport(true)">
|
||||
<i class="material-icons">import_export</i>
|
||||
<span>Import / Export</span>
|
||||
{{ $t("import_export") }}
|
||||
</button>
|
||||
<!-- <a
|
||||
href="https://github.com/liyasthomas/postwoman/wiki/Collections"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" v-tooltip="'Wiki'">
|
||||
<i class="material-icons">help</i>
|
||||
</button>
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="collections.length === 0" class="info">
|
||||
Create new collection
|
||||
</p>
|
||||
<virtual-list
|
||||
class="virtual-list"
|
||||
:class="{ filled: collections.length }"
|
||||
:size="152"
|
||||
:remain="Math.min(5, collections.length)"
|
||||
>
|
||||
<ul>
|
||||
<li v-for="(collection, index) in collections" :key="collection.name">
|
||||
<collection
|
||||
v-bind:collection-index="index"
|
||||
v-bind:collection="collection"
|
||||
v-on:edit-collection="editCollection(collection, index)"
|
||||
v-on:add-folder="addFolder(collection, index)"
|
||||
v-on:edit-folder="editFolder($event)"
|
||||
v-on:edit-request="editRequest($event)"
|
||||
></collection>
|
||||
:collection-index="index"
|
||||
:collection="collection"
|
||||
@edit-collection="editCollection(collection, index)"
|
||||
@add-folder="addFolder(collection, index)"
|
||||
@edit-folder="editFolder($event)"
|
||||
@edit-request="editRequest($event)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="collections.length === 0">
|
||||
<label>Collections are empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</virtual-list>
|
||||
<nuxt-link :to="localePath('doc')" :aria-label="$t('documentation')">
|
||||
<button class="icon">
|
||||
<i class="material-icons">books</i>
|
||||
<span>{{ $t("generate_docs") }}</span>
|
||||
</button>
|
||||
</nuxt-link>
|
||||
</pw-section>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 276px);
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -80,23 +107,20 @@ ul {
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import addCollection from "./addCollection";
|
||||
import addFolder from "./addFolder";
|
||||
import collection from "./collection";
|
||||
import editCollection from "./editCollection";
|
||||
import editFolder from "./editFolder";
|
||||
import editRequest from "./editRequest";
|
||||
import importExportCollections from "./importExportCollections";
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addCollection,
|
||||
addFolder,
|
||||
collection,
|
||||
editCollection,
|
||||
editFolder,
|
||||
editRequest,
|
||||
importExportCollections
|
||||
"pw-section": () => import("../section"),
|
||||
addCollection: () => import("./addCollection"),
|
||||
addFolder: () => import("./addFolder"),
|
||||
editCollection: () => import("./editCollection"),
|
||||
editFolder: () => import("./editFolder"),
|
||||
editRequest: () => import("./editRequest"),
|
||||
importExportCollections: () => import("./importExportCollections"),
|
||||
VirtualList: () => import("vue-virtual-scroll-list")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -119,6 +143,15 @@ export default {
|
||||
return this.$store.state.postwoman.collections;
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this._keyListener = function(e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
this.showModalAdd = this.showModalEdit = this.showModalImportExport = this.showModalAddFolder = this.showModalEditFolder = this.showModalEditRequest = false;
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", this._keyListener.bind(this));
|
||||
},
|
||||
methods: {
|
||||
displayModalAdd(shouldDisplay) {
|
||||
this.showModalAdd = shouldDisplay;
|
||||
@@ -150,11 +183,13 @@ export default {
|
||||
this.$data.editingCollection = collection;
|
||||
this.$data.editingCollectionIndex = collectionIndex;
|
||||
this.displayModalEdit(true);
|
||||
this.syncCollections();
|
||||
},
|
||||
addFolder(collection, collectionIndex) {
|
||||
this.$data.editingCollection = collection;
|
||||
this.$data.editingCollectionIndex = collectionIndex;
|
||||
this.displayModalAddFolder(true);
|
||||
this.syncCollections();
|
||||
},
|
||||
editFolder(payload) {
|
||||
const { collectionIndex, folder, folderIndex } = payload;
|
||||
@@ -163,6 +198,7 @@ export default {
|
||||
this.$data.editingFolder = folder;
|
||||
this.$data.editingFolderIndex = folderIndex;
|
||||
this.displayModalEditFolder(true);
|
||||
this.syncCollections();
|
||||
},
|
||||
editRequest(payload) {
|
||||
const { request, collectionIndex, folderIndex, requestIndex } = payload;
|
||||
@@ -171,6 +207,7 @@ export default {
|
||||
this.$data.editingRequest = request;
|
||||
this.$data.editingRequestIndex = requestIndex;
|
||||
this.displayModalEditRequest(true);
|
||||
this.syncCollections();
|
||||
},
|
||||
resetSelectedData() {
|
||||
this.$data.editingCollection = undefined;
|
||||
@@ -179,7 +216,19 @@ export default {
|
||||
this.$data.editingFolderIndex = undefined;
|
||||
this.$data.editingRequest = undefined;
|
||||
this.$data.editingRequestIndex = undefined;
|
||||
},
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
<template>
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="selectRequest()" v-tooltip="'Use request'">
|
||||
<button
|
||||
class="icon"
|
||||
@click="selectRequest()"
|
||||
v-tooltip="$t('use_request')"
|
||||
>
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
<span>{{request.name}}</span>
|
||||
<span>{{ request.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('more')">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="$emit('edit-request')" v-close-popover>
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeRequest" v-tooltip="'Delete request'">
|
||||
<button class="icon" @click="removeRequest" v-close-popover>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
<button class="icon" @click="$emit('edit-request')" v-tooltip="'Edit request'">
|
||||
<i class="material-icons">edit</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Save Request As</h3>
|
||||
<h3 class="title">{{ $t("save_request_as") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
@@ -17,54 +17,94 @@
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="selectLabel">Label</label>
|
||||
<label for="selectLabel">{{ $t("label") }}</label>
|
||||
<input
|
||||
type="text"
|
||||
id="selectLabel"
|
||||
v-model="requestData.name"
|
||||
v-bind:placeholder="defaultRequestName"
|
||||
:placeholder="defaultRequestName"
|
||||
@keyup.enter="saveRequestAs"
|
||||
/>
|
||||
<label for="selectCollection">Collection</label>
|
||||
<select type="text" id="selectCollection" v-model="requestData.collectionIndex">
|
||||
<option :key="undefined" :value="undefined" hidden disabled selected>Select a Collection</option>
|
||||
<label for="selectCollection">{{ $t("collection") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
type="text"
|
||||
id="selectCollection"
|
||||
v-model="requestData.collectionIndex"
|
||||
>
|
||||
<option
|
||||
v-for="(collection, index) in $store.state.postwoman.collections"
|
||||
:key="undefined"
|
||||
:value="undefined"
|
||||
hidden
|
||||
disabled
|
||||
selected
|
||||
>{{ $t("select_collection") }}</option
|
||||
>
|
||||
<option
|
||||
v-for="(collection, index) in $store.state.postwoman
|
||||
.collections"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>{{ collection.name }}</option>
|
||||
>
|
||||
{{ collection.name }}
|
||||
</option>
|
||||
</select>
|
||||
<label for="selectFolder">Folder</label>
|
||||
<select type="text" id="selectFolder" v-model="requestData.folderIndex">
|
||||
</span>
|
||||
<label for="selectFolder">{{ $t("folder") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
type="text"
|
||||
id="selectFolder"
|
||||
v-model="requestData.folderIndex"
|
||||
>
|
||||
<option :key="undefined" :value="undefined">/</option>
|
||||
<option v-for="(folder, index) in folders" :key="index" :value="index">{{ folder.name }}</option>
|
||||
<option
|
||||
v-for="(folder, index) in folders"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>
|
||||
{{ folder.name }}
|
||||
</option>
|
||||
</select>
|
||||
<label for="selectRequest">Request</label>
|
||||
<select type="text" id="selectRequest" v-model="requestData.requestIndex">
|
||||
</span>
|
||||
<label for="selectRequest">{{ $t("request") }}</label>
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
type="text"
|
||||
id="selectRequest"
|
||||
v-model="requestData.requestIndex"
|
||||
>
|
||||
<option :key="undefined" :value="undefined">/</option>
|
||||
<option
|
||||
v-for="(folder, index) in requests"
|
||||
:key="index"
|
||||
:value="index"
|
||||
>{{ folder.name }}</option>
|
||||
>
|
||||
{{ folder.name }}
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="saveRequestAs">
|
||||
<i class="material-icons">save</i>
|
||||
<span>Save</span>
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="icon primary" @click="saveRequestAs">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import modal from "../../components/modal";
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -72,11 +112,11 @@ export default {
|
||||
editingRequest: Object
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultRequestName: "My New Request",
|
||||
defaultRequestName: "My Request",
|
||||
requestData: {
|
||||
name: undefined,
|
||||
collectionIndex: undefined,
|
||||
@@ -102,6 +142,12 @@ export default {
|
||||
this.$data.requestData.collectionIndex !== undefined;
|
||||
if (!userSelectedAnyCollection) return [];
|
||||
|
||||
const noCollectionAvailable =
|
||||
this.$store.state.postwoman.collections[
|
||||
this.$data.requestData.collectionIndex
|
||||
] !== undefined;
|
||||
if (!noCollectionAvailable) return [];
|
||||
|
||||
return this.$store.state.postwoman.collections[
|
||||
this.$data.requestData.collectionIndex
|
||||
].folders;
|
||||
@@ -124,17 +170,32 @@ export default {
|
||||
const collection = this.$store.state.postwoman.collections[
|
||||
this.$data.requestData.collectionIndex
|
||||
];
|
||||
const noCollectionAvailable =
|
||||
this.$store.state.postwoman.collections[
|
||||
this.$data.requestData.collectionIndex
|
||||
] !== undefined;
|
||||
if (!noCollectionAvailable) return [];
|
||||
|
||||
const requests = collection.requests;
|
||||
return requests;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
syncCollections() {
|
||||
if (fb.currentUser !== null) {
|
||||
if (fb.currentSettings[0].value) {
|
||||
fb.writeCollections(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections))
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
saveRequestAs() {
|
||||
const userDidntSpecifyCollection =
|
||||
this.$data.requestData.collectionIndex === undefined;
|
||||
if (userDidntSpecifyCollection) {
|
||||
this.$toast.error("Select a Collection", {
|
||||
this.$toast.error(this.$t("select_collection"), {
|
||||
icon: "error"
|
||||
});
|
||||
return;
|
||||
@@ -154,6 +215,7 @@ export default {
|
||||
});
|
||||
|
||||
this.hideModal();
|
||||
this.syncCollections();
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
|
||||
94
components/environments/addEnvironment.vue
Normal file
94
components/environments/addEnvironment.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">{{ $t("new_environment") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
:placeholder="$t('my_new_environment')"
|
||||
@keyup.enter="addNewEnvironment"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button class="icon primary" @click="addNewEnvironment">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
components: {
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
syncEnvironments() {
|
||||
if (fb.currentUser !== null) {
|
||||
if (fb.currentSettings[1].value) {
|
||||
fb.writeEnvironments(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
addNewEnvironment() {
|
||||
if (!this.$data.name) {
|
||||
this.$toast.info(this.$t("invalid_environment_name"));
|
||||
return;
|
||||
}
|
||||
let newEnvironment = [
|
||||
{
|
||||
name: this.$data.name,
|
||||
variables: []
|
||||
}
|
||||
];
|
||||
this.$store.commit("postwoman/importAddEnvironments", {
|
||||
environments: newEnvironment,
|
||||
confirmation: "Environment added"
|
||||
});
|
||||
this.$emit("hide-modal");
|
||||
this.syncEnvironments();
|
||||
},
|
||||
hideModal() {
|
||||
this.$data.name = undefined;
|
||||
this.$emit("hide-modal");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
221
components/environments/editEnvironment.vue
Normal file
221
components/environments/editEnvironment.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">{{ $t("edit_environment") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
:placeholder="editingEnvironment.name"
|
||||
@keyup.enter="saveEnvironment"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="variableList">{{ $t("env_variable_list") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="clearContent($event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
id="variableList"
|
||||
readonly
|
||||
v-textarea-auto-height="variableString"
|
||||
v-model="variableString"
|
||||
:placeholder="$t('add_one_variable')"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
v-for="(variable, index) in this.editingEnvCopy.variables"
|
||||
:key="index"
|
||||
>
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('parameter_count', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="variable.key"
|
||||
@change="
|
||||
$store.commit('postwoman/setVariableKey', {
|
||||
index,
|
||||
value: $event.target.value
|
||||
})
|
||||
"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="
|
||||
typeof variable.value === 'string'
|
||||
? variable.value
|
||||
: JSON.stringify(variable.value)
|
||||
"
|
||||
@change="
|
||||
$store.commit('postwoman/setVariableValue', {
|
||||
index,
|
||||
value: $event.target.value
|
||||
})
|
||||
"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="removeEnvironmentVariable(index)"
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
id="variable"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addEnvironmentVariable">
|
||||
<i class="material-icons">add</i>
|
||||
<span>{{ $t("add_new") }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button class="icon primary" @click="saveEnvironment">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import textareaAutoHeight from "../../directives/textareaAutoHeight";
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
textareaAutoHeight
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingEnvironment: Object,
|
||||
editingEnvironmentIndex: Number
|
||||
},
|
||||
components: {
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
editingEnvironment: function(update) {
|
||||
this.name = this.$props.editingEnvironment && this.$props.editingEnvironment.name
|
||||
? this.$props.editingEnvironment.name
|
||||
: undefined
|
||||
this.$store.commit(
|
||||
"postwoman/setEditingEnvironment",
|
||||
this.$props.editingEnvironment
|
||||
);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
editingEnvCopy() {
|
||||
return this.$store.state.postwoman.editingEnvironment;
|
||||
},
|
||||
variableString() {
|
||||
const result = this.editingEnvCopy.variables;
|
||||
return result === "" ? "" : JSON.stringify(result);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearContent(e) {
|
||||
this.$store.commit("postwoman/removeVariables", []);
|
||||
e.target.innerHTML = this.doneButton;
|
||||
this.$toast.info(this.$t("cleared"), {
|
||||
icon: "clear_all"
|
||||
});
|
||||
setTimeout(
|
||||
() => (e.target.innerHTML = '<i class="material-icons">clear_all</i>'),
|
||||
1000
|
||||
);
|
||||
},
|
||||
addEnvironmentVariable() {
|
||||
let value = { key: "", value: "" };
|
||||
this.$store.commit("postwoman/addVariable", value);
|
||||
},
|
||||
removeEnvironmentVariable(index) {
|
||||
let variableIndex = index;
|
||||
const oldVariables = this.editingEnvCopy.variables.slice();
|
||||
const newVariables = this.editingEnvCopy.variables.filter(
|
||||
(variable, index) => variableIndex !== index
|
||||
);
|
||||
|
||||
this.$store.commit("postwoman/removeVariable", newVariables);
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
action: {
|
||||
text: this.$t("undo"),
|
||||
onClick: (e, toastObject) => {
|
||||
this.$store.commit("postwoman/removeVariable", oldVariables);
|
||||
toastObject.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
saveEnvironment() {
|
||||
if (!this.$data.name) {
|
||||
this.$toast.info(this.$t("invalid_environment_name"));
|
||||
return;
|
||||
}
|
||||
const environmentUpdated = {
|
||||
...this.editingEnvCopy,
|
||||
name: this.$data.name
|
||||
};
|
||||
this.$store.commit("postwoman/saveEnvironment", {
|
||||
environment: environmentUpdated,
|
||||
environmentIndex: this.$props.editingEnvironmentIndex
|
||||
});
|
||||
this.$emit("hide-modal");
|
||||
},
|
||||
hideModal() {
|
||||
this.$data.name = undefined;
|
||||
this.$emit("hide-modal");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
65
components/environments/environment.vue
Normal file
65
components/environments/environment.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="$emit('select-environment')"
|
||||
v-tooltip="$t('use_environment')"
|
||||
>
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
<span>{{ environment.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('more')">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="$emit('edit-environment')"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">create</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeEnvironment" v-close-popover>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: flex;
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid var(--brd-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
environment: Object,
|
||||
environmentIndex: Number
|
||||
},
|
||||
methods: {
|
||||
removeEnvironment() {
|
||||
if (!confirm("Are you sure you want to remove this environment?")) return;
|
||||
this.$store.commit("postwoman/removeEnvironment", this.environmentIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
173
components/environments/importExportEnvironment.vue
Normal file
173
components/environments/importExportEnvironment.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">Import / Export Environment</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-wrap">
|
||||
<span
|
||||
v-tooltip="{
|
||||
content: !fb.currentUser
|
||||
? $t('login_first')
|
||||
: $t('replace_current')
|
||||
}"
|
||||
>
|
||||
<button
|
||||
:disabled="!fb.currentUser"
|
||||
class="icon"
|
||||
@click="syncEnvironments"
|
||||
>
|
||||
<i class="material-icons">folder_shared</i>
|
||||
<span>{{ $t("import_from_sync") }}</span>
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToReplaceWith"
|
||||
v-tooltip="$t('replace_current')"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("replace_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="replaceWithJSON"
|
||||
style="display: none;"
|
||||
ref="inputChooseFileToReplaceWith"
|
||||
accept="application/json"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="openDialogChooseFileToImportFrom"
|
||||
v-tooltip="$t('preserve_current')"
|
||||
>
|
||||
<i class="material-icons">folder_special</i>
|
||||
<span>{{ $t("import_json") }}</span>
|
||||
<input
|
||||
type="file"
|
||||
@change="importFromJSON"
|
||||
style="display: none;"
|
||||
ref="inputChooseFileToImportFrom"
|
||||
accept="application/json"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<textarea v-model="environmentJson" rows="8"></textarea>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button
|
||||
class="icon primary"
|
||||
@click="exportJSON"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
{{ $t("export") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
fb
|
||||
};
|
||||
},
|
||||
props: {
|
||||
show: Boolean
|
||||
},
|
||||
components: {
|
||||
modal: () => import("../../components/modal")
|
||||
},
|
||||
computed: {
|
||||
environmentJson() {
|
||||
return JSON.stringify(this.$store.state.postwoman.environments, null, 2);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
this.$emit("hide-modal");
|
||||
},
|
||||
openDialogChooseFileToReplaceWith() {
|
||||
this.$refs.inputChooseFileToReplaceWith.click();
|
||||
},
|
||||
openDialogChooseFileToImportFrom() {
|
||||
this.$refs.inputChooseFileToImportFrom.click();
|
||||
},
|
||||
replaceWithJSON() {
|
||||
let reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
let content = event.target.result;
|
||||
let environments = JSON.parse(content);
|
||||
this.$store.commit("postwoman/replaceEnvironments", environments);
|
||||
};
|
||||
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0]);
|
||||
this.fileImported();
|
||||
},
|
||||
importFromJSON() {
|
||||
let reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
let content = event.target.result;
|
||||
let environments = JSON.parse(content);
|
||||
let confirmation = this.$t("file_imported")
|
||||
this.$store.commit("postwoman/importAddEnvironments", {
|
||||
environments,
|
||||
confirmation
|
||||
});
|
||||
};
|
||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0]);
|
||||
},
|
||||
exportJSON() {
|
||||
let text = this.environmentJson;
|
||||
text = text.replace(/\n/g, "\r\n");
|
||||
let blob = new Blob([text], {
|
||||
type: "text/json"
|
||||
});
|
||||
let anchor = document.createElement("a");
|
||||
anchor.download = "postwoman-environment.json";
|
||||
anchor.href = window.URL.createObjectURL(blob);
|
||||
anchor.target = "_blank";
|
||||
anchor.style.display = "none";
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done"
|
||||
});
|
||||
},
|
||||
syncEnvironments() {
|
||||
this.$store.commit(
|
||||
"postwoman/replaceEnvironments",
|
||||
fb.currentEnvironments
|
||||
);
|
||||
this.fileImported();
|
||||
},
|
||||
fileImported() {
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
icon: "folder_shared"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
147
components/environments/index.vue
Normal file
147
components/environments/index.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<pw-section
|
||||
class="green"
|
||||
icon="history"
|
||||
:label="$t('environment')"
|
||||
ref="environment"
|
||||
>
|
||||
<addEnvironment :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
|
||||
<editEnvironment
|
||||
:show="showModalEdit"
|
||||
:editingEnvironment="editingEnvironment"
|
||||
:editingEnvironmentIndex="editingEnvironmentIndex"
|
||||
@hide-modal="displayModalEdit(false)"
|
||||
/>
|
||||
<importExportEnvironment
|
||||
:show="showModalImportExport"
|
||||
@hide-modal="displayModalImportExport(false)"
|
||||
/>
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="displayModalAdd(true)">
|
||||
<i class="material-icons">add</i>
|
||||
<span>{{ $t("new") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="displayModalImportExport(true)">
|
||||
{{ $t("import_export") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="environments.length === 0" class="info">
|
||||
Create new environment
|
||||
</p>
|
||||
<virtual-list
|
||||
class="virtual-list"
|
||||
:class="{ filled: environments.length }"
|
||||
:size="152"
|
||||
:remain="Math.min(5, environments.length)"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(environment, index) in environments"
|
||||
:key="environment.name"
|
||||
>
|
||||
<environment
|
||||
:environmentIndex="index"
|
||||
:environment="environment"
|
||||
@edit-environment="editEnvironment(environment, index)"
|
||||
@select-environment="$emit('use-environment', environment)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="environments.length === 0">
|
||||
<label>Environments are empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
</virtual-list>
|
||||
</pw-section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 276px);
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import environment from "./environment";
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
const updateOnLocalStorage = (propertyName, property) =>
|
||||
window.localStorage.setItem(propertyName, JSON.stringify(property));
|
||||
|
||||
export default {
|
||||
components: {
|
||||
environment,
|
||||
"pw-section": () => import("../section"),
|
||||
addEnvironment: () => import("./addEnvironment"),
|
||||
editEnvironment: () => import("./editEnvironment"),
|
||||
importExportEnvironment: () => import("./importExportEnvironment"),
|
||||
VirtualList: () => import("vue-virtual-scroll-list")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModalImportExport: false,
|
||||
showModalAdd: false,
|
||||
showModalEdit: false,
|
||||
editingEnvironment: undefined,
|
||||
editingEnvironmentIndex: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
environments() {
|
||||
return this.$store.state.postwoman.environments;
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this._keyListener = function(e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
this.showModalImportExport = false;
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", this._keyListener.bind(this));
|
||||
},
|
||||
methods: {
|
||||
displayModalAdd(shouldDisplay) {
|
||||
this.showModalAdd = shouldDisplay;
|
||||
},
|
||||
displayModalEdit(shouldDisplay) {
|
||||
this.showModalEdit = shouldDisplay;
|
||||
|
||||
if (!shouldDisplay) this.resetSelectedData();
|
||||
},
|
||||
displayModalImportExport(shouldDisplay) {
|
||||
this.showModalImportExport = shouldDisplay;
|
||||
},
|
||||
editEnvironment(environment, environmentIndex) {
|
||||
this.$data.editingEnvironment = environment;
|
||||
this.$data.editingEnvironmentIndex = environmentIndex;
|
||||
this.displayModalEdit(true);
|
||||
this.syncEnvironments;
|
||||
},
|
||||
resetSelectedData() {
|
||||
this.$data.editingEnvironment = undefined;
|
||||
this.$data.editingEnvironmentIndex = undefined;
|
||||
},
|
||||
syncEnvironments() {
|
||||
if (fb.currentUser !== null) {
|
||||
if (fb.currentSettings[1].value) {
|
||||
fb.writeEnvironments(
|
||||
JSON.parse(JSON.stringify(this.$store.state.postwoman.environments))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
78
components/firebase/feeds.vue
Normal file
78
components/firebase/feeds.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<virtual-list
|
||||
v-if="fb.currentFeeds.length !== 0"
|
||||
class="virtual-list"
|
||||
:class="{ filled: fb.currentFeeds.length }"
|
||||
:size="90"
|
||||
:remain="Math.min(5, fb.currentFeeds.length)"
|
||||
>
|
||||
<ul v-for="feed in fb.currentFeeds" :key="feed.id" class="entry">
|
||||
<div class="show-on-large-screen">
|
||||
<li class="info">
|
||||
<label>
|
||||
{{ feed.label || $t("no_label") }}
|
||||
</label>
|
||||
</li>
|
||||
<button class="icon" @click="deleteFeed(feed)">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="show-on-large-screen">
|
||||
<li class="info clamb-3">
|
||||
<label>{{ feed.message || $t("empty") }}</label>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</virtual-list>
|
||||
<ul v-else>
|
||||
<li>
|
||||
<label class="info">{{ $t("empty") }}</label>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 288px);
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.entry {
|
||||
border-bottom: 1px dashed var(--brd-color);
|
||||
padding: 0 0 8px;
|
||||
}
|
||||
|
||||
.clamb-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VirtualList: () => import("vue-virtual-scroll-list")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fb
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
deleteFeed(feed) {
|
||||
fb.deleteFeed(feed.id);
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
69
components/firebase/inputform.vue
Normal file
69
components/firebase/inputform.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul>
|
||||
<div class="show-on-large-screen">
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('label')"
|
||||
type="text"
|
||||
autofocus
|
||||
v-model="message"
|
||||
:placeholder="$t('paste_a_note')"
|
||||
@keyup.enter="formPost"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
<div class="show-on-large-screen">
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('label')"
|
||||
type="text"
|
||||
autofocus
|
||||
v-model="label"
|
||||
:placeholder="$t('label')"
|
||||
@keyup.enter="formPost"
|
||||
/>
|
||||
</li>
|
||||
<button
|
||||
class="icon"
|
||||
:disabled="!(this.message || this.label)"
|
||||
value="Save"
|
||||
@click="formPost"
|
||||
>
|
||||
<i class="material-icons">add</i>
|
||||
<span>Add</span>
|
||||
</button>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
ul,
|
||||
ol {
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
message: null,
|
||||
label: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
formPost() {
|
||||
if (!(this.message || this.label)) {
|
||||
return;
|
||||
}
|
||||
fb.writeFeeds(this.message, this.label);
|
||||
this.message = null;
|
||||
this.label = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
115
components/firebase/login.vue
Normal file
115
components/firebase/login.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<v-popover>
|
||||
<button class="icon" v-tooltip="$t('login_with')">
|
||||
<i class="material-icons">vpn_key</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="signInWithGoogle" v-close-popover>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Google</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="signInWithGithub" v-close-popover>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||
/>
|
||||
</svg>
|
||||
<span>GitHub</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import firebase from "firebase/app";
|
||||
import { fb } from "../../functions/fb";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
fb
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
signInWithGoogle() {
|
||||
const provider = new firebase.auth.GoogleAuthProvider();
|
||||
firebase
|
||||
.auth()
|
||||
.signInWithPopup(provider)
|
||||
.then(({ additionalUserInfo }) => {
|
||||
if (additionalUserInfo.isNewUser) {
|
||||
this.$toast.info(`${this.$t("turn_on")} ${this.$t("sync")}`, {
|
||||
icon: "sync",
|
||||
duration: null,
|
||||
closeOnSwipe: false,
|
||||
action: {
|
||||
text: this.$t("yes"),
|
||||
onClick: (e, toastObject) => {
|
||||
fb.writeSettings("syncHistory", false);
|
||||
fb.writeSettings("syncCollections", true);
|
||||
this.$router.push({ path: "/settings" });
|
||||
toastObject.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.$toast.show(err.message || err, {
|
||||
icon: "error"
|
||||
});
|
||||
});
|
||||
},
|
||||
signInWithGithub() {
|
||||
const provider = new firebase.auth.GithubAuthProvider();
|
||||
firebase
|
||||
.auth()
|
||||
.signInWithPopup(provider)
|
||||
.then(({ additionalUserInfo }) => {
|
||||
if (additionalUserInfo.isNewUser) {
|
||||
this.$toast.info(`${this.$t("turn_on")} ${this.$t("sync")}`, {
|
||||
icon: "sync",
|
||||
duration: null,
|
||||
closeOnSwipe: false,
|
||||
action: {
|
||||
text: this.$t("yes"),
|
||||
onClick: (e, toastObject) => {
|
||||
fb.writeSettings("syncHistory", false);
|
||||
fb.writeSettings("syncCollections", true);
|
||||
this.$router.push({ path: "/settings" });
|
||||
toastObject.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.$toast.show(err.message || err, {
|
||||
icon: "error"
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
38
components/graphql/argument.vue
Normal file
38
components/graphql/argument.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<span>
|
||||
<span class="argumentName">
|
||||
{{ argName }}
|
||||
</span>
|
||||
:
|
||||
<typelink :type="argType" :jumpTypeCallback="jumpCallback" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
<script>
|
||||
import typelink from "./typelink";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
typelink: typelink
|
||||
},
|
||||
|
||||
props: {
|
||||
gqlArg: Object
|
||||
},
|
||||
|
||||
computed: {
|
||||
argName() {
|
||||
return this.gqlArg.name;
|
||||
},
|
||||
argType() {
|
||||
return this.gqlArg.type;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
jumpCallback(typeName) {}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
93
components/graphql/field.vue
Normal file
93
components/graphql/field.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="field-box">
|
||||
<div class="field-title">
|
||||
{{ fieldName }}
|
||||
<span v-if="fieldArgs.length > 0">
|
||||
(
|
||||
<span v-for="(field, index) in fieldArgs" :key="index">
|
||||
{{ field.name }}:
|
||||
<typelink
|
||||
:gqlType="field.type"
|
||||
:jumpTypeCallback="jumpTypeCallback"
|
||||
/>
|
||||
<span v-if="index !== fieldArgs.length - 1">
|
||||
,
|
||||
</span>
|
||||
</span>
|
||||
) </span
|
||||
>:
|
||||
<typelink :gqlType="gqlField.type" :jumpTypeCallback="jumpTypeCallback" />
|
||||
</div>
|
||||
<div class="field-desc" v-if="gqlField.description">
|
||||
{{ gqlField.description }}
|
||||
</div>
|
||||
|
||||
<div class="field-deprecated" v-if="gqlField.isDeprecated">
|
||||
{{ $t("deprecated") }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.field-box {
|
||||
padding: 16px;
|
||||
margin: 4px;
|
||||
border-bottom: 1px dashed var(--brd-color);
|
||||
}
|
||||
|
||||
.field-deprecated {
|
||||
background-color: yellow;
|
||||
color: black;
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
margin: 4px 0;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.field-desc {
|
||||
color: var(--fg-light-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import typelink from "./typelink";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
typelink: typelink
|
||||
},
|
||||
|
||||
props: {
|
||||
gqlField: Object,
|
||||
jumpTypeCallback: Function
|
||||
},
|
||||
|
||||
computed: {
|
||||
fieldString() {
|
||||
const args = (this.gqlField.args || []).reduce((acc, arg, index) => {
|
||||
return (
|
||||
acc +
|
||||
`${arg.name}: ${arg.type.toString()}${
|
||||
index !== this.gqlField.args.length - 1 ? ", " : ""
|
||||
}`
|
||||
);
|
||||
}, "");
|
||||
const argsString = args.length > 0 ? `(${args})` : "";
|
||||
return `${
|
||||
this.gqlField.name
|
||||
}${argsString}: ${this.gqlField.type.toString()}`;
|
||||
},
|
||||
|
||||
fieldName() {
|
||||
return this.gqlField.name;
|
||||
},
|
||||
|
||||
fieldArgs() {
|
||||
return this.gqlField.args || [];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
173
components/graphql/queryeditor.vue
Normal file
173
components/graphql/queryeditor.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<pre ref="editor"></pre>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const DEFAULT_THEME = "twilight";
|
||||
|
||||
import ace from "ace-builds";
|
||||
import * as gql from "graphql";
|
||||
import { getAutocompleteSuggestions } from "graphql-language-service-interface";
|
||||
import "ace-builds/webpack-resolver";
|
||||
import "ace-builds/src-noconflict/ext-language_tools";
|
||||
import debounce from "../../functions/utils/debounce";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
lang: {
|
||||
type: String,
|
||||
default: "json"
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
cacheValue: "",
|
||||
validationSchema: null
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(value) {
|
||||
if (value !== this.cacheValue) {
|
||||
this.editor.session.setValue(value, 1);
|
||||
this.cacheValue = value;
|
||||
}
|
||||
},
|
||||
theme() {
|
||||
this.editor.setTheme(`ace/theme/${this.defineTheme()}`);
|
||||
},
|
||||
lang(value) {
|
||||
this.editor.getSession().setMode(`ace/mode/${value}`);
|
||||
},
|
||||
options(value) {
|
||||
this.editor.setOptions(value);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let langTools = ace.require("ace/ext/language_tools");
|
||||
|
||||
const editor = ace.edit(this.$refs.editor, {
|
||||
theme: `ace/theme/${this.defineTheme()}`,
|
||||
mode: `ace/mode/${this.lang}`,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true,
|
||||
...this.options
|
||||
});
|
||||
|
||||
const completer = {
|
||||
getCompletions: (
|
||||
editor,
|
||||
_session,
|
||||
{ row, column },
|
||||
_prefix,
|
||||
callback
|
||||
) => {
|
||||
if (this.validationSchema) {
|
||||
const completions = getAutocompleteSuggestions(
|
||||
this.validationSchema,
|
||||
editor.getValue(),
|
||||
{ line: row, character: column }
|
||||
);
|
||||
|
||||
callback(
|
||||
null,
|
||||
completions.map(({ label, detail }) => ({
|
||||
name: label,
|
||||
value: label,
|
||||
score: 1.0,
|
||||
meta: detail
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
callback(null, []);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
langTools.setCompleters([completer]);
|
||||
|
||||
if (this.value) editor.setValue(this.value, 1);
|
||||
|
||||
this.editor = editor;
|
||||
this.cacheValue = this.value;
|
||||
|
||||
editor.on("change", () => {
|
||||
const content = editor.getValue();
|
||||
this.$emit("input", content);
|
||||
this.parseContents(content);
|
||||
this.cacheValue = content;
|
||||
});
|
||||
|
||||
this.parseContents(this.value);
|
||||
},
|
||||
|
||||
methods: {
|
||||
defineTheme() {
|
||||
if (this.theme) {
|
||||
return this.theme;
|
||||
} else {
|
||||
return (
|
||||
this.$store.state.postwoman.settings.THEME_ACE_EDITOR || DEFAULT_THEME
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
setValidationSchema(schema) {
|
||||
this.validationSchema = schema;
|
||||
this.parseContents(this.cacheValue);
|
||||
},
|
||||
|
||||
parseContents: debounce(function(content) {
|
||||
if (content !== "") {
|
||||
try {
|
||||
const doc = gql.parse(content);
|
||||
|
||||
if (this.validationSchema) {
|
||||
this.editor.session.setAnnotations(
|
||||
gql
|
||||
.validate(this.validationSchema, doc)
|
||||
.map(({ locations, message }) => ({
|
||||
row: locations[0].line - 1,
|
||||
column: locations[0].column - 1,
|
||||
text: message,
|
||||
type: "error"
|
||||
}))
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
this.editor.session.setAnnotations([
|
||||
{
|
||||
row: e.locations[0].line - 1,
|
||||
column: e.locations[0].column - 1,
|
||||
text: e.message,
|
||||
type: "error"
|
||||
}
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
this.editor.session.setAnnotations([]);
|
||||
}
|
||||
}, 2000)
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy();
|
||||
this.editor.container.remove();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
44
components/graphql/type.vue
Normal file
44
components/graphql/type.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="type-box">
|
||||
<div class="type-title">{{ gqlType.name }}</div>
|
||||
<div class="type-desc" v-if="gqlType.description">
|
||||
{{ gqlType.description }}
|
||||
</div>
|
||||
|
||||
<div v-if="gqlType.getFields">
|
||||
<h5>{{ $t("fields") }}</h5>
|
||||
<div v-for="field in gqlType.getFields()" :key="field.name">
|
||||
<gql-field :gqlField="field" :jumpTypeCallback="jumpTypeCallback" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.type-box {
|
||||
padding: 16px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.type-desc {
|
||||
color: var(--fg-light-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.type-title {
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {
|
||||
"gql-field": () => import("./field")
|
||||
},
|
||||
|
||||
props: {
|
||||
gqlType: {},
|
||||
jumpTypeCallback: Function
|
||||
}
|
||||
};
|
||||
</script>
|
||||
34
components/graphql/typelink.vue
Normal file
34
components/graphql/typelink.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<span class="typelink" @click="jumpToType">{{ typeString }}</span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.typelink {
|
||||
color: var(--ac-color);
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
gqlType: null,
|
||||
// (typeName: string) => void
|
||||
jumpTypeCallback: Function
|
||||
},
|
||||
|
||||
computed: {
|
||||
typeString() {
|
||||
return this.gqlType.toString();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
jumpToType() {
|
||||
this.jumpTypeCallback(this.gqlType);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,137 +1,179 @@
|
||||
<template>
|
||||
<pw-section class="green" icon="history" label="History" ref="history">
|
||||
<pw-section class="green" icon="history" :label="$t('history')" ref="history">
|
||||
<ul>
|
||||
<div class="show-on-large-screen">
|
||||
<li id="filter-history">
|
||||
<input
|
||||
aria-label="Search"
|
||||
type="text"
|
||||
placeholder="search history"
|
||||
:readonly="history.length === 0"
|
||||
type="search"
|
||||
:placeholder="$t('search')"
|
||||
v-model="filterText"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li></li>
|
||||
<li @click="sort_by_label()">
|
||||
<label>
|
||||
Label
|
||||
</label>
|
||||
</li>
|
||||
<li @click="sort_by_time()">
|
||||
<label>
|
||||
Time
|
||||
</label>
|
||||
</li>
|
||||
<li @click="sort_by_status_code()">
|
||||
<label>
|
||||
Status
|
||||
</label>
|
||||
</li>
|
||||
<li @click="sort_by_url()">
|
||||
<label>
|
||||
URL
|
||||
</label>
|
||||
</li>
|
||||
<li @click="sort_by_path()">
|
||||
<label>
|
||||
Path
|
||||
</label>
|
||||
</li>
|
||||
<li></li>
|
||||
<button class="icon">
|
||||
<i class="material-icons">search</i>
|
||||
</button>
|
||||
</div>
|
||||
</ul>
|
||||
<virtual-list
|
||||
class="virtual-list"
|
||||
:class="{filled: filteredHistory.length}"
|
||||
:size="54"
|
||||
:class="{ filled: filteredHistory.length }"
|
||||
:size="185"
|
||||
:remain="Math.min(5, filteredHistory.length)"
|
||||
>
|
||||
<ul v-for="(entry, index) in filteredHistory" :key="index" class="entry">
|
||||
<li>
|
||||
<button v-if="entry.usesScripts"
|
||||
v-tooltip="'This entry used pre-request scripts'"
|
||||
<div class="show-on-large-screen">
|
||||
<button
|
||||
class="icon"
|
||||
:class="{ stared: entry.star }"
|
||||
@click="toggleStar(entry)"
|
||||
v-tooltip="{
|
||||
content: !entry.star ? $t('add_star') : $t('remove_star')
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">code</i>
|
||||
<i class="material-icons">
|
||||
{{ entry.star ? "star" : "star_border" }}
|
||||
</i>
|
||||
</button>
|
||||
<button v-else
|
||||
v-tooltip="'No pre-request scripts'"
|
||||
class="icon"
|
||||
>
|
||||
<i class="material-icons">http</i>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
aria-label="Label"
|
||||
:aria-label="$t('label')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.label"
|
||||
placeholder="No label"
|
||||
:placeholder="$t('no_label')"
|
||||
class="bg-color"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input aria-label="Time" type="text" readonly :value="entry.time" v-tooltip="entry.date" />
|
||||
</li>
|
||||
<!-- <li>
|
||||
<button
|
||||
class="icon"
|
||||
v-tooltip="{
|
||||
content: !entry.usesScripts
|
||||
? 'No pre-request script'
|
||||
: 'Used pre-request script'
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !entry.usesScripts ? "http" : "code" }}
|
||||
</i>
|
||||
</button>
|
||||
</li> -->
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('options')">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
:id="'use-button#' + index"
|
||||
@click="useHistory(entry)"
|
||||
:aria-label="$t('edit')"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">restore</i>
|
||||
<span>{{ $t("restore") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
:id="'delete-button#' + index"
|
||||
@click="deleteHistory(entry)"
|
||||
:aria-label="$t('delete')"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
<div class="show-on-large-screen">
|
||||
<li class="method-list-item">
|
||||
<input
|
||||
aria-label="Method"
|
||||
:aria-label="$t('method')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.method"
|
||||
:class="findEntryStatus(entry).className"
|
||||
:style="{'--status-code': entry.status}"
|
||||
:style="{ '--status-code': entry.status }"
|
||||
/>
|
||||
<span
|
||||
class="entry-status-code"
|
||||
:class="findEntryStatus(entry).className"
|
||||
:style="{'--status-code': entry.status}"
|
||||
>{{entry.status}}</span>
|
||||
</li>
|
||||
<li>
|
||||
<input aria-label="URL" type="text" readonly :value="entry.url" />
|
||||
</li>
|
||||
<li>
|
||||
<input aria-label="Path" type="text" readonly :value="entry.path" placeholder="No path" />
|
||||
</li>
|
||||
<div class="show-on-small-screen">
|
||||
<li>
|
||||
<button
|
||||
v-tooltip="'Delete entry'"
|
||||
class="icon"
|
||||
:id="'delete-button#'+index"
|
||||
@click="deleteHistory(entry)"
|
||||
aria-label="Delete"
|
||||
:style="{ '--status-code': entry.status }"
|
||||
>{{ entry.status }}</span
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
v-tooltip="'Edit entry'"
|
||||
class="icon"
|
||||
:id="'use-button#'+index"
|
||||
@click="useHistory(entry)"
|
||||
aria-label="Edit"
|
||||
>
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
<div class="show-on-large-screen">
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('url')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.url"
|
||||
:placeholder="$t('no_url')"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('path')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.path"
|
||||
:placeholder="$t('no_path')"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div v-if="showMore" class="show-on-large-screen">
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('time')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.time"
|
||||
v-tooltip="entry.date"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('duration')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.duration"
|
||||
:placeholder="$t('no_duration')"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('prerequest_script')"
|
||||
type="text"
|
||||
readonly
|
||||
:value="entry.preRequestScript"
|
||||
:placeholder="$t('no_prerequest_script')"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
</transition>
|
||||
</ul>
|
||||
</virtual-list>
|
||||
<ul :class="{hidden: filteredHistory.length != 0 || history.length === 0 }">
|
||||
<ul
|
||||
:class="{ hidden: filteredHistory.length != 0 || history.length === 0 }"
|
||||
>
|
||||
<li>
|
||||
<label>Nothing found "{{filterText}}"</label>
|
||||
<label>{{ $t("nothing_found") }} "{{ filterText }}"</label>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="history.length === 0">
|
||||
<li>
|
||||
<label>History is empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="history.length !== 0">
|
||||
<li v-if="!isClearingHistory">
|
||||
<p v-if="history.length === 0" class="info">
|
||||
{{ $t("history_empty") }}
|
||||
</p>
|
||||
<div v-if="history.length !== 0">
|
||||
<div class="flex-wrap" v-if="!isClearingHistory">
|
||||
<button
|
||||
class="icon"
|
||||
id="clear-history-button"
|
||||
@@ -139,62 +181,168 @@
|
||||
@click="enableHistoryClearing"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
<span>Clear All</span>
|
||||
<span>{{ $t("clear_all") }}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li v-else>
|
||||
<div class="flex-wrap">
|
||||
<label for="clear-history-button">Are you sure?</label>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('sort')">
|
||||
<i class="material-icons">sort</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" id="confirm-clear-history-button" @click="clearHistory">Yes</button>
|
||||
<button class="icon" id="reject-clear-history-button" @click="disableHistoryClearing">No</button>
|
||||
<button class="icon" @click="sort_by_label()" v-close-popover>
|
||||
<i class="material-icons">sort_by_alpha</i>
|
||||
<span>{{ $t("label") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="sort_by_time()" v-close-popover>
|
||||
<i class="material-icons">access_time</i>
|
||||
<span>{{ $t("time") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="sort_by_status_code()"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">assistant</i>
|
||||
<span>{{ $t("status") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="sort_by_url()" v-close-popover>
|
||||
<i class="material-icons">language</i>
|
||||
<span>{{ $t("url") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="sort_by_path()" v-close-popover>
|
||||
<i class="material-icons">timeline</i>
|
||||
<span>{{ $t("path") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="showMore">
|
||||
<button class="icon" @click="sort_by_duration()" v-close-popover>
|
||||
<i class="material-icons">timer</i>
|
||||
<span>{{ $t("duration") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="toggleCollapse()">
|
||||
<i class="material-icons">
|
||||
{{ !showMore ? "first_page" : "last_page" }}
|
||||
</i>
|
||||
<span>{{ !showMore ? $t("show_more") : $t("hide_more") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
<div class="flex-wrap" v-else>
|
||||
<label for="clear-history-button" class="info">
|
||||
{{ $t("are_you_sure") }}
|
||||
</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
id="confirm-clear-history-button"
|
||||
@click="clearHistory"
|
||||
v-tooltip="$t('yes')"
|
||||
>
|
||||
<i class="material-icons">done</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
id="reject-clear-history-button"
|
||||
@click="disableHistoryClearing"
|
||||
v-tooltip="$t('no')"
|
||||
>
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 284px);
|
||||
|
||||
[readonly] {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: var(--fg-color);
|
||||
}
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.stared {
|
||||
color: #f8e81c !important;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.method-list-item {
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-weight: 400;
|
||||
background-color: transparent;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.entry {
|
||||
border-bottom: 1px dashed var(--brd-color);
|
||||
padding: 0 0 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.virtual-list.filled {
|
||||
min-height: 200px;
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
.labels {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import VirtualList from "vue-virtual-scroll-list";
|
||||
import section from "./section";
|
||||
import { findStatusGroup } from "../pages/index";
|
||||
import { findStatusGroup } from "../pages/index";
|
||||
import { fb } from "../functions/fb";
|
||||
|
||||
const updateOnLocalStorage = (propertyName, property) =>
|
||||
const updateOnLocalStorage = (propertyName, property) =>
|
||||
window.localStorage.setItem(propertyName, JSON.stringify(property));
|
||||
export default {
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": section,
|
||||
VirtualList
|
||||
"pw-section": () => import("./section"),
|
||||
VirtualList: () => import("vue-virtual-scroll-list")
|
||||
},
|
||||
data() {
|
||||
const localStorageHistory = JSON.parse(
|
||||
window.localStorage.getItem("history")
|
||||
);
|
||||
return {
|
||||
history: localStorageHistory || [],
|
||||
history:
|
||||
fb.currentUser !== null
|
||||
? fb.currentHistory
|
||||
: JSON.parse(window.localStorage.getItem("history")) || [],
|
||||
filterText: "",
|
||||
showFilter: false,
|
||||
isClearingHistory: false,
|
||||
@@ -202,11 +350,16 @@
|
||||
reverse_sort_time: false,
|
||||
reverse_sort_status_code: false,
|
||||
reverse_sort_url: false,
|
||||
reverse_sort_path: false
|
||||
reverse_sort_path: false,
|
||||
showMore: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filteredHistory() {
|
||||
this.history =
|
||||
fb.currentUser !== null
|
||||
? fb.currentHistory
|
||||
: JSON.parse(window.localStorage.getItem("history")) || [];
|
||||
return this.history.filter(entry => {
|
||||
const filterText = this.filterText.toLowerCase();
|
||||
return Object.keys(entry).some(key => {
|
||||
@@ -219,11 +372,14 @@
|
||||
},
|
||||
methods: {
|
||||
clearHistory() {
|
||||
if (fb.currentUser !== null) {
|
||||
fb.clearHistory();
|
||||
}
|
||||
this.history = [];
|
||||
this.filterText = "";
|
||||
this.disableHistoryClearing();
|
||||
updateOnLocalStorage("history", this.history);
|
||||
this.$toast.error("History Deleted", {
|
||||
this.$toast.error(this.$t("history_deleted"), {
|
||||
icon: "delete"
|
||||
});
|
||||
},
|
||||
@@ -239,12 +395,15 @@
|
||||
);
|
||||
},
|
||||
deleteHistory(entry) {
|
||||
if (fb.currentUser !== null) {
|
||||
fb.deleteHistory(entry);
|
||||
}
|
||||
this.history.splice(this.history.indexOf(entry), 1);
|
||||
if (this.history.length === 0) {
|
||||
this.filterText = "";
|
||||
}
|
||||
updateOnLocalStorage("history", this.history);
|
||||
this.$toast.error("Deleted", {
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete"
|
||||
});
|
||||
},
|
||||
@@ -301,8 +460,8 @@
|
||||
let byUrl = this.history.slice(0);
|
||||
byUrl.sort((a, b) => {
|
||||
if (this.reverse_sort_url)
|
||||
return a.url == b.url ? 0 : +(a.url < b.url) || -1;
|
||||
else return a.url == b.url ? 0 : +(a.url > b.url) || -1;
|
||||
return a.url === b.url ? 0 : +(a.url < b.url) || -1;
|
||||
else return a.url === b.url ? 0 : +(a.url > b.url) || -1;
|
||||
});
|
||||
this.history = byUrl;
|
||||
this.reverse_sort_url = !this.reverse_sort_url;
|
||||
@@ -311,8 +470,8 @@
|
||||
let byLabel = this.history.slice(0);
|
||||
byLabel.sort((a, b) => {
|
||||
if (this.reverse_sort_label)
|
||||
return a.label == b.label ? 0 : +(a.label < b.label) || -1;
|
||||
else return a.label == b.label ? 0 : +(a.label > b.label) || -1;
|
||||
return a.label === b.label ? 0 : +(a.label < b.label) || -1;
|
||||
else return a.label === b.label ? 0 : +(a.label > b.label) || -1;
|
||||
});
|
||||
this.history = byLabel;
|
||||
this.reverse_sort_label = !this.reverse_sort_label;
|
||||
@@ -321,12 +480,37 @@
|
||||
let byPath = this.history.slice(0);
|
||||
byPath.sort((a, b) => {
|
||||
if (this.reverse_sort_path)
|
||||
return a.path == b.path ? 0 : +(a.path < b.path) || -1;
|
||||
else return a.path == b.path ? 0 : +(a.path > b.path) || -1;
|
||||
return a.path === b.path ? 0 : +(a.path < b.path) || -1;
|
||||
else return a.path === b.path ? 0 : +(a.path > b.path) || -1;
|
||||
});
|
||||
this.history = byPath;
|
||||
this.reverse_sort_path = !this.reverse_sort_path;
|
||||
},
|
||||
sort_by_duration() {
|
||||
let byDuration = this.history.slice(0);
|
||||
byDuration.sort((a, b) => {
|
||||
if (this.reverse_sort_duration)
|
||||
return a.duration === b.duration
|
||||
? 0
|
||||
: +(a.duration < b.duration) || -1;
|
||||
else
|
||||
return a.duration === b.duration
|
||||
? 0
|
||||
: +(a.duration > b.duration) || -1;
|
||||
});
|
||||
this.history = byDuration;
|
||||
this.reverse_sort_duration = !this.reverse_sort_duration;
|
||||
},
|
||||
toggleCollapse() {
|
||||
this.showMore = !this.showMore;
|
||||
},
|
||||
toggleStar(entry) {
|
||||
if (fb.currentUser !== null) {
|
||||
fb.toggleStar(entry, !entry.star);
|
||||
}
|
||||
entry.star = !entry.star;
|
||||
updateOnLocalStorage("history", this.history);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
<path
|
||||
:fill="color"
|
||||
id="path3816"
|
||||
data-old_color="#121212"
|
||||
data-old_color="#202124"
|
||||
class="active-path"
|
||||
data-original="#121212"
|
||||
data-original="#202124"
|
||||
d="M 64.601,236.822 C 64.601,394.256 192.786,612 306.001,612 412.582,612 547.4,394.256 547.4,236.822 547.4,79.388 439.322,0 306,0 172.678,0 64.601,79.388 64.601,236.822 Z m 304.12,116.415 c 29.475,-29.475 70.598,-40.195 108.552,-32.173 8.021,37.954 -2.698,79.077 -32.173,108.552 -29.475,29.475 -70.598,40.195 -108.552,32.173 -8.022,-37.955 2.698,-79.078 32.173,-108.552 z M 134.727,321.063 c 37.954,-8.021 79.077,2.698 108.552,32.173 29.475,29.475 40.195,70.598 32.173,108.552 -37.954,8.021 -79.077,-2.698 -108.552,-32.173 -29.475,-29.476 -40.194,-70.598 -32.173,-108.552 z"
|
||||
/>
|
||||
</g>
|
||||
@@ -35,7 +35,7 @@
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
<style scoped lang="scss">
|
||||
#circle3814 {
|
||||
/* fill: var(--fg-color); */
|
||||
fill: transparent;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<transition name="modal-fade">
|
||||
<transition name="modal" appear>
|
||||
<div class="modal-backdrop">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-container">
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
z-index: 998;
|
||||
@@ -25,7 +25,7 @@
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-color: rgba(0, 0, 0, 0.32);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -37,19 +37,24 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
max-width: 800px;
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
margin: 8px;
|
||||
padding: 12px;
|
||||
margin: 16px;
|
||||
padding: 16px;
|
||||
transition: all 0.2s ease;
|
||||
background-color: var(--bg-color);
|
||||
border-radius: 8px;
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0px 16px 70px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0px 16px 70px rgba(0, 0, 0, 0.5);
|
||||
max-height: calc(100vh - 32px);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -61,13 +66,13 @@
|
||||
* these styles.
|
||||
*/
|
||||
|
||||
.modal-fade-enter,
|
||||
.modal-fade-leave-active {
|
||||
.modal-enter,
|
||||
.modal-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-fade-enter .modal-container,
|
||||
.modal-fade-leave-active .modal-container {
|
||||
.modal-enter .modal-container,
|
||||
.modal-leave-active .modal-container {
|
||||
transform: scale(0.8);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<fieldset :id="label.toLowerCase()" :class="{ 'no-colored-frames': !frameColorsEnabled }">
|
||||
<fieldset
|
||||
:id="label.toLowerCase()"
|
||||
:class="{ 'no-colored-frames': !frameColorsEnabled }"
|
||||
>
|
||||
<legend @click.prevent="collapse">
|
||||
<span>{{ label }}</span>
|
||||
<i class="material-icons" v-if="isCollapsed">expand_more</i>
|
||||
<i class="material-icons" v-if="!isCollapsed">expand_less</i>
|
||||
<i class="material-icons">
|
||||
{{ isCollapsed ? "expand_more" : "expand_less" }}
|
||||
</i>
|
||||
</legend>
|
||||
<div class="collapsible" :class="{ hidden: collapsed }">
|
||||
<slot />
|
||||
@@ -11,7 +15,7 @@
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
<style scoped lang="scss">
|
||||
fieldset.no-colored-frames legend {
|
||||
color: var(--fg-color);
|
||||
}
|
||||
|
||||
@@ -1,50 +1,41 @@
|
||||
<template>
|
||||
<div class="color" :data-color="color">
|
||||
<span :style="{backgroundColor: color}" class="preview">
|
||||
<div
|
||||
class="color"
|
||||
:data-color="color"
|
||||
:class="{ active: active }"
|
||||
v-tooltip="{ content: name || color }"
|
||||
:style="{ backgroundColor: color }"
|
||||
>
|
||||
<i v-if="active" class="material-icons activeTick">done</i>
|
||||
</span>
|
||||
{{ name || color }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
<style scoped lang="scss">
|
||||
.color {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 16px 0 4px;
|
||||
margin: 4px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
border-radius: 20px;
|
||||
margin: 8px;
|
||||
padding: 16px;
|
||||
border-radius: 100%;
|
||||
border: 3px solid var(--bg-dark-color);
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&.active {
|
||||
background-color: var(--bg-dark-color);
|
||||
&.fg {
|
||||
color: var(--act-color);
|
||||
}
|
||||
|
||||
.preview {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
border-radius: 100%;
|
||||
margin-right: 8px;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
&.active {
|
||||
border: 3px solid var(--ac-color);
|
||||
}
|
||||
|
||||
&.fg.active {
|
||||
border: 3px solid var(--fg-color);
|
||||
}
|
||||
|
||||
.activeTick {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.color.vibrant {
|
||||
.preview .activeTick {
|
||||
color: var(--act-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div @click="toggle()">
|
||||
<label class="toggle" :class="{on: on}" ref="toggle">
|
||||
<label class="toggle" :class="{ on: on }" ref="toggle">
|
||||
<span class="handle"></span>
|
||||
</label>
|
||||
<label class="caption">
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style scoped lang="scss">
|
||||
$useBorder: false;
|
||||
$borderColor: var(--fg-light-color);
|
||||
$activeColor: var(--ac-color);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
|
||||
"rules": {
|
||||
".read": false,
|
||||
".write": false
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export default {
|
||||
name: "textareaAutoHeight",
|
||||
update(element) {
|
||||
if (element.scrollHeight !== element.clientHeight) {
|
||||
element.style.minHeight = `${element.scrollHeight}px`;
|
||||
update({ scrollHeight, clientHeight, style }) {
|
||||
if (scrollHeight !== clientHeight) {
|
||||
style.minHeight = `${scrollHeight}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ services:
|
||||
- "./plugins:/app/plugins"
|
||||
- "./static:/app/static"
|
||||
- "./store:/app/store"
|
||||
- "./components:/app/components"
|
||||
ports:
|
||||
- "3000:3000"
|
||||
command: "npm run dev"
|
||||
|
||||
15
docs/index.html
Normal file
15
docs/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Postwoman</title>
|
||||
<meta http-equiv="refresh" content="0; url=https://postwoman.io" />
|
||||
<link rel="canonical" href="https://postwoman.io" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
Redirecting to postwoman.io
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,14 @@
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents {
|
||||
match /{document=**} {
|
||||
allow read, write;
|
||||
allow read, write: if request.auth.uid != null;
|
||||
}
|
||||
// Make sure the uid of the requesting user matches name of the user
|
||||
// document. The wildcard expression {userId} makes the userId variable
|
||||
// available in rules.
|
||||
match /users/{userId} {
|
||||
allow read, update, delete: if request.auth.uid == userId;
|
||||
allow create: if request.auth.uid != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
functions/editorutils.js
Normal file
11
functions/editorutils.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const mimeToMode = {
|
||||
"text/plain": "plain_text",
|
||||
"text/html": "html",
|
||||
"application/xml": "xml",
|
||||
"application/hal+json": "json",
|
||||
"application/json": "json"
|
||||
}
|
||||
|
||||
export function getEditorLangForMimeType(mimeType) {
|
||||
return mimeToMode[mimeType] || "plain_text";
|
||||
}
|
||||
221
functions/fb.js
Normal file
221
functions/fb.js
Normal file
@@ -0,0 +1,221 @@
|
||||
import firebase from "firebase/app";
|
||||
import "firebase/firestore";
|
||||
import "firebase/auth";
|
||||
|
||||
// Initialize Firebase, copied from cloud console
|
||||
const firebaseConfig = {
|
||||
apiKey: "AIzaSyCMsFreESs58-hRxTtiqQrIcimh4i1wbsM",
|
||||
authDomain: "postwoman-api.firebaseapp.com",
|
||||
databaseURL: "https://postwoman-api.firebaseio.com",
|
||||
projectId: "postwoman-api",
|
||||
storageBucket: "postwoman-api.appspot.com",
|
||||
messagingSenderId: "421993993223",
|
||||
appId: "1:421993993223:web:ec0baa8ee8c02ffa1fc6a2",
|
||||
measurementId: "G-ERJ6025CEB"
|
||||
};
|
||||
firebase.initializeApp(firebaseConfig);
|
||||
|
||||
// a reference to the users collection
|
||||
const usersCollection = firebase.firestore().collection("users");
|
||||
|
||||
// the shared state object that any vue component
|
||||
// can get access to
|
||||
export const fb = {
|
||||
currentUser: {},
|
||||
currentFeeds: [],
|
||||
currentSettings: [],
|
||||
currentHistory: [],
|
||||
currentCollections: [],
|
||||
currentEnvironments: [],
|
||||
writeFeeds: async (message, label) => {
|
||||
const dt = {
|
||||
createdOn: new Date(),
|
||||
author: fb.currentUser.uid,
|
||||
author_name: fb.currentUser.displayName,
|
||||
author_image: fb.currentUser.photoURL,
|
||||
message,
|
||||
label
|
||||
};
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("feeds")
|
||||
.add(dt)
|
||||
.catch(e => console.error("error inserting", dt, e));
|
||||
},
|
||||
deleteFeed: id => {
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("feeds")
|
||||
.doc(id)
|
||||
.delete()
|
||||
.catch(e => console.error("error deleting", id, e));
|
||||
},
|
||||
writeSettings: async (setting, value) => {
|
||||
const st = {
|
||||
updatedOn: new Date(),
|
||||
author: fb.currentUser.uid,
|
||||
author_name: fb.currentUser.displayName,
|
||||
author_image: fb.currentUser.photoURL,
|
||||
name: setting,
|
||||
value
|
||||
};
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("settings")
|
||||
.doc(setting)
|
||||
.set(st)
|
||||
.catch(e => console.error("error updating", st, e));
|
||||
},
|
||||
writeHistory: async entry => {
|
||||
const hs = entry;
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("history")
|
||||
.add(hs)
|
||||
.catch(e => console.error("error inserting", hs, e));
|
||||
},
|
||||
deleteHistory: entry => {
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("history")
|
||||
.doc(entry.id)
|
||||
.delete()
|
||||
.catch(e => console.error("error deleting", entry, e));
|
||||
},
|
||||
clearHistory: () => {
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("history")
|
||||
.get()
|
||||
.then(({ docs }) => {
|
||||
docs.forEach(e => fb.deleteHistory(e));
|
||||
});
|
||||
},
|
||||
toggleStar: (entry, value) => {
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("history")
|
||||
.doc(entry.id)
|
||||
.update({ star: value })
|
||||
.catch(e => console.error("error deleting", entry, e));
|
||||
},
|
||||
writeCollections: async collection => {
|
||||
const cl = {
|
||||
updatedOn: new Date(),
|
||||
author: fb.currentUser.uid,
|
||||
author_name: fb.currentUser.displayName,
|
||||
author_image: fb.currentUser.photoURL,
|
||||
collection: collection
|
||||
};
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("collections")
|
||||
.doc("sync")
|
||||
.set(cl)
|
||||
.catch(e => console.error("error updating", cl, e));
|
||||
},
|
||||
writeEnvironments: async environment => {
|
||||
const ev = {
|
||||
updatedOn: new Date(),
|
||||
author: fb.currentUser.uid,
|
||||
author_name: fb.currentUser.displayName,
|
||||
author_image: fb.currentUser.photoURL,
|
||||
environment: environment
|
||||
};
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("environments")
|
||||
.doc("sync")
|
||||
.set(ev)
|
||||
.catch(e => console.error("error updating", ev, e));
|
||||
}
|
||||
};
|
||||
|
||||
// When a user logs in or out, save that in the store
|
||||
firebase.auth().onAuthStateChanged(user => {
|
||||
if (user) {
|
||||
fb.currentUser = user;
|
||||
fb.currentUser.providerData.forEach(profile => {
|
||||
let us = {
|
||||
updatedOn: new Date(),
|
||||
provider: profile.providerId,
|
||||
name: profile.displayName,
|
||||
email: profile.email,
|
||||
photoUrl: profile.photoURL,
|
||||
uid: profile.uid
|
||||
};
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.set(us)
|
||||
.catch(e => console.error("error updating", us, e));
|
||||
});
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("feeds")
|
||||
.orderBy("createdOn", "desc")
|
||||
.onSnapshot(feedsRef => {
|
||||
const feeds = [];
|
||||
feedsRef.forEach(doc => {
|
||||
const feed = doc.data();
|
||||
feed.id = doc.id;
|
||||
feeds.push(feed);
|
||||
});
|
||||
fb.currentFeeds = feeds;
|
||||
});
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("settings")
|
||||
.onSnapshot(settingsRef => {
|
||||
const settings = [];
|
||||
settingsRef.forEach(doc => {
|
||||
const setting = doc.data();
|
||||
setting.id = doc.id;
|
||||
settings.push(setting);
|
||||
});
|
||||
fb.currentSettings = settings;
|
||||
});
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("history")
|
||||
.onSnapshot(historyRef => {
|
||||
const history = [];
|
||||
historyRef.forEach(doc => {
|
||||
const entry = doc.data();
|
||||
entry.id = doc.id;
|
||||
history.push(entry);
|
||||
});
|
||||
fb.currentHistory = history;
|
||||
});
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("collections")
|
||||
.onSnapshot(collectionsRef => {
|
||||
const collections = [];
|
||||
collectionsRef.forEach(doc => {
|
||||
const collection = doc.data();
|
||||
collection.id = doc.id;
|
||||
collections.push(collection);
|
||||
});
|
||||
fb.currentCollections = collections[0].collection;
|
||||
});
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("environments")
|
||||
.onSnapshot(environmentsRef => {
|
||||
const environments = [];
|
||||
environmentsRef.forEach(doc => {
|
||||
const environment = doc.data();
|
||||
environment.id = doc.id;
|
||||
environments.push(environment);
|
||||
});
|
||||
fb.currentEnvironments = environments[0].environment;
|
||||
});
|
||||
} else {
|
||||
fb.currentUser = null;
|
||||
}
|
||||
});
|
||||
124
functions/headers.js
Normal file
124
functions/headers.js
Normal file
@@ -0,0 +1,124 @@
|
||||
export const commonHeaders = [
|
||||
"WWW-Authenticate",
|
||||
"Authorization",
|
||||
"Proxy-Authenticate",
|
||||
"Proxy-Authorization",
|
||||
"Age",
|
||||
"Cache-Control",
|
||||
"Clear-Site-Data",
|
||||
"Expires",
|
||||
"Pragma",
|
||||
"Warning",
|
||||
"Accept-CH",
|
||||
"Accept-CH-Lifetime",
|
||||
"Early-Data",
|
||||
"Content-DPR",
|
||||
"DPR",
|
||||
"Device-Memory",
|
||||
"Save-Data",
|
||||
"Viewport-Width",
|
||||
"Width",
|
||||
"Last-Modified",
|
||||
"ETag",
|
||||
"If-Match",
|
||||
"If-None-Match",
|
||||
"If-Modified-Since",
|
||||
"If-Unmodified-Since",
|
||||
"Vary",
|
||||
"Connection",
|
||||
"Keep-Alive",
|
||||
"Accept",
|
||||
"Accept-Charset",
|
||||
"Accept-Encoding",
|
||||
"Accept-Language",
|
||||
"Expect",
|
||||
"Max-Forwards",
|
||||
"Cookie",
|
||||
"Set-Cookie",
|
||||
"Cookie2",
|
||||
"Set-Cookie2",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Access-Control-Allow-Credentials",
|
||||
"Access-Control-Allow-Headers",
|
||||
"Access-Control-Allow-Methods",
|
||||
"Access-Control-Expose-Headers",
|
||||
"Access-Control-Max-Age",
|
||||
"Access-Control-Request-Headers",
|
||||
"Access-Control-Request-Method",
|
||||
"Origin",
|
||||
"Service-Worker-Allowed",
|
||||
"Timing-Allow-Origin",
|
||||
"X-Permitted-Cross-Domain-Policies",
|
||||
"DNT",
|
||||
"Tk",
|
||||
"Content-Disposition",
|
||||
"Content-Length",
|
||||
"Content-Type",
|
||||
"Content-Encoding",
|
||||
"Content-Language",
|
||||
"Content-Location",
|
||||
"Forwarded",
|
||||
"X-Forwarded-For",
|
||||
"X-Forwarded-Host",
|
||||
"X-Forwarded-Proto",
|
||||
"Via",
|
||||
"Location",
|
||||
"From",
|
||||
"Host",
|
||||
"Referer",
|
||||
"Referrer-Policy",
|
||||
"User-Agent",
|
||||
"Allow",
|
||||
"Server",
|
||||
"Accept-Ranges",
|
||||
"Range",
|
||||
"If-Range",
|
||||
"Content-Range",
|
||||
"Cross-Origin-Opener-Policy",
|
||||
"Cross-Origin-Resource-Policy",
|
||||
"Content-Security-Policy",
|
||||
"Content-Security-Policy-Report-Only",
|
||||
"Expect-CT",
|
||||
"Feature-Policy",
|
||||
"Public-Key-Pins",
|
||||
"Public-Key-Pins-Report-Only",
|
||||
"Strict-Transport-Security",
|
||||
"Upgrade-Insecure-Requests",
|
||||
"X-Content-Type-Options",
|
||||
"X-Download-Options",
|
||||
"X-Frame-Options",
|
||||
"X-Powered-By",
|
||||
"X-XSS-Protection",
|
||||
"Last-Event-ID",
|
||||
"NEL",
|
||||
"Ping-From",
|
||||
"Ping-To",
|
||||
"Report-To",
|
||||
"Transfer-Encoding",
|
||||
"TE",
|
||||
"Trailer",
|
||||
"Sec-WebSocket-Key",
|
||||
"Sec-WebSocket-Extensions",
|
||||
"Sec-WebSocket-Accept",
|
||||
"Sec-WebSocket-Protocol",
|
||||
"Sec-WebSocket-Version",
|
||||
"Accept-Push-Policy",
|
||||
"Accept-Signature",
|
||||
"Alt-Svc",
|
||||
"Date",
|
||||
"Large-Allocation",
|
||||
"Link",
|
||||
"Push-Policy",
|
||||
"Retry-After",
|
||||
"Signature",
|
||||
"Signed-Headers",
|
||||
"Server-Timing",
|
||||
"SourceMap",
|
||||
"Upgrade",
|
||||
"X-DNS-Prefetch-Control",
|
||||
"X-Firefox-Spdy",
|
||||
"X-Pingback",
|
||||
"X-Requested-With",
|
||||
"X-Robots-Tag",
|
||||
"X-UA-Compatible"
|
||||
];
|
||||
@@ -1,4 +1,4 @@
|
||||
const functions = require('firebase-functions');
|
||||
// const functions = require('firebase-functions');
|
||||
|
||||
// // Create and Deploy Your First Cloud Functions
|
||||
// // https://firebase.google.com/docs/functions/write-firebase-functions
|
||||
|
||||
42
functions/network.js
Normal file
42
functions/network.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import AxiosStrategy from "./strategies/AxiosStrategy";
|
||||
import ExtensionStrategy, {
|
||||
hasExtensionInstalled
|
||||
} from "./strategies/ExtensionStrategy";
|
||||
import FirefoxStrategy from "./strategies/FirefoxStrategy";
|
||||
import ChromeStrategy, {
|
||||
hasChromeExtensionInstalled
|
||||
} from "./strategies/ChromeStrategy";
|
||||
|
||||
const isExtensionsAllowed = ({ state }) =>
|
||||
typeof state.postwoman.settings.EXTENSIONS_ENABLED === "undefined" ||
|
||||
state.postwoman.settings.EXTENSIONS_ENABLED;
|
||||
|
||||
const runAppropriateStrategy = (req, store) => {
|
||||
if (isExtensionsAllowed(store)) {
|
||||
if (hasExtensionInstalled()) {
|
||||
return ExtensionStrategy(req, store);
|
||||
}
|
||||
|
||||
// The following strategies are deprecated and kept to support older version of the extensions
|
||||
|
||||
// Chrome Provides a chrome object for scripts to access
|
||||
// Check its availability to say whether you are in Google Chrome
|
||||
if (window.chrome && hasChromeExtensionInstalled()) {
|
||||
return ChromeStrategy(req, store);
|
||||
}
|
||||
// The firefox plugin injects a function to send requests through it
|
||||
// If that is available, then we can use the FirefoxStrategy
|
||||
if (window.firefoxExtSendRequest) {
|
||||
return FirefoxStrategy(req, store);
|
||||
}
|
||||
}
|
||||
|
||||
return AxiosStrategy(req, store);
|
||||
};
|
||||
|
||||
const sendNetworkRequest = (req, store) =>
|
||||
runAppropriateStrategy(req, store).finally(() =>
|
||||
window.$nuxt.$loading.finish()
|
||||
);
|
||||
|
||||
export { sendNetworkRequest };
|
||||
175
functions/postwomanTesting.js
Normal file
175
functions/postwomanTesting.js
Normal file
@@ -0,0 +1,175 @@
|
||||
const PASS = "PASS";
|
||||
const FAIL = "FAIL";
|
||||
const ERROR = "ERROR";
|
||||
|
||||
const styles = {
|
||||
[PASS]: { icon: "check", class: "success-response" },
|
||||
[FAIL]: { icon: "close", class: "cl-error-response" },
|
||||
[ERROR]: { icon: "close", class: "cl-error-response" },
|
||||
none: { icon: "", class: "" }
|
||||
};
|
||||
|
||||
// TODO: probably have to use a more global state for `test`
|
||||
|
||||
export default function runTestScriptWithVariables(script, variables) {
|
||||
let pw = {
|
||||
_errors: [],
|
||||
_testReports: [],
|
||||
_report: "",
|
||||
expect(value) {
|
||||
try {
|
||||
return expect(value, this._testReports);
|
||||
} catch (e) {
|
||||
pw._testReports.push({ result: ERROR, message: e });
|
||||
}
|
||||
},
|
||||
test: (descriptor, func) => test(descriptor, func, pw._testReports)
|
||||
// globals that the script is allowed to have access to.
|
||||
};
|
||||
Object.assign(pw, variables);
|
||||
|
||||
// run pre-request script within this function so that it has access to the pw object.
|
||||
new Function("pw", script)(pw);
|
||||
//
|
||||
const testReports = pw._testReports.map(item => {
|
||||
if (item.result) {
|
||||
item.styles = styles[item.result];
|
||||
} else {
|
||||
item.styles = styles.none;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
return { report: pw._report, errors: pw._errors, testResults: testReports };
|
||||
}
|
||||
|
||||
function test(descriptor, func, _testReports) {
|
||||
_testReports.push({ startBlock: descriptor });
|
||||
try {
|
||||
func();
|
||||
} catch (e) {
|
||||
_testReports.push({ result: ERROR, message: e });
|
||||
}
|
||||
_testReports.push({ endBlock: true });
|
||||
|
||||
// TODO: Organize and generate text report of each {descriptor: true} section in testReports.
|
||||
// add checkmark or x depending on if each testReport is pass=true or pass=false
|
||||
}
|
||||
|
||||
function expect(expectValue, _testReports) {
|
||||
return new Expectation(expectValue, null, _testReports);
|
||||
}
|
||||
|
||||
class Expectation {
|
||||
constructor(expectValue, _not, _testReports) {
|
||||
this.expectValue = expectValue;
|
||||
this.not = _not || new Expectation(this.expectValue, true, _testReports);
|
||||
this._testReports = _testReports; // this values is used within Test.it, which wraps Expectation and passes _testReports value.
|
||||
this._satisfies = function(expectValue, targetValue) {
|
||||
// Used for testing if two values match the expectation, which could be === OR !==, depending on if not
|
||||
// was used. Expectation#_satisfies prevents the need to have an if(this.not) branch in every test method.
|
||||
// Signature is _satisfies([expectValue,] targetValue): if only one argument is given, it is assumed the targetValue, and expectValue is set to this.expectValue
|
||||
if (!targetValue) {
|
||||
targetValue = expectValue;
|
||||
expectValue = this.expectValue;
|
||||
}
|
||||
if (this.not === true) {
|
||||
// test the inverse. this.not is always truthly, but an Expectation that is inverted will always be strictly `true`
|
||||
return expectValue !== targetValue;
|
||||
} else {
|
||||
return expectValue === targetValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
_fmtNot(message) {
|
||||
// given a string with "(not)" in it, replaces with "not" or "", depending if the expectation is expecting the positive or inverse (this._not)
|
||||
if (this.not === true) {
|
||||
return message.replace("(not)", "not ");
|
||||
} else {
|
||||
return message.replace("(not)", "");
|
||||
}
|
||||
}
|
||||
_fail(message) {
|
||||
this._testReports.push({ result: FAIL, message });
|
||||
}
|
||||
_pass(message) {
|
||||
this._testReports.push({ result: PASS });
|
||||
}
|
||||
// TEST METHODS DEFINED BELOW
|
||||
// these are the usual methods that would follow expect(...)
|
||||
toBe(value) {
|
||||
return this._satisfies(value)
|
||||
? this._pass()
|
||||
: this._fail(
|
||||
this._fmtNot(`Expected ${this.expectValue} (not)to be ${value}`)
|
||||
);
|
||||
}
|
||||
toHaveProperty(value) {
|
||||
return this._satisfies(this.expectValue.hasOwnProperty(value), true)
|
||||
? this._pass()
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected object ${this.expectValue} to (not)have property ${value}`
|
||||
)
|
||||
);
|
||||
}
|
||||
toBeLevel2xx() {
|
||||
const code = parseInt(this.expectValue);
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(
|
||||
`Expected 200-level status but could not parse value ${this.expectValue}`
|
||||
);
|
||||
}
|
||||
return this._satisfies(code >= 200 && code < 300)
|
||||
? this._pass()
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected ${this.expectValue} to (not)be 200-level status`
|
||||
)
|
||||
);
|
||||
}
|
||||
toBeLevel3xx() {
|
||||
const code = parseInt(this.expectValue);
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(
|
||||
`Expected 300-level status but could not parse value ${this.expectValue}`
|
||||
);
|
||||
}
|
||||
return this._satisfies(code >= 300 && code < 400)
|
||||
? this._pass()
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected ${this.expectValue} to (not)be 300-level status`
|
||||
)
|
||||
);
|
||||
}
|
||||
toBeLevel4xx() {
|
||||
const code = parseInt(this.expectValue);
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(
|
||||
`Expected 400-level status but could not parse value ${this.expectValue}`
|
||||
);
|
||||
}
|
||||
return this._satisfies(code >= 400 && code < 500)
|
||||
? this._pass()
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected ${this.expectValue} to (not)be 400-level status`
|
||||
)
|
||||
);
|
||||
}
|
||||
toBeLevel5xx() {
|
||||
const code = parseInt(this.expectValue);
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(
|
||||
`Expected 500-level status but could not parse value ${this.expectValue}`
|
||||
);
|
||||
}
|
||||
return this._satisfies(code >= 500 && code < 600)
|
||||
? this._pass()
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected ${this.expectValue} to (not)be 500-level status`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,16 @@ export default function getEnvironmentVariablesFromScript(script) {
|
||||
// for security and control purposes, this is the only way a pre-request script should modify variables.
|
||||
let pw = {
|
||||
environment: {
|
||||
set: (key, value) => _variables[key] = value,
|
||||
set: (key, value) => (_variables[key] = value)
|
||||
},
|
||||
env: {
|
||||
set: (key, value) => (_variables[key] = value)
|
||||
}
|
||||
// globals that the script is allowed to have access to.
|
||||
};
|
||||
|
||||
// run pre-request script within this function so that it has access to the pw object.
|
||||
(new Function('pw', script))(pw);
|
||||
new Function("pw", script)(pw);
|
||||
|
||||
return _variables;
|
||||
}
|
||||
|
||||
23
functions/strategies/AxiosStrategy.js
Normal file
23
functions/strategies/AxiosStrategy.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import axios from "axios";
|
||||
|
||||
const axiosWithProxy = async (req, { state }) => {
|
||||
const { data } = await axios.post(
|
||||
state.postwoman.settings.PROXY_URL || "https://postwoman.apollotv.xyz/",
|
||||
req
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
const axiosWithoutProxy = async (req, _store) => {
|
||||
const res = await axios(req);
|
||||
return res;
|
||||
};
|
||||
|
||||
const axiosStrategy = (req, store) => {
|
||||
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||
return axiosWithProxy(req, store);
|
||||
}
|
||||
return axiosWithoutProxy(req, store);
|
||||
};
|
||||
|
||||
export default axiosStrategy;
|
||||
63
functions/strategies/ChromeStrategy.js
Normal file
63
functions/strategies/ChromeStrategy.js
Normal file
@@ -0,0 +1,63 @@
|
||||
const EXTENSION_ID = "amknoiejhlmhancpahfcfcfhllgkpbld";
|
||||
|
||||
// Check if the Chrome Extension is present
|
||||
// The Chrome extension injects an empty span to help detection.
|
||||
// Also check for the presence of window.chrome object to confirm smooth operations
|
||||
export const hasChromeExtensionInstalled = () =>
|
||||
document.getElementById("chromePWExtensionDetect") !== null;
|
||||
|
||||
const chromeWithoutProxy = (req, _store) =>
|
||||
new Promise((resolve, reject) => {
|
||||
chrome.runtime.sendMessage(
|
||||
EXTENSION_ID,
|
||||
{
|
||||
messageType: "send-req",
|
||||
data: {
|
||||
config: req
|
||||
}
|
||||
},
|
||||
({ data }) => {
|
||||
if (data.error) {
|
||||
reject(data.error);
|
||||
} else {
|
||||
resolve(data.response);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const chromeWithProxy = (req, { state }) =>
|
||||
new Promise((resolve, reject) => {
|
||||
chrome.runtime.sendMessage(
|
||||
EXTENSION_ID,
|
||||
{
|
||||
messageType: "send-req",
|
||||
data: {
|
||||
config: {
|
||||
method: "post",
|
||||
url:
|
||||
state.postwoman.settings.PROXY_URL ||
|
||||
"https://postwoman.apollotv.xyz/",
|
||||
data: req
|
||||
}
|
||||
}
|
||||
},
|
||||
({ data }) => {
|
||||
if (data.error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(data.response.data);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const chromeStrategy = (req, store) => {
|
||||
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||
return chromeWithProxy(req, store);
|
||||
} else {
|
||||
return chromeWithoutProxy(req, store);
|
||||
}
|
||||
};
|
||||
|
||||
export default chromeStrategy;
|
||||
26
functions/strategies/ExtensionStrategy.js
Normal file
26
functions/strategies/ExtensionStrategy.js
Normal file
@@ -0,0 +1,26 @@
|
||||
export const hasExtensionInstalled = () =>
|
||||
typeof window.__POSTWOMAN_EXTENSION_HOOK__ !== "undefined";
|
||||
|
||||
const extensionWithProxy = async (req, { state }) => {
|
||||
const { data } = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest({
|
||||
method: "post",
|
||||
url:
|
||||
state.postwoman.settings.PROXY_URL || "https://postwoman.apollotv.xyz/",
|
||||
data: req
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const extensionWithoutProxy = async (req, _store) => {
|
||||
const res = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest(req);
|
||||
return res;
|
||||
};
|
||||
|
||||
const extensionStrategy = (req, store) => {
|
||||
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||
return extensionWithProxy(req, store);
|
||||
}
|
||||
return extensionWithoutProxy(req, store);
|
||||
};
|
||||
|
||||
export default extensionStrategy;
|
||||
50
functions/strategies/FirefoxStrategy.js
Normal file
50
functions/strategies/FirefoxStrategy.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const firefoxWithProxy = (req, { state }) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const eventListener = event => {
|
||||
window.removeEventListener("firefoxExtSendRequestComplete", event);
|
||||
|
||||
if (event.detail.error) {
|
||||
reject(JSON.parse(event.detail.error));
|
||||
} else {
|
||||
resolve(JSON.parse(event.detail.response).data);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("firefoxExtSendRequestComplete", eventListener);
|
||||
|
||||
window.firefoxExtSendRequest({
|
||||
method: "post",
|
||||
url:
|
||||
state.postwoman.settings.PROXY_URL || "https://postwoman.apollotv.xyz/",
|
||||
data: req
|
||||
});
|
||||
});
|
||||
|
||||
const firefoxWithoutProxy = (req, _store) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const eventListener = ({ detail }) => {
|
||||
window.removeEventListener(
|
||||
"firefoxExtSendRequestComplete",
|
||||
eventListener
|
||||
);
|
||||
|
||||
if (detail.error) {
|
||||
reject(JSON.parse(detail.error));
|
||||
} else {
|
||||
resolve(JSON.parse(detail.response));
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("firefoxExtSendRequestComplete", eventListener);
|
||||
|
||||
window.firefoxExtSendRequest(req);
|
||||
});
|
||||
|
||||
const firefoxStrategy = (req, store) => {
|
||||
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||
return firefoxWithProxy(req, store);
|
||||
}
|
||||
return firefoxWithoutProxy(req, store);
|
||||
};
|
||||
|
||||
export default firefoxStrategy;
|
||||
@@ -1,7 +1,7 @@
|
||||
export default function parseTemplateString(string, variables) {
|
||||
if(!variables || !string) {
|
||||
if (!variables || !string) {
|
||||
return string;
|
||||
}
|
||||
const searchTerm = /<<([^>]*)>>/g; // "<<myVariable>>"
|
||||
return string.replace(searchTerm, (match, p1) => variables[p1] || '');
|
||||
return string.replace(searchTerm, (match, p1) => variables[p1] || "");
|
||||
}
|
||||
|
||||
15
functions/utils/debounce.js
Normal file
15
functions/utils/debounce.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// Debounce is a higher order function which makes its enclosed function be executed
|
||||
// only if the function wasn't called again till 'delay' time has passed, this helps reduce impact of heavy working
|
||||
// functions which might be called frequently
|
||||
// NOTE : Don't use lambda functions as this doesn't get bound properly in them, use the 'function (args) {}' format
|
||||
const debounce = (func, delay) => {
|
||||
let inDebounce;
|
||||
return function() {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
clearTimeout(inDebounce);
|
||||
inDebounce = setTimeout(() => func.apply(context, args), delay);
|
||||
};
|
||||
};
|
||||
|
||||
export default debounce;
|
||||
90
lang/de-DE.js
Normal file
90
lang/de-DE.js
Normal file
@@ -0,0 +1,90 @@
|
||||
export default {
|
||||
home: "Startseite",
|
||||
realtime: "Echtzeit",
|
||||
graphql: "GraphQL",
|
||||
settings: "Einstellungen",
|
||||
request: "Anfrage",
|
||||
install_pwa: "PWA installieren",
|
||||
support_us: "Unterstütze uns",
|
||||
tweet: "Twittern",
|
||||
options: "Optionen",
|
||||
communication: "Kommunikation",
|
||||
endpoint: "Endpunkt",
|
||||
schema: "Schema",
|
||||
theme: "Design",
|
||||
subscribe: "Abonnieren",
|
||||
choose_language: "Sprache auswählen",
|
||||
shortcuts: "Tastenkombinationen",
|
||||
send_request: "Anfrage senden",
|
||||
save_to_collections: "In Sammlungen speichern",
|
||||
copy_request_link: "Anfragelink kopieren",
|
||||
reset_request: "Anfrage zurücksetzen",
|
||||
support_us_on: "Unterstütze uns auf",
|
||||
open_collective: "Open Collective",
|
||||
paypal: "PayPal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "JavaScript-Code",
|
||||
method: "Methode",
|
||||
path: "Pfad",
|
||||
label: "Beschriftung",
|
||||
again: "Wiederholen",
|
||||
content_type: "Content-Typ",
|
||||
raw_input: "Rohdaten-Eingabe",
|
||||
parameter_list: "Parameterliste",
|
||||
raw_request_body: "Rohdaten der Anfrage",
|
||||
show_code: "Code zeigen",
|
||||
hide_code: "Code ausblenden",
|
||||
show_prerequest_script: "Preanfrageskript anzeigen",
|
||||
hide_prerequest_script: "Preanfrageskript ausblenden",
|
||||
authentication: "Authentifizierung",
|
||||
authentication_type: "Authentifizierungs-Typ",
|
||||
include_in_url: "In URL einfügen",
|
||||
parameters: "Parameter",
|
||||
expand_response: "Antwort ausklappen",
|
||||
collapse_response: "Antwort einklappen",
|
||||
hide_preview: "Vorschau verstecken",
|
||||
preview_html: "HTML-Vorschau",
|
||||
history: "Verlauf",
|
||||
collections: "Kollektionen",
|
||||
import_curl: "cURL importieren",
|
||||
import: "Importieren",
|
||||
generate_code: "Code generieren",
|
||||
request_type: "Anfrage-Typ",
|
||||
generated_code: "Generierter Code",
|
||||
status: "Status",
|
||||
headers: "Headers",
|
||||
websocket: "WebSocket",
|
||||
waiting_for_connection: "(warte auf Verbindung)",
|
||||
message: "Nachricht",
|
||||
sse: "SSE",
|
||||
server: "Server",
|
||||
events: "Ereignisse",
|
||||
url: "URL",
|
||||
get_schema: "Schema abrufen",
|
||||
header_list: "Headerliste",
|
||||
add_new: "Neu hinzufügen",
|
||||
response: "Antwort",
|
||||
query: "Abfrage",
|
||||
queries: "Abfragen",
|
||||
query_variables: "Variablen",
|
||||
mutations: "Mutationen",
|
||||
subscriptions: "Abonnements",
|
||||
types: "Typen",
|
||||
send: "Senden",
|
||||
background: "Hintergrund",
|
||||
color: "Farbe",
|
||||
labels: "Bezeichner",
|
||||
multi_color: "Mehrfarbig",
|
||||
enabled: "Aktiviert",
|
||||
disabled: "Deaktiviert",
|
||||
proxy: "Proxy",
|
||||
postwoman_official_proxy_hosting:
|
||||
"Postwomans offizieller Proxy wird durch ApolloTV bereitgestellt.",
|
||||
read_the: "Lies die",
|
||||
apollotv_privacy_policy: "ApolloTV Datenschutzerklärung",
|
||||
contact_us: "Kontaktiere uns",
|
||||
connect: "Verbinden",
|
||||
disconnect: "Trennen",
|
||||
start: "Start",
|
||||
stop: "Stopp"
|
||||
};
|
||||
277
lang/en-US.js
Normal file
277
lang/en-US.js
Normal file
@@ -0,0 +1,277 @@
|
||||
export default {
|
||||
home: "Home",
|
||||
realtime: "Realtime",
|
||||
graphql: "GraphQL",
|
||||
settings: "Settings",
|
||||
request: "Request",
|
||||
install_pwa: "Install PWA",
|
||||
support_us: "Support us",
|
||||
tweet: "Tweet",
|
||||
options: "Options",
|
||||
communication: "Communication",
|
||||
endpoint: "Endpoint",
|
||||
schema: "Schema",
|
||||
theme: "Theme",
|
||||
subscribe: "Subscribe",
|
||||
choose_language: "Choose Language",
|
||||
shortcuts: "Shortcuts",
|
||||
send_request: "Send Request",
|
||||
save_to_collections: "Save to Collections",
|
||||
copy_request_link: "Copy Request Link",
|
||||
reset_request: "Reset Request",
|
||||
support_us_on: "Support us on",
|
||||
open_collective: "Open Collective",
|
||||
paypal: "PayPal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "JavaScript Code",
|
||||
method: "Method",
|
||||
path: "Path",
|
||||
label: "Label",
|
||||
content_type: "Content Type",
|
||||
raw_input: "Raw input",
|
||||
parameter_list: "Parameter List",
|
||||
raw_request_body: "Raw Request Body",
|
||||
show_code: "Show Code",
|
||||
hide_code: "Hide Code",
|
||||
show_prerequest_script: "Show Pre-Request Script",
|
||||
hide_prerequest_script: "Hide Pre-Request Script",
|
||||
authentication: "Authentication",
|
||||
authentication_type: "Authentication type",
|
||||
include_in_url: "Include in URL",
|
||||
parameters: "Parameters",
|
||||
expand_response: "Expand response",
|
||||
collapse_response: "Collapse response",
|
||||
hide_preview: "Hide Preview",
|
||||
preview_html: "Preview HTML",
|
||||
history: "History",
|
||||
collections: "Collections",
|
||||
environment: "Environment",
|
||||
new_environment: "New Environment",
|
||||
my_new_environment: "My New Environment",
|
||||
edit_environment: "Edit Environment",
|
||||
env_variable_list: "Variable List",
|
||||
invalid_environment_name: "Please provide a valid name for the environment",
|
||||
use_environment: "Use Environment",
|
||||
add_one_variable: "(add at least one variable)",
|
||||
import_curl: "Import cURL",
|
||||
import: "Import",
|
||||
generate_code: "Generate code",
|
||||
request_type: "Request type",
|
||||
generated_code: "Generated code",
|
||||
status: "Status",
|
||||
headers: "Headers",
|
||||
websocket: "WebSocket",
|
||||
waiting_for_connection: "(waiting for connection)",
|
||||
message: "Message",
|
||||
sse: "SSE",
|
||||
server: "Server",
|
||||
events: "Events",
|
||||
url: "URL",
|
||||
get_schema: "Get schema",
|
||||
header_list: "Header list",
|
||||
add_new: "Add new",
|
||||
response: "Response",
|
||||
query: "Query",
|
||||
queries: "Queries",
|
||||
query_variables: "Variables",
|
||||
mutations: "Mutations",
|
||||
subscriptions: "Subscriptions",
|
||||
types: "Types",
|
||||
send: "Send",
|
||||
background: "Background",
|
||||
color: "Color",
|
||||
labels: "Labels",
|
||||
multi_color: "Multi-color",
|
||||
enabled: "Enabled",
|
||||
disabled: "Disabled",
|
||||
proxy: "Proxy",
|
||||
postwoman_official_proxy_hosting:
|
||||
"Postwoman's Official Proxy is hosted by ApolloTV.",
|
||||
read_the: "Read the",
|
||||
apollotv_privacy_policy: "ApolloTV privacy policy",
|
||||
contact_us: "Contact us",
|
||||
connect: "Connect",
|
||||
disconnect: "Disconnect",
|
||||
start: "Start",
|
||||
stop: "Stop",
|
||||
access_token: "Access Token",
|
||||
token_list: "Token List",
|
||||
get_token: "Get New Token",
|
||||
manage_token: "Manage Access Token",
|
||||
save_token: "Save Access Token",
|
||||
use_token: "Use Saved Token",
|
||||
request_token: "Request Token",
|
||||
save_token_req: "Save Token Request",
|
||||
manage_token_req: "Manage Token Request",
|
||||
use_token_req: "Use Token Request",
|
||||
token_req_name: "Request Name",
|
||||
token_req_details: "Request Details",
|
||||
token_name: "Token Name",
|
||||
oidc_discovery_url: "OIDC Discovery URL",
|
||||
auth_url: "Auth URL",
|
||||
access_token_url: "Access Token URL",
|
||||
client_id: "Client ID",
|
||||
scope: "Scope",
|
||||
state: "State",
|
||||
token_req_list: "Token Request List",
|
||||
no_path: "No path",
|
||||
no_label: "No label",
|
||||
prerequest_script: "Pre-Request Script",
|
||||
no_prerequest_script: "No pre-request script",
|
||||
search: "Search",
|
||||
history_empty: "History is empty",
|
||||
history_deleted: "History Deleted",
|
||||
clear: "Clear",
|
||||
clear_all: "Clear All",
|
||||
cleared: "Cleared",
|
||||
close: "Close",
|
||||
sort: "Sort",
|
||||
time: "Time",
|
||||
duration: "Duration",
|
||||
no_duration: "No duration",
|
||||
show_more: "Show more",
|
||||
hide_more: "Hide more",
|
||||
collection: "Collection",
|
||||
current_collection: "Current Collection",
|
||||
select_collection: "Select a Collection",
|
||||
create_collection: "Create a Collection",
|
||||
new: "New",
|
||||
import_export: "Import / Export",
|
||||
more: "More",
|
||||
folder: "Folder",
|
||||
new_folder: "New Folder",
|
||||
my_new_folder: "My New Folder",
|
||||
folder_empty: "Folder is empty",
|
||||
edit_folder: "Edit Folder",
|
||||
edit: "Edit",
|
||||
delete: "Delete",
|
||||
deleted: "Deleted",
|
||||
undo: "Undo",
|
||||
collection_empty: "Collection is empty",
|
||||
invalid_collection_name: "Please provide a valid name for the collection",
|
||||
new_collection: "New Collection",
|
||||
my_new_collection: "My New Collection",
|
||||
edit_collection: "Edit Collection",
|
||||
edit_request: "Edit Request",
|
||||
save_request_as: "Save Request As",
|
||||
export: "Export",
|
||||
connecting_to: "Connecting to {name}...",
|
||||
connected: "Connected",
|
||||
connected_to: "Connected to {name}",
|
||||
disconnected: "Disconnected",
|
||||
disconnected_from: "Disconnected from {name}",
|
||||
something_went_wrong: "Something went wrong!",
|
||||
error_occurred: "An error has occurred.",
|
||||
browser_support_sse:
|
||||
"This browser doesn't seems to have Server Sent Events support.",
|
||||
log: "Log",
|
||||
no_url: "No URL",
|
||||
run_query: "Run Query",
|
||||
copy_query: "Copy Query",
|
||||
kinda_dark: "Kinda Dark",
|
||||
clearly_white: "Clearly White",
|
||||
just_black: "Just Black",
|
||||
auto_system: "Auto (system)",
|
||||
green: "Green",
|
||||
yellow: "Yellow",
|
||||
pink: "Pink",
|
||||
red: "Red",
|
||||
purple: "Purple",
|
||||
orange: "Orange",
|
||||
cyan: "Cyan",
|
||||
blue: "Blue",
|
||||
loading: "Loading...",
|
||||
fetching: "Fetching...",
|
||||
waiting_send_req: "(waiting to send request)",
|
||||
cancel: "Cancel",
|
||||
save: "Save",
|
||||
dismiss: "Dismiss",
|
||||
are_you_sure: "Are you sure?",
|
||||
yes: "Yes",
|
||||
no: "No",
|
||||
restore: "Restore",
|
||||
add_star: "Add star",
|
||||
remove_star: "Remove star",
|
||||
nothing_found: "Nothing found",
|
||||
replace_current: "Replace current",
|
||||
replace_json: "Replace with JSON",
|
||||
preserve_current: "Preserve current",
|
||||
import_json: "Import from JSON",
|
||||
download_file: "Download file",
|
||||
upload_file: "Upload file",
|
||||
copy_response: "Copy response",
|
||||
copy_code: "Copy code",
|
||||
copy_schema: "Copy Schema",
|
||||
use_request: "Use request",
|
||||
documentation: "Documentation",
|
||||
docs: "Docs",
|
||||
reset_default: "Reset to default",
|
||||
fields: "FIELDS",
|
||||
deprecated: "DEPRECATED",
|
||||
add_one_header: "(add at least one header)",
|
||||
add_one_parameter: "(add at least one parameter)",
|
||||
header_count: "header {count}",
|
||||
parameter_count: "parameter {count}",
|
||||
variable_count: "variable {count}",
|
||||
value_count: "value {count}",
|
||||
send_request_first: "Send a request first",
|
||||
generate_docs: "Generate Documentation",
|
||||
generate_docs_message:
|
||||
"Import any Postwoman Collection to Generate Documentation on-the-go.",
|
||||
generate_docs_first: "Generate documentation first",
|
||||
docs_generated: "Documentation generated",
|
||||
import_collections: "Import collections",
|
||||
optional: "(optional)",
|
||||
json: "JSON",
|
||||
none: "None",
|
||||
username: "Username",
|
||||
password: "Password",
|
||||
token: "Token",
|
||||
payload: "Payload",
|
||||
choose_file: "Choose a file",
|
||||
file_imported: "File imported",
|
||||
import_failed: "Import failed",
|
||||
f12_details: "(F12 for details)",
|
||||
we_use_cookies: "We use cookies",
|
||||
copied_to_clipboard: "Copied to clipboard",
|
||||
finished_in: "Finished in {duration}ms",
|
||||
check_console_details: "Check console for details.",
|
||||
download_started: "Download started",
|
||||
url_invalid_format: "URL is not formatted properly",
|
||||
curl_invalid_format: "cURL is not formatted properly",
|
||||
enable_proxy: "Try enabling Proxy",
|
||||
complete_config_urls: "Please complete configuration urls.",
|
||||
token_request_saved: "Token request saved",
|
||||
donate_info1:
|
||||
"If you have enjoyed the productivity of using Postwoman, consider donating as a sign of appreciation.",
|
||||
donate_info2:
|
||||
"You can support Postwoman development via the following methods:",
|
||||
one_time_recurring: "One-time or recurring",
|
||||
one_time: "One-time",
|
||||
recurring: "Recurring",
|
||||
wiki: "Wiki",
|
||||
error: "Error",
|
||||
go_home: "Go Home",
|
||||
reload: "Reload",
|
||||
enter_curl: "Enter cURL",
|
||||
empty: "Empty",
|
||||
extensions: "Extensions",
|
||||
extensions_use_toggle:
|
||||
"Use the browser extension to send requests (if present)",
|
||||
extensions_info1: "Browser extension that simplifies access to Postwoman",
|
||||
extensions_info2: "Get Postwoman browser extension!",
|
||||
installed: "Installed",
|
||||
login_with: "Login with",
|
||||
logged_out: "Logged out",
|
||||
logout: "Logout",
|
||||
account: "Account",
|
||||
sync: "Sync",
|
||||
syncHistory: "History",
|
||||
syncCollections: "Collections",
|
||||
syncEnvironments: "Environments",
|
||||
turn_on: "Turn on",
|
||||
login_first: "Login first",
|
||||
paste_a_note: "Paste a note",
|
||||
import_from_sync: "Import from Sync",
|
||||
notes: "Notes"
|
||||
};
|
||||
264
lang/es-ES.js
Normal file
264
lang/es-ES.js
Normal file
@@ -0,0 +1,264 @@
|
||||
export default {
|
||||
home: "Inicio",
|
||||
realtime: "Tiempo real",
|
||||
graphql: "GraphQL",
|
||||
settings: "Ajustes",
|
||||
request: "Petición",
|
||||
install_pwa: "Instalar PWA",
|
||||
support_us: "Ayúdanos",
|
||||
tweet: "Tweet",
|
||||
options: "Opciones",
|
||||
communication: "Comunicación",
|
||||
endpoint: "Endpoint",
|
||||
schema: "Esquema",
|
||||
theme: "Tema",
|
||||
subscribe: "Subscribirse",
|
||||
choose_language: "Seleccione un idioma",
|
||||
shortcuts: "Atajos",
|
||||
send_request: "Enviar petición",
|
||||
save_to_collections: "Guardar en las Colecciones",
|
||||
copy_request_link: "Copiar enlace de la petición",
|
||||
reset_request: "Reiniciar Petición",
|
||||
support_us_on: "Ayúdanos en",
|
||||
open_collective: "Open Collective",
|
||||
paypal: "PayPal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "Código JavaScript",
|
||||
method: "Método",
|
||||
path: "Ruta",
|
||||
label: "Etiqueta",
|
||||
again: "De nuevo",
|
||||
content_type: "Tipo de Contenido",
|
||||
raw_input: "Datos sin Procesar",
|
||||
parameter_list: "Lista de Parámetros",
|
||||
raw_request_body: "Cuerpo de la Solicitud sin Procesar",
|
||||
show_code: "Mostrar el código",
|
||||
hide_code: "Ocultar el código",
|
||||
show_prerequest_script: "Mostrar Script pre solicitud",
|
||||
hide_prerequest_script: "Ocultar Script pre solicitud",
|
||||
authentication: "Autenticación",
|
||||
authentication_type: "Tipo de autenticación",
|
||||
include_in_url: "Incluir en el URL",
|
||||
parameters: "Parámetros",
|
||||
expand_response: "Ampliar Respuesta",
|
||||
collapse_response: "Contraer Respuesta",
|
||||
hide_preview: "Ocultar la vista previa",
|
||||
preview_html: "Vista Previa del HTML",
|
||||
history: "Historial",
|
||||
collections: "Colecciones",
|
||||
import_curl: "Importar cURL",
|
||||
import: "Importar",
|
||||
generate_code: "Generar código",
|
||||
request_type: "Tipo de Petición",
|
||||
generated_code: "Código Generado",
|
||||
status: "Estado",
|
||||
headers: "Cabeceras",
|
||||
websocket: "WebSocket",
|
||||
waiting_for_connection: "(esperando por conexión)",
|
||||
message: "Mensaje",
|
||||
sse: "SSE",
|
||||
server: "Servidor",
|
||||
events: "Eventos",
|
||||
url: "URL",
|
||||
get_schema: "Obtener esquema",
|
||||
header_list: "Lista de Cabeceras",
|
||||
add_new: "Agregar nuevo",
|
||||
response: "Respuesta",
|
||||
query: "Consulta",
|
||||
queries: "Consultas",
|
||||
query_variables: "Variables",
|
||||
mutations: "Mutaciones",
|
||||
subscriptions: "Subscripciones",
|
||||
types: "Tipos",
|
||||
send: "Enviar",
|
||||
background: "Fondo",
|
||||
color: "Color",
|
||||
labels: "Etiquetas",
|
||||
multi_color: "Multicolor",
|
||||
enabled: "Habilitado",
|
||||
disabled: "Deshabilitado",
|
||||
proxy: "Proxy",
|
||||
postwoman_official_proxy_hosting:
|
||||
"Proxy Oficial de Postwoman está hospedado en ApolloTV.",
|
||||
read_the: "Leer la",
|
||||
apollotv_privacy_policy: "Política de Privacidad de ApolloTV",
|
||||
contact_us: "Contáctenos",
|
||||
connect: "Conectar",
|
||||
disconnect: "Desconectar",
|
||||
start: "Comienzo",
|
||||
stop: "Detener",
|
||||
access_token: "Token de acceso",
|
||||
token_list: "Lista de token",
|
||||
get_token: "Obtener un nuevo token",
|
||||
manage_token: "Gestionar el token de acceso",
|
||||
save_token: "Guardar el token de acceso",
|
||||
use_token: "Usar token guardado",
|
||||
request_token: "Petición del token",
|
||||
save_token_req: "Guardar la petición del token",
|
||||
manage_token_req: "Gestionar la petición del token",
|
||||
use_token_req: "Usar el token de la petición",
|
||||
token_req_name: "Nombre de la petición",
|
||||
token_req_details: "Petición de detalles",
|
||||
token_name: "Nombre del token",
|
||||
oidc_discovery_url: "URL de descubrimiento de OIDC",
|
||||
auth_url: "URL de autenticación",
|
||||
access_token_url: "URL de token de acceso",
|
||||
client_id: "ID del cliente",
|
||||
scope: "Alcance",
|
||||
state: "Estado",
|
||||
token_req_list: "Lista de solicitud de token",
|
||||
no_path: "Sin ruta",
|
||||
no_label: "Sin etiqueta",
|
||||
prerequest_script: "Pre-Request Script",
|
||||
no_prerequest_script: "Script sin pre-requisito",
|
||||
search: "buscar historial",
|
||||
history_empty: "Historial vacío",
|
||||
history_deleted: "Historial borrado",
|
||||
clear: "Limpiar",
|
||||
clear_all: "Limpiar todo",
|
||||
cleared: "Limpiado",
|
||||
close: "Cerrar",
|
||||
sort: "Ordenar",
|
||||
time: "Tiempo",
|
||||
duration: "Duración",
|
||||
no_duration: "Sin duración",
|
||||
show_more: "Mostrar más",
|
||||
hide_more: "Ocultar más",
|
||||
collection: "Colección",
|
||||
current_collection: "Actual colección",
|
||||
select_collection: "Seleccionar una colección",
|
||||
create_collection: "Crear una colección",
|
||||
new: "Nuevo",
|
||||
import_export: "Importar / Exportar",
|
||||
more: "Más",
|
||||
folder: "Directorio",
|
||||
new_folder: "Nuevo directorio",
|
||||
my_new_folder: "Mi nuevo directorio",
|
||||
folder_empty: "Directorio vacío",
|
||||
edit_folder: "Editar directorio",
|
||||
edit: "Editar",
|
||||
delete: "Borrar",
|
||||
deleted: "Borrado",
|
||||
undo: "Deshacer",
|
||||
collection_empty: "Colección vacía",
|
||||
new_collection: "Nueva colección",
|
||||
my_new_collection: "Mi nueva colección",
|
||||
edit_collection: "Editar colección",
|
||||
edit_request: "Editar petición",
|
||||
save_request_as: "Guardar petición como",
|
||||
export: "Exportar",
|
||||
connecting_to: "Conectando a {name}...",
|
||||
connected: "Conectado",
|
||||
connected_to: "Conectado a {name}",
|
||||
disconnected: "Desconectado",
|
||||
disconnected_from: "Desconectado desde {name}",
|
||||
something_went_wrong: "Algo ha salido mal!",
|
||||
error_occurred: "Ha ocurrido un error.",
|
||||
browser_support_sse:
|
||||
"Este navegador parace no tener soporte a los eventos enviados desde el servidor.",
|
||||
log: "Bitácora",
|
||||
no_url: "Sin URL",
|
||||
run_query: "Ejecutar consulta",
|
||||
copy_query: "Copiar consulta",
|
||||
kinda_dark: "Un poco oscúro",
|
||||
clearly_white: "Claramento blanco",
|
||||
just_black: "Solo Negro",
|
||||
auto_system: "Autenticación (sistema)",
|
||||
green: "Verde",
|
||||
yellow: "Amarillo",
|
||||
pink: "Rosado",
|
||||
red: "Rojo",
|
||||
purple: "Púrpura",
|
||||
orange: "Anaranjado",
|
||||
cyan: "Cian",
|
||||
blue: "Azul",
|
||||
loading: "Cargando...",
|
||||
fetching: "Recuperando...",
|
||||
waiting_send_req: "(esperando para enviar la petición)",
|
||||
cancel: "Cancelar",
|
||||
save: "Guardar",
|
||||
dismiss: "Descartar",
|
||||
are_you_sure: "Está seguro?",
|
||||
yes: "Sí",
|
||||
no: "No",
|
||||
restore: "Restaurar",
|
||||
add_star: "Agregar estrella",
|
||||
remove_star: "Eliminar estrella",
|
||||
nothing_found: "No se encontró nada",
|
||||
replace_current: "Reemplaza el actual",
|
||||
replace_json: "Reemplazar con JSON",
|
||||
preserve_current: "Preservar el actual",
|
||||
import_json: "Importar desde JSON",
|
||||
download_file: "Descargar archivo",
|
||||
upload_file: "Cargar archivo",
|
||||
copy_response: "Copiar respuesta",
|
||||
copy_code: "Copiar codigo",
|
||||
copy_schema: "Copiar Esquema",
|
||||
use_request: "Usar la petición",
|
||||
documentation: "Documentación",
|
||||
docs: "Documentos",
|
||||
reset_default: "Reiniciar valores por defecto",
|
||||
fields: "CAMPOS",
|
||||
deprecated: "OBSOLETO",
|
||||
add_one_header: "(agregar al menos una cabecera)",
|
||||
add_one_parameter: "(agregar al menos un parámetro)",
|
||||
header_count: "cabecera {count}",
|
||||
parameter_count: "parámetro {count}",
|
||||
variable_count: "variable {count}",
|
||||
value_count: "valor {count}",
|
||||
send_request_first: "Enviar primero la petición",
|
||||
generate_docs: "Generar la Documentación",
|
||||
generate_docs_message:
|
||||
"Importar cualquier Colección de Postwoman para Generar la Documentación sobre la marcha.",
|
||||
generate_docs_first: "Generar primero la documentación",
|
||||
docs_generated: "Documentación generada",
|
||||
import_collections: "Importar colecciones",
|
||||
optional: "(opcional)",
|
||||
json: "JSON",
|
||||
none: "Nada",
|
||||
username: "Nombre de usuario",
|
||||
password: "Contrasaeña",
|
||||
token: "Token",
|
||||
payload: "Carga",
|
||||
choose_file: "Seleccione un archivo",
|
||||
file_imported: "Archivo imporado",
|
||||
f12_details: "(F12 para ver detalles)",
|
||||
we_use_cookies: "Usamos las cookies",
|
||||
copied_to_clipboard: "Copiado al portapapeles",
|
||||
finished_in: "Terminado en {duration}ms",
|
||||
check_console_details: "Verifique la consola para más detalles.",
|
||||
download_started: "Inició la descarga",
|
||||
url_invalid_format: "La URL no está formateado apropiadamente",
|
||||
curl_invalid_format: "El cURL no está formateado apropiadamente",
|
||||
enable_proxy: "Pruebe habilitando el Proxy",
|
||||
complete_config_urls: "Por favor, termine la configuración de las urls.",
|
||||
token_request_saved: "La petición de tToken ha sido guardada",
|
||||
donate_info1:
|
||||
"Si le ha gustado su productividad usando Postwoman, considere hacer una donación como un signo de su apreciación.",
|
||||
donate_info2:
|
||||
"Puede ayudar al desarrollo de Postwoman mediante los siguientes métodos:",
|
||||
one_time_recurring: "Una vez o recurrente",
|
||||
one_time: "Una vez",
|
||||
recurring: "Recurrente",
|
||||
wiki: "Wiki",
|
||||
error: "Error",
|
||||
go_home: "Ir al inicio",
|
||||
reload: "Recargar",
|
||||
enter_curl: "Intruduzca cURL",
|
||||
empty: "Vacío",
|
||||
extensions: "Extensiones",
|
||||
extensions_info1: "Extensión del navegador que simplifica el acceso a Postwoman",
|
||||
extensions_info2: "Obtener la extensión del navegador de Postwoman!",
|
||||
installed: "Instalado",
|
||||
login_with: "Iniciar sesión con",
|
||||
logged_out: "Sesión cerreda",
|
||||
logout: "Cerrar sesión",
|
||||
account: "Cuenta",
|
||||
sync: "Sync",
|
||||
syncHistory: "Historial",
|
||||
syncCollections: "Colecciones",
|
||||
turn_on: "Encender",
|
||||
login_first: "Inicie sesión primero",
|
||||
paste_a_collection: "Pegar una Colección",
|
||||
import_from_sync: "Importar desde Sync"
|
||||
};
|
||||
90
lang/fa-IR.js
Normal file
90
lang/fa-IR.js
Normal file
@@ -0,0 +1,90 @@
|
||||
export default {
|
||||
home: "خانه",
|
||||
realtime: "بلادرنگ",
|
||||
graphql: "GraphQL",
|
||||
settings: "تنظیمات",
|
||||
request: "درخواست",
|
||||
install_pwa: "نصب PWA",
|
||||
support_us: "از ما حمایت کنید",
|
||||
tweet: "Tweet",
|
||||
options: "گزینهها",
|
||||
communication: "ارتباط",
|
||||
endpoint: "Endpoint",
|
||||
schema: "Schema",
|
||||
theme: "پوسته",
|
||||
subscribe: "اشتراک",
|
||||
choose_language: "تغییر زبان",
|
||||
shortcuts: "میانبرها",
|
||||
send_request: "ارسال درخواست",
|
||||
save_to_collections: "ذخیره در کلکسیون",
|
||||
copy_request_link: "کپی لینک درخواست",
|
||||
reset_request: "بازنشانی درخواست",
|
||||
support_us_on: "حمایت از ما از طریق",
|
||||
open_collective: "Open Collective",
|
||||
paypal: "PayPal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "کد JavaScript",
|
||||
method: "متد",
|
||||
path: "مسیر",
|
||||
label: "برچسب",
|
||||
again: "دوباره",
|
||||
content_type: "Content Type",
|
||||
raw_input: "ورودی raw",
|
||||
parameter_list: "لیست پارامترها",
|
||||
raw_request_body: "Raw Request Body",
|
||||
show_code: "نمایش کد",
|
||||
hide_code: "عدم نمایش کد",
|
||||
show_prerequest_script: "Show Pre-Request Script",
|
||||
hide_prerequest_script: "Hide Pre-Request Script",
|
||||
authentication: "Authentication",
|
||||
authentication_type: "Authentication type",
|
||||
include_in_url: "در URL گنجانده شود",
|
||||
parameters: "پارامترها",
|
||||
expand_response: "نمایش کامل پاسخ",
|
||||
collapse_response: "نمایش مختصر پاسخ",
|
||||
hide_preview: "مخفی کردن نمایش",
|
||||
preview_html: "نمایش HTML",
|
||||
history: "تاریخچه",
|
||||
collections: "کلکسیون",
|
||||
import_curl: "وارد کردن cURL",
|
||||
import: "وارد کردن",
|
||||
generate_code: "تولید کد",
|
||||
request_type: "Request type",
|
||||
generated_code: "کد تولید شده",
|
||||
status: "Status",
|
||||
headers: "Headers",
|
||||
websocket: "WebSocket",
|
||||
waiting_for_connection: "(منتظر برقراری اتصال)",
|
||||
message: "پیام",
|
||||
sse: "SSE",
|
||||
server: "سرور",
|
||||
events: "رویداد",
|
||||
url: "URL",
|
||||
get_schema: "دریافت Schema",
|
||||
header_list: "لیست Header",
|
||||
add_new: "افزودن",
|
||||
response: "Response",
|
||||
query: "Query",
|
||||
queries: "Queries",
|
||||
query_variables: "Variables",
|
||||
mutations: "Mutations",
|
||||
subscriptions: "Subscriptions",
|
||||
types: "Types",
|
||||
send: "ارسال",
|
||||
background: "پس زمینه",
|
||||
color: "رنگ",
|
||||
labels: "برچسبها",
|
||||
multi_color: "چند رنگی",
|
||||
enabled: "فعال",
|
||||
disabled: "غیر فعال",
|
||||
proxy: "پراکسی",
|
||||
postwoman_official_proxy_hosting:
|
||||
"پراکسی Postwoman برروی هاست ApolloTV قرار دارد.",
|
||||
read_the: "بخوانید",
|
||||
apollotv_privacy_policy: "خط مشی رازداری ApolloTV",
|
||||
contact_us: "Contact us",
|
||||
connect: "Connect",
|
||||
disconnect: "Disconnect",
|
||||
start: "Start",
|
||||
stop: "Stop"
|
||||
};
|
||||
264
lang/fr-FR.js
Normal file
264
lang/fr-FR.js
Normal file
@@ -0,0 +1,264 @@
|
||||
export default {
|
||||
home: "Accueil",
|
||||
realtime: "Temps réel",
|
||||
graphql: "GraphQL",
|
||||
settings: "Paramètres",
|
||||
request: "Request",
|
||||
install_pwa: "Installer la PWA",
|
||||
support_us: "Nous supporter",
|
||||
tweet: "Tweeter",
|
||||
options: "Options",
|
||||
communication: "Communication",
|
||||
endpoint: "Endpoint",
|
||||
schema: "Schéma",
|
||||
theme: "Thème",
|
||||
subscribe: "S'inscrire",
|
||||
choose_language: "Sélectionner une langue",
|
||||
shortcuts: "Raccourcis",
|
||||
send_request: "Envoyer la requête",
|
||||
save_to_collections: "Sauvegarder dans les collections",
|
||||
copy_request_link: "Copier le lien de la requête",
|
||||
reset_request: "Réinitialiser la requête",
|
||||
support_us_on: "Supportez-nous sur",
|
||||
open_collective: "Ouvrir Collective",
|
||||
paypal: "PayPal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "Code JavaScript",
|
||||
method: "Méthode",
|
||||
path: "Chemin d'accès",
|
||||
label: "Libellé",
|
||||
again: "Réessayer",
|
||||
content_type: "Type de contenu",
|
||||
raw_input: "Texte brut",
|
||||
parameter_list: "Liste des paramètres",
|
||||
raw_request_body: "Corps de la requête en texte brut",
|
||||
show_code: "Afficher le code",
|
||||
hide_code: "Masquer le code",
|
||||
show_prerequest_script: "Afficher le script de pré-requête",
|
||||
hide_prerequest_script: "Masquer le script de pré-requête",
|
||||
authentication: "Authentification",
|
||||
authentication_type: "Type d'authentification",
|
||||
include_in_url: "Inclure dans l'URL",
|
||||
parameters: "Paramètres",
|
||||
expand_response: "Agrandir la réponse",
|
||||
collapse_response: "Réduire la réponse",
|
||||
hide_preview: "Masquer la prévisualisation",
|
||||
preview_html: "Prévisualiser le HTML",
|
||||
history: "Historique",
|
||||
collections: "Collections",
|
||||
import_curl: "Importer en cURL",
|
||||
importer: "Importer",
|
||||
generate_code: "Générer le code",
|
||||
request_type: "Type de requête",
|
||||
generated_code: "Code généré",
|
||||
status: "Statut",
|
||||
headers: "En-têtes",
|
||||
websocket: "WebSocket",
|
||||
waiting_for_connection: "(en attente de connexion)",
|
||||
message: "Message",
|
||||
sse: "SSE",
|
||||
server: "Serveur",
|
||||
events: "Évènements",
|
||||
url: "URL",
|
||||
get_schema: "Récuperer le schéma",
|
||||
header_list: "Liste d'en-têtes",
|
||||
add_new: "Ajouter",
|
||||
response: "Réponse",
|
||||
query: "Requête",
|
||||
queries: "Requêtes",
|
||||
query_variables: "Variables",
|
||||
mutations: "Mutations",
|
||||
subscriptions: "Abonnements",
|
||||
types: "Types",
|
||||
send: "Envoyer",
|
||||
background: "Arrière-plan",
|
||||
color: "Couleur",
|
||||
labels: "Libellés",
|
||||
multi_color: "Multi-couleurs",
|
||||
enabled: "Activé",
|
||||
disabled: "Désactivé",
|
||||
proxy: "Proxy",
|
||||
postwoman_official_proxy_hosting:
|
||||
"Le proxy officiel de Postwoman est hébergé par ApolloTV.",
|
||||
read_the: "Lire la",
|
||||
apollotv_privacy_policy: "politique de confidentialité ApolloTV",
|
||||
contact_us: "Contactez nous",
|
||||
connect: "Relier",
|
||||
disconnect: "Déconnecter",
|
||||
start: "Début",
|
||||
stop: "Arrêtez",
|
||||
access_token: "Token d'accès",
|
||||
token_list: "Liste des Tokens",
|
||||
get_token: "Obtenir un nouveau Token",
|
||||
manage_token: "Gérer le Token",
|
||||
save_token: "Sauvegarder le Token",
|
||||
use_token: "Utiliser un Token",
|
||||
request_token: "Demander un Token",
|
||||
save_token_req: "Sauvegarder la requête ",
|
||||
manage_token_req: "Gérer la requête avec Token",
|
||||
use_token_req: "Utiliser la requête avec Token",
|
||||
token_req_name: "Nom de la requête",
|
||||
token_req_details: "Détails de la requête",
|
||||
token_name: "Nom du Token",
|
||||
oidc_discovery_url: "OIDC Discovery URL",
|
||||
auth_url: "Auth URL",
|
||||
access_token_url: "URL du Token d'accès",
|
||||
client_id: "Client ID",
|
||||
scope: "Scope",
|
||||
state: "État",
|
||||
token_req_list: "Liste des requêtes avec Token",
|
||||
no_path: "Aucun chemin",
|
||||
no_label: "Aucun label",
|
||||
prerequest_script: "Pre-Request Script",
|
||||
no_prerequest_script: "No pre-request script",
|
||||
search: "Historique de la recherche",
|
||||
history_empty: "L'historique est vide",
|
||||
history_deleted: "Historique supprimé",
|
||||
clear: "Nettoyer",
|
||||
clear_all: "Tout nettoyer",
|
||||
cleared: "Nettoyé",
|
||||
close: "Fermer",
|
||||
sort: "Trier",
|
||||
time: "Temps",
|
||||
duration: "Durée",
|
||||
no_duration: "Aucune durée",
|
||||
show_more: "Plus d'informations",
|
||||
hide_more: "Moins d'informations",
|
||||
collection: "Collection",
|
||||
current_collection: "Collection Actuelle",
|
||||
select_collection: "Selectionner une Collection",
|
||||
create_collection: "Créer une Collection",
|
||||
new: "Nouveau",
|
||||
import_export: "Importer / Exporter",
|
||||
more: "Plus",
|
||||
folder: "Dossier",
|
||||
new_folder: "Nouveau Dossier",
|
||||
my_new_folder: "Mon Nouveau Dossier",
|
||||
folder_empty: "Ce Dossier est vide",
|
||||
edit_folder: "Éditer le Dossier",
|
||||
edit: "Éditer",
|
||||
delete: "Supprimer",
|
||||
deleted: "Supprimé",
|
||||
undo: "Annuler",
|
||||
collection_empty: "Cette Collection est vide",
|
||||
new_collection: "Nouvelle Collection",
|
||||
my_new_collection: "Ma Nouvelle Collection",
|
||||
edit_collection: "Éditer la Collection",
|
||||
edit_request: "Éditer ma requête",
|
||||
save_request_as: "Sauvegarder ma requête sous",
|
||||
export: "Exporter",
|
||||
connecting_to: "Connexion à {name}...",
|
||||
connected: "Connecté",
|
||||
connected_to: "Connecté à {name}",
|
||||
disconnected: "Déconnecté",
|
||||
disconnected_from: "Déconnecté sur {name}",
|
||||
something_went_wrong: "Quelque chose n'a pas marché!",
|
||||
error_occurred: "Une erreur s'est produite.",
|
||||
browser_support_sse:
|
||||
"Ce navigateur ne semble pas prendre en charge les événements envoyés par le serveur.",
|
||||
log: "Log",
|
||||
no_url: "Aucune URL",
|
||||
run_query: "Lancer une recherche",
|
||||
copy_query: "Copier la recherche",
|
||||
kinda_dark: "Plutôt Sombre",
|
||||
clearly_white: "Clairement Blanc",
|
||||
just_black: "Seulement Noir",
|
||||
auto_system: "Auth (système)",
|
||||
green: "Vert",
|
||||
yellow: "Jaune",
|
||||
pink: "Rose",
|
||||
red: "Rouge",
|
||||
purple: "Violet",
|
||||
orange: "Orange",
|
||||
cyan: "Cyan",
|
||||
blue: "Bleue",
|
||||
loading: "Chargement...",
|
||||
fetching: "Récupération...",
|
||||
waiting_send_req: "(en attente de l'envoi de la demande)",
|
||||
cancel: "Annuler",
|
||||
save: "Sauvarder",
|
||||
dismiss: "Dismiss",
|
||||
are_you_sure: "Êtes-vous sûr?",
|
||||
yes: "Oui",
|
||||
no: "Non",
|
||||
restore: "Restaurer",
|
||||
add_star: "Ajouter une étoile",
|
||||
remove_star: "Supprimer une étoile",
|
||||
nothing_found: "Rien n'a été trouvé",
|
||||
replace_current: "Remplacer l'actuel",
|
||||
replace_json: "Replacer avec du JSON",
|
||||
preserve_current: "Conserver l'actuel",
|
||||
import_json: "Importer depuis un JSON",
|
||||
download_file: "Télécharger un fichier",
|
||||
upload_file: "Charger un fichier",
|
||||
copy_response: "Copier la réponse",
|
||||
copy_code: "Copier le code",
|
||||
copy_schema: "Copier le Schéma",
|
||||
use_request: "Utiliser cette requête",
|
||||
documentation: "Documentation",
|
||||
docs: "Docs",
|
||||
reset_default: "Rétablir la valeur par défaut",
|
||||
fields: "CHAMPS",
|
||||
deprecated: "DÉPRÉCIÉ",
|
||||
add_one_header: "(ajouter au moins un en-tête)",
|
||||
add_one_parameter: "(ajouter au moins un paramètre)",
|
||||
header_count: "{count} en-tête",
|
||||
parameter_count: "{count} paramètre",
|
||||
variable_count: "{count} variable",
|
||||
value_count: "{count} valeur",
|
||||
send_request_first: "Envoyez d'abord une requête",
|
||||
generate_docs: "Genérer une Documentation",
|
||||
generate_docs_message:
|
||||
"Importer n'importe quelle collection de Postwoman pour générer de la documentation en déplacement.",
|
||||
generate_docs_first: "Générer la documentation d'abord",
|
||||
docs_generated: "Documentation générée",
|
||||
import_collections: "Importer les collections",
|
||||
optional: "(optionnel)",
|
||||
json: "JSON",
|
||||
none: "Aucun",
|
||||
username: "Username",
|
||||
password: "Password",
|
||||
token: "Token",
|
||||
payload: "Payload",
|
||||
choose_file: "Choisir un fichier",
|
||||
file_imported: "Fichier importé",
|
||||
f12_details: "(F12 pour voir les détails)",
|
||||
we_use_cookies: "Nous utilisons des cookies",
|
||||
copied_to_clipboard: "Copié dans le presse-papier",
|
||||
finished_in: "Fini en {duration}ms",
|
||||
check_console_details: "Consultez la console pour plus de détails.",
|
||||
download_started: "Téléchargement démarré",
|
||||
url_invalid_format: "L'URL n'est pas formatée correctement",
|
||||
curl_invalid_format: "cURL n'est pas formaté correctement",
|
||||
enable_proxy: "Essayez en activant le Proxy",
|
||||
complete_config_urls: "Veuillez compléter les urls de configuration.",
|
||||
token_request_saved: "Requête de Token sauvegardé",
|
||||
donate_info1:
|
||||
"Si vous avez apprécié la productivité de l'utilisation de Postwoman, considérez le don comme un signe d'appréciation.",
|
||||
donate_info2:
|
||||
"Vous pouvez soutenir le développement de Postwoman par les méthodes suivantes :",
|
||||
one_time_recurring: "Ponctuel ou récurrent",
|
||||
one_time: "Une fois",
|
||||
recurring: "Récurrent",
|
||||
wiki: "Wiki",
|
||||
error: "Erreur",
|
||||
go_home: "Aller à l'accueil",
|
||||
reload: "Recharger",
|
||||
enter_curl: "Entrer cURL",
|
||||
empty: "Vide",
|
||||
extensions: "Extensions",
|
||||
extensions_info1: "Extension pour navigateur qui simplifie l'accès à Postwoman",
|
||||
extensions_info2: "Obtenez l'extension Postwoman pour navigateur !",
|
||||
installed: "Installé",
|
||||
login_with: "Se connecter avec",
|
||||
logged_out: "Se déconnecter",
|
||||
logout: "Déconnexion",
|
||||
account: "Compte",
|
||||
sync: "Synchroniser",
|
||||
syncHistory: "Historique",
|
||||
syncCollections: "Collections",
|
||||
turn_on: "Activer",
|
||||
login_first: "Se connecter d'abord",
|
||||
paste_a_collection: "Coller une collection",
|
||||
import_from_sync: "Importer depuis la synchronisation"
|
||||
};
|
||||
90
lang/id-ID.js
Normal file
90
lang/id-ID.js
Normal file
@@ -0,0 +1,90 @@
|
||||
export default {
|
||||
home: "Beranda",
|
||||
realtime: "Waktu Nyata",
|
||||
graphql: "GraphQL",
|
||||
settings: "Pengaturan",
|
||||
request: "Permintaan",
|
||||
install_pwa: "Pasang PWA",
|
||||
support_us: "Dukung kami",
|
||||
tweet: "Cuitkan",
|
||||
options: "Opsi",
|
||||
communication: "Komunikasi",
|
||||
endpoint: "Titik Akhir",
|
||||
schema: "Skema",
|
||||
theme: "Tema",
|
||||
subscribe: "Berlangganan",
|
||||
choose_language: "Pilih Bahasa",
|
||||
shortcuts: "Pintasan",
|
||||
send_request: "Kirim Permintaan",
|
||||
save_to_collections: "Simpan ke Koleksi",
|
||||
copy_request_link: "Salin Tautan Permintaan",
|
||||
reset_request: "Atur Ulang Permintaan",
|
||||
support_us_on: "Dukung kami di",
|
||||
open_collective: "Open Collective",
|
||||
paypal: "Paypal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "Kode Javascript",
|
||||
method: "Metode",
|
||||
path: "Lintasan",
|
||||
label: "Label",
|
||||
again: "Lagi",
|
||||
content_type: "Jenis Konten",
|
||||
raw_input: "Masukan mentah",
|
||||
parameter_list: "Daftar parameter",
|
||||
raw_request_body: "Badan Permintaan Mentah",
|
||||
show_code: "Tampilkan Kode",
|
||||
hide_code: "Sembunyikan Kode",
|
||||
show_prerequest_script: "Tampilkan Skrip Pra-Permintaan",
|
||||
hide_prerequest_script: "Sembunyikan Skrip Pra-Permintaan",
|
||||
authentication: "Autentikasi",
|
||||
authentication_type: "Jenis Autentikasi",
|
||||
include_in_url: "Sertakan di URL",
|
||||
parameters: "Parameter",
|
||||
expand_response: "Bentangkan Balasan",
|
||||
collapse_response: "Ciutkan Balasan",
|
||||
hide_preview: "Sembunyikan Pratinjau",
|
||||
preview_html: "Pratinjau HTML",
|
||||
history: "Riwayat",
|
||||
collections: "Koleksi",
|
||||
import_curl: "Impor cURL",
|
||||
import: "Impor",
|
||||
generate_code: "Hasilkan kode",
|
||||
request_type: "Jenis permintaan",
|
||||
generated_code: "Kode yang dihasilkan",
|
||||
status: "Status",
|
||||
headers: "Header",
|
||||
websocket: "WebSocket",
|
||||
waiting_for_connection: "(Menunggu sambungan)",
|
||||
message: "Pesan",
|
||||
sse: "SSE",
|
||||
server: "Peladen",
|
||||
events: "Kejadian",
|
||||
url: "URL",
|
||||
get_schema: "Ambil skema",
|
||||
header_list: "Daftar header",
|
||||
add_new: "Tambah baru",
|
||||
response: "Balasan",
|
||||
query: "Kueri",
|
||||
queries: "Kueri",
|
||||
query_variables: "Variables",
|
||||
mutations: "Mutasi",
|
||||
subscriptions: "Langganan",
|
||||
types: "Jenis",
|
||||
send: "Kirim",
|
||||
background: "Latar belakang",
|
||||
color: "Warna",
|
||||
labels: "Label",
|
||||
multi_color: "Warna beragam",
|
||||
enabled: "diaktifkan",
|
||||
disabled: "dinonaktifkan",
|
||||
proxy: "Proksi",
|
||||
postwoman_official_proxy_hosting:
|
||||
"Proksi Resmi Postwoman dalam penginangan ApolloTV.",
|
||||
read_the: "Bacalah",
|
||||
apollotv_privacy_policy: "kebijakan privasi ApolloTV",
|
||||
contact_us: "Hubungi kami",
|
||||
connect: "Menghubungkan",
|
||||
disconnect: "Memutuskan",
|
||||
start: "Mulai",
|
||||
stop: "Berhenti"
|
||||
};
|
||||
245
lang/ja-JP.js
Normal file
245
lang/ja-JP.js
Normal file
@@ -0,0 +1,245 @@
|
||||
export default {
|
||||
home: "ホーム",
|
||||
realtime: "リアルタイム",
|
||||
graphql: "GraphQL",
|
||||
settings: "設定",
|
||||
request: "リクエスト",
|
||||
install_pwa: "PWAをインストール",
|
||||
support_us: "寄付",
|
||||
tweet: "ツイート",
|
||||
options: "オプション",
|
||||
communication: "通信",
|
||||
endpoint: "エンドポイント",
|
||||
schema: "スキーマ",
|
||||
theme: "テーマ",
|
||||
subscribe: "登録",
|
||||
choose_language: "言語の選択",
|
||||
shortcuts: "ショートカット",
|
||||
send_request: "リクエストを送信",
|
||||
save_to_collections: "コレクションに保存",
|
||||
copy_request_link: "リクエストURLをコピー",
|
||||
reset_request: "リクエストをリセット",
|
||||
support_us_on: "以下より寄付",
|
||||
open_collective: "Open Collective",
|
||||
paypal: "PayPal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "JavaScriptコード",
|
||||
method: "メソッド",
|
||||
path: "パス",
|
||||
label: "ラベル",
|
||||
again: "",
|
||||
content_type: "Content Type",
|
||||
raw_input: "Raw入力",
|
||||
parameter_list: "パラメータリスト",
|
||||
raw_request_body: "Rawリクエストボディー",
|
||||
show_code: "コードを表示",
|
||||
hide_code: "コードを非表示",
|
||||
show_prerequest_script: "プレリクエストスクリプトを表示",
|
||||
hide_prerequest_script: "プレリクエストスクリプトを非表示",
|
||||
authentication: "認証",
|
||||
authentication_type: "認証タイプ",
|
||||
include_in_url: "URLに含む",
|
||||
parameters: "パラメータ",
|
||||
expand_response: "レスポンスを広げる",
|
||||
collapse_response: "レスポンスを狭める",
|
||||
hide_preview: "プレビューしない",
|
||||
preview_html: "HTMLプレビュー表示",
|
||||
history: "履歴",
|
||||
collections: "コレクション",
|
||||
import_curl: "cURLをインポート",
|
||||
import: "インポート",
|
||||
generate_code: "コード生成",
|
||||
request_type: "リクエストタイプ",
|
||||
generated_code: "生成されたコード",
|
||||
status: "ステータス",
|
||||
headers: "ヘッダー",
|
||||
websocket: "ウェブソケット",
|
||||
waiting_for_connection: "(接続待ち)",
|
||||
message: "メッセージ",
|
||||
sse: "SSE",
|
||||
server: "サーバ",
|
||||
events: "イベント",
|
||||
url: "URL",
|
||||
get_schema: "スキーマを取得",
|
||||
header_list: "ヘッダーリスト",
|
||||
add_new: "追加",
|
||||
response: "レスポンス",
|
||||
query: "クエリ",
|
||||
queries: "クエリ",
|
||||
query_variables: "変数",
|
||||
mutations: "ミューテーション",
|
||||
subscriptions: "サブスクリプション",
|
||||
types: "タイプ",
|
||||
send: "送信",
|
||||
background: "背景",
|
||||
color: "色",
|
||||
labels: "ラベル",
|
||||
multi_color: "マルチカラー",
|
||||
enabled: "有効",
|
||||
disabled: "無効",
|
||||
proxy: "プロキシ",
|
||||
postwoman_official_proxy_hosting:
|
||||
"Postwomanの公式プロキシは、Apollo TVがホストしています。",
|
||||
read_the: "プライバシーポリシー",
|
||||
apollotv_privacy_policy: "を読む",
|
||||
contact_us: "お問い合わせ",
|
||||
connect: "接続",
|
||||
disconnect: "切断",
|
||||
start: "開始",
|
||||
stop: "停止",
|
||||
access_token: "アクセストークン",
|
||||
token_list: "トークンリスト",
|
||||
get_token: "新しいトークンを取得",
|
||||
manage_token: "アクセストークンを管理",
|
||||
save_token: "アクセストークンを保存",
|
||||
use_token: "アクセストークンを使用",
|
||||
request_token: "トークンをリクエスト",
|
||||
save_token_req: "トークンリクエストを保存",
|
||||
manage_token_req: "トークンリクエストを管理",
|
||||
use_token_req: "トークンリクエストを使用",
|
||||
token_req_name: "リクエスト名",
|
||||
token_req_details: "リクエスト詳細",
|
||||
token_name: "トークン名",
|
||||
oidc_discovery_url: "OIDC Discovery URL",
|
||||
auth_url: "認証URL",
|
||||
access_token_url: "アクセストークンURL",
|
||||
client_id: "クライアントID",
|
||||
scope: "スコープ",
|
||||
state: "ステート",
|
||||
token_req_list: "トークンリクエストリスト",
|
||||
no_path: "パス無し",
|
||||
no_label: "ラベル無し",
|
||||
prerequest_script: "プレリクエストスクリプト",
|
||||
no_prerequest_script: "プレリクエストスクリプト無し",
|
||||
search: "検索履歴",
|
||||
history_empty: "履歴が空です",
|
||||
history_deleted: "履歴が削除された",
|
||||
clear: "クリア",
|
||||
clear_all: "全てクリア",
|
||||
cleared: "クリアされた",
|
||||
close: "閉じる",
|
||||
sort: "ソート",
|
||||
time: "時間",
|
||||
duration: "期間",
|
||||
no_duration: "期間なし",
|
||||
show_more: "もっと表示する",
|
||||
hide_more: "隠す",
|
||||
collection: "コレクション",
|
||||
current_collection: "現在のコレクション",
|
||||
select_collection: "コレクションを選択",
|
||||
create_collection: "コレクションを作成",
|
||||
new: "新規",
|
||||
import_export: "インポート・エクスポート",
|
||||
more: "More",
|
||||
folder: "フォルダ",
|
||||
new_folder: "新しいフォルダー",
|
||||
my_new_folder: "私の新しいフォルダー",
|
||||
folder_empty: "フォルダーが空です",
|
||||
edit_folder: "フォルダーを編集",
|
||||
edit: "編集",
|
||||
delete: "削除",
|
||||
deleted: "削除された",
|
||||
undo: "元に戻す",
|
||||
collection_empty: "コレクションが空です",
|
||||
new_collection: "新しいコレクション",
|
||||
my_new_collection: "私の新しいコレクション",
|
||||
edit_collection: "コレクションを編集",
|
||||
edit_request: "リクエストを編集",
|
||||
save_request_as: "名前を付けてリクエストを保存",
|
||||
export: "エクスポート",
|
||||
connecting_to: "{name}に接続中...",
|
||||
connected: "接続した",
|
||||
connected_to: "{name}に接続した",
|
||||
disconnected: "切断された",
|
||||
disconnected_from: "{name}から切断された",
|
||||
something_went_wrong: "何かの問題が起きた",
|
||||
error_occurred: "エラーが発生した",
|
||||
browser_support_sse: "このブラウザはサーバー送信イベントのサポートがないようです。",
|
||||
log: "ログ",
|
||||
no_url: "URL無し",
|
||||
run_query: "クエリを実行",
|
||||
copy_query: "クエリをコピー",
|
||||
kinda_dark: "ちょっと暗い",
|
||||
clearly_white: "明らかに白",
|
||||
just_black: "ただの黒",
|
||||
auto_system: "オート(システム)",
|
||||
green: "緑",
|
||||
yellow: "黄",
|
||||
pink: "ピンク",
|
||||
red: "赤",
|
||||
purple: "紫",
|
||||
orange: "オレンジ",
|
||||
cyan: "シヤン",
|
||||
blue: "青",
|
||||
loading: "ロード中...",
|
||||
fetching: "フェッチ中...",
|
||||
waiting_send_req: "(リクエスト送信待ち)",
|
||||
cancel: "キャンセル",
|
||||
save: "保存",
|
||||
dismiss: "Dismiss",
|
||||
are_you_sure: "よろしいですか?",
|
||||
yes: "はい",
|
||||
no: "いいえ",
|
||||
restore: "リストア",
|
||||
add_star: "星を付ける",
|
||||
remove_star: "星を外す",
|
||||
nothing_found: "何も見つからない",
|
||||
replace_current: "置換",
|
||||
replace_json: "JSONに置換",
|
||||
preserve_current: "保持",
|
||||
import_json: "JSONをインポート",
|
||||
download_file: "ファイルをダウンロード",
|
||||
upload_file: "ファイルをアップロード",
|
||||
copy_response: "レスポンスをコピー",
|
||||
copy_code: "コードをコピー",
|
||||
copy_schema: "スキーマをコピー",
|
||||
use_request: "リクエストを使用",
|
||||
documentation: "ドキュメンテーション",
|
||||
docs: "ドキュメント",
|
||||
reset_default: "デフォルトにリセット",
|
||||
fields: "FIELDS",
|
||||
deprecated: "DEPRECATED",
|
||||
add_one_header: "(ヘッダーを少なくとも1つ追加してください)",
|
||||
add_one_parameter: "(パラメータを少なくとも1つ追加してください)",
|
||||
header_count: "ヘッダー {count}",
|
||||
parameter_count: "パラメータ {count}",
|
||||
variable_count: "変数 {count}",
|
||||
value_count: "値 {count}",
|
||||
send_request_first: "リクエストを先に送信してください",
|
||||
generate_docs: "ドキュメンテーションを生成",
|
||||
generate_docs_message: "Postwomanのコレクションをインポートし、直ちにドキュメンテーションを生成",
|
||||
generate_docs_first: "ドキュメントを先に生成してください",
|
||||
docs_generated: "ドキュメンテーションを生成した",
|
||||
import_collections: "コレクションをインポート",
|
||||
optional: "(オプション)",
|
||||
json: "JSON",
|
||||
none: "なし",
|
||||
username: "ユーザー名",
|
||||
password: "パスワード",
|
||||
token: "トークン",
|
||||
payload: "ペイロード",
|
||||
choose_file: "ファイルを選択",
|
||||
file_imported: "ファイルをインポートした",
|
||||
f12_details: "(F12を押して詳細を確認してください)",
|
||||
we_use_cookies: "クッキーを使用します。",
|
||||
copied_to_clipboard: "クリップボードにコピーした",
|
||||
finished_in: "{duration}msで終了した",
|
||||
check_console_details: "コンソールより詳細を確認してください",
|
||||
download_started: "ダウンロードを開始した",
|
||||
url_invalid_format: "URLが正しくフォーマットされていない",
|
||||
curl_invalid_format: "cURLが正しくフォーマットされていない",
|
||||
enable_proxy: "プロキシを有効にしてみてください",
|
||||
complete_config_urls: "設定URLsを入力してください",
|
||||
token_request_saved: "トークンリクエストを保存した",
|
||||
donate_info1: "Postwomanを非常に役に立つと思われる場合、感謝の印として寄付のご検討をお願いします。",
|
||||
donate_info2: "以下の方法でPostwomanの開発をサポートできます:",
|
||||
one_time_recurring: "一度又は定期的",
|
||||
one_time: "一度",
|
||||
recurring: "定期的",
|
||||
wiki: "Wiki",
|
||||
error: "エラー",
|
||||
go_home: "ホームに戻る",
|
||||
reload: "リロード",
|
||||
enter_curl: "cURLを入力",
|
||||
empty: "空"
|
||||
};
|
||||
89
lang/pt-BR.js
Normal file
89
lang/pt-BR.js
Normal file
@@ -0,0 +1,89 @@
|
||||
export default {
|
||||
home: "Home",
|
||||
realtime: "Tempo real",
|
||||
graphql: "GraphQL",
|
||||
settings: "Configurações",
|
||||
request: "Request",
|
||||
install_pwa: "Instalar PWA",
|
||||
support_us: "Nos ajude",
|
||||
tweet: "Tweet",
|
||||
options: "Opções",
|
||||
communication: "Comunicação",
|
||||
endpoint: "Endpoint",
|
||||
schema: "Schema",
|
||||
theme: "Tema",
|
||||
subscribe: "Subscribe",
|
||||
choose_language: "Escolher idioma",
|
||||
shortcuts: "Atalhos",
|
||||
send_request: "Enviar request",
|
||||
save_to_collections: "Salvar nas coleções",
|
||||
copy_request_link: "Copiar link da request",
|
||||
reset_request: "Reiniciar request",
|
||||
support_us_on: "Nos ajude no",
|
||||
open_collective: "Abrir coletivamente",
|
||||
paypal: "Paypal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "Codigo JavaScript",
|
||||
method: "Método",
|
||||
path: "Caminho",
|
||||
label: "Label",
|
||||
again: "Novamente",
|
||||
content_type: "Content Type",
|
||||
raw_input: "Raw input",
|
||||
parameter_list: "Lista de parâmetros",
|
||||
raw_request_body: "Raw request body",
|
||||
show_code: "Mostrar código",
|
||||
hide_code: "Esconder código",
|
||||
show_prerequest_script: "Mostrar script de pré-request",
|
||||
hide_prerequest_script: "Esconder script de pré-request",
|
||||
authentication: "Autenticação",
|
||||
authentication_type: "Tipo de autenticação",
|
||||
include_in_url: "Incluir na url",
|
||||
parameters: "Parâmetros",
|
||||
expand_response: "Expandir response",
|
||||
collapse_response: "Esconder response",
|
||||
hide_preview: "Esconder preview",
|
||||
preview_html: "Preview html",
|
||||
history: "Histórico",
|
||||
collections: "Coleções",
|
||||
import_curl: "Importar curl",
|
||||
import: "Importar",
|
||||
generate_code: "Gerar código",
|
||||
request_type: "Tipo de request",
|
||||
generated_code: "Código gerado",
|
||||
status: "Status",
|
||||
headers: "Headers",
|
||||
websocket: "Websocket",
|
||||
waiting_for_connection: "(aguardando conexão)",
|
||||
message: "Mensagem",
|
||||
sse: "SSE",
|
||||
server: "Servidor",
|
||||
events: "Eventos",
|
||||
url: "URL",
|
||||
get_schema: "Get schema",
|
||||
header_list: "Lista de headers",
|
||||
add_new: "Adicionar novo",
|
||||
response: "Response",
|
||||
query: "Query",
|
||||
queries: "Queries",
|
||||
query_variables: "Variáveis",
|
||||
mutations: "Mutações",
|
||||
subscriptions: "Assinaturas",
|
||||
types: "Tipos",
|
||||
send: "Enviar",
|
||||
background: "Fundo",
|
||||
color: "Cor",
|
||||
labels: "Labels",
|
||||
multi_color: "Multi cor",
|
||||
enabled: "Ativado",
|
||||
disabled: "Desativado",
|
||||
proxy: "Proxy",
|
||||
postwoman_official_proxy_hosting: "Postwoman's alojamento proxy oficial",
|
||||
read_the: "Leia o",
|
||||
apollotv_privacy_policy: "ApolloTV Política de Privacidade",
|
||||
contact_us: "Contate-Nos",
|
||||
connect: "Conectar",
|
||||
disconnect: "Desconectar",
|
||||
start: "Começar",
|
||||
stop: "Pare"
|
||||
};
|
||||
90
lang/tr-TR.js
Normal file
90
lang/tr-TR.js
Normal file
@@ -0,0 +1,90 @@
|
||||
export default {
|
||||
home: "Ana Sayfa",
|
||||
realtime: "Realtime",
|
||||
graphql: "GraphQL",
|
||||
settings: "Ayarlar",
|
||||
request: "İstek",
|
||||
install_pwa: "PWA yükle",
|
||||
support_us: "Bize destek ol",
|
||||
tweet: "Tweet",
|
||||
options: "Options",
|
||||
communication: "İletişim",
|
||||
endpoint: "Endpoint",
|
||||
schema: "Taslak",
|
||||
theme: "Tema",
|
||||
subscribe: "Abonelik",
|
||||
choose_language: "Dil seç",
|
||||
shortcuts: "Kısayollar",
|
||||
send_request: "İstek gönder",
|
||||
save_to_collections: "Koleksiyonları kaydet",
|
||||
copy_request_link: "İstek adresini kopyala",
|
||||
reset_request: "İstekleri resetle",
|
||||
support_us_on: "Bizi destekle",
|
||||
open_collective: "Open Collective",
|
||||
paypal: "PayPal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "JavaScript code",
|
||||
method: "Metot",
|
||||
path: "Yol",
|
||||
label: "Etiket",
|
||||
again: "Yeniden",
|
||||
content_type: "İçerik tipi",
|
||||
raw_input: "Raw giriş",
|
||||
parameter_list: "Parametre listesi",
|
||||
raw_request_body: "Raw istek içeriği",
|
||||
show_code: "Kodu göster",
|
||||
hide_code: "Kodu gizle",
|
||||
show_prerequest_script: "Pre-Request scriptini göster",
|
||||
hide_prerequest_script: "Pre-Request scriptini gizle",
|
||||
authentication: "Authentication",
|
||||
authentication_type: "Authentication tipi",
|
||||
include_in_url: "URL'den içeri aktar",
|
||||
parameters: "Parametre",
|
||||
expand_response: "Cevabı genişlet",
|
||||
collapse_response: "Cevap daralt",
|
||||
hide_preview: "Görüntülemeyi gizle",
|
||||
preview_html: "HTML formatında görüntüle",
|
||||
history: "Geçmiş",
|
||||
collections: "Koleksiyonlar",
|
||||
import_curl: "cURL içeri aktar",
|
||||
import: "İçeri Aktar",
|
||||
generate_code: "Kod Üret",
|
||||
request_type: "Request tipi",
|
||||
generated_code: "Üretilen Kod",
|
||||
status: "Durum",
|
||||
headers: "Headers",
|
||||
websocket: "WebSocket",
|
||||
waiting_for_connection: "(esperando por conexión)",
|
||||
message: "Mesaj",
|
||||
sse: "SSE",
|
||||
server: "Server",
|
||||
events: "Events",
|
||||
url: "URL",
|
||||
get_schema: "Taslak",
|
||||
header_list: "Header listesi",
|
||||
add_new: "Yeni Ekle",
|
||||
response: "Cevap",
|
||||
query: "Sorgu",
|
||||
queries: "Sorgular",
|
||||
query_variables: "Değişkenler",
|
||||
mutations: "Değişimler",
|
||||
subscriptions: "Aboneler",
|
||||
types: "Tipler",
|
||||
send: "Gönder",
|
||||
background: "Arka Plan",
|
||||
color: "Renk",
|
||||
labels: "Etiketler",
|
||||
multi_color: "Çoklu renk",
|
||||
enabled: "Aktif",
|
||||
disabled: "Aktif değil",
|
||||
proxy: "Proxy",
|
||||
postwoman_official_proxy_hosting:
|
||||
"Proxy Oficial de Postwoman está hospedado en ApolloTV.",
|
||||
read_the: "Leer la",
|
||||
apollotv_privacy_policy: "ApolloTV gizlilik politikaları",
|
||||
contact_us: "Bizimle iletişime geçin",
|
||||
connect: "Bağlan",
|
||||
disconnect: "Kesmek",
|
||||
start: "Başla",
|
||||
stop: "Durdurmak"
|
||||
};
|
||||
89
lang/zh-CN.js
Normal file
89
lang/zh-CN.js
Normal file
@@ -0,0 +1,89 @@
|
||||
export default {
|
||||
home: "主页",
|
||||
realtime: "长连接",
|
||||
graphql: "GraphQL",
|
||||
settings: "设置",
|
||||
request: "请求",
|
||||
install_pwa: "安装PWA应用",
|
||||
support_us: "支持我们",
|
||||
tweet: "推特",
|
||||
options: "选项",
|
||||
communication: "联系我们",
|
||||
endpoint: "服务端点",
|
||||
schema: "模式",
|
||||
theme: "主题",
|
||||
subscribe: "订阅",
|
||||
choose_language: "选择语言",
|
||||
shortcuts: "快捷键",
|
||||
send_request: "发送请求",
|
||||
save_to_collections: "保存到收藏夹",
|
||||
copy_request_link: "复制请求链接",
|
||||
reset_request: "重置请求",
|
||||
support_us_on: "支持我们",
|
||||
open_collective: "Open Collective",
|
||||
paypal: "Paypal",
|
||||
patreon: "Patreon",
|
||||
javascript_code: "JavaScript代码",
|
||||
method: "方法",
|
||||
path: "路径",
|
||||
label: "标签",
|
||||
again: "重试",
|
||||
content_type: "内容类型",
|
||||
raw_input: "raw数据",
|
||||
parameter_list: "参数列表",
|
||||
raw_request_body: "raw请求主体",
|
||||
show_code: "显示代码",
|
||||
hide_code: "隐藏代码",
|
||||
show_prerequest_script: "显示预请求脚本",
|
||||
hide_prerequest_script: "隐藏预请求脚本",
|
||||
authentication: "认证方式",
|
||||
authentication_type: "认证类型",
|
||||
include_in_url: "包含在URL中",
|
||||
parameters: "参数",
|
||||
expand_response: "展开显示响应内容",
|
||||
collapse_response: "折叠显示响应内容",
|
||||
hide_preview: "隐藏预览",
|
||||
preview_html: "预览HTML",
|
||||
history: "历史记录",
|
||||
collections: "收藏夹",
|
||||
import_curl: "批量导入",
|
||||
import: "导入",
|
||||
generate_code: "生成代码",
|
||||
request_type: "请求类型",
|
||||
generated_code: "生成的代码",
|
||||
status: "状态码",
|
||||
headers: "请求头",
|
||||
websocket: "Websocket",
|
||||
waiting_for_connection: "(等待连接)",
|
||||
message: "消息内容",
|
||||
sse: "SSE",
|
||||
server: "Server",
|
||||
events: "事件",
|
||||
url: "地址",
|
||||
get_schema: "获取模式",
|
||||
header_list: "请求头列表",
|
||||
add_new: "添加",
|
||||
response: "响应",
|
||||
query: "查询",
|
||||
queries: "查询",
|
||||
query_variables: "变数",
|
||||
mutations: "Mutations",
|
||||
subscriptions: "订阅",
|
||||
types: "种类",
|
||||
send: "发送",
|
||||
background: "背景",
|
||||
color: "颜色",
|
||||
labels: "标签",
|
||||
multi_color: "彩色",
|
||||
enabled: "已启用",
|
||||
disabled: "已禁用",
|
||||
proxy: "代理",
|
||||
postwoman_official_proxy_hosting: "Postwoman的官方代理由ApolloTV托管",
|
||||
read_the: "阅读",
|
||||
apollotv_privacy_policy: "ApolloTV隐私政策",
|
||||
contact_us: "联系我们",
|
||||
connect: "连接",
|
||||
disconnect: "断开",
|
||||
start: "开始",
|
||||
stop: "停止"
|
||||
};
|
||||
@@ -1,21 +1,5 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<header class="header">
|
||||
<div>
|
||||
<div class="slide-in">
|
||||
<nuxt-link to="/">
|
||||
<h1 class="logo">Postwoman</h1>
|
||||
</nuxt-link>
|
||||
<h3 class="tagline">API request builder</h3>
|
||||
</div>
|
||||
<a href="https://github.com/liyasthomas/postwoman" target="_blank" rel="noopener">
|
||||
<button class="icon">
|
||||
<img id="imgGitHub" src="~static/icons/github.svg" alt="GitHub" :style="logoStyle()" />
|
||||
<span>GitHub</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<div class="columns">
|
||||
<aside class="nav-first">
|
||||
@@ -24,82 +8,252 @@
|
||||
We're using manual checks for linkActive because the query string
|
||||
seems to mess up the nuxt-link active class.
|
||||
-->
|
||||
<nuxt-link to="/" :class="linkActive('/')" v-tooltip.right="'Home'" aria-label="Home">
|
||||
<logo alt style="height: 24px;"></logo>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
to="/websocket"
|
||||
:class="linkActive('/websocket')"
|
||||
v-tooltip.right="'WebSocket'"
|
||||
:to="localePath('index')"
|
||||
:class="linkActive('/')"
|
||||
v-tooltip.right="$t('home')"
|
||||
:aria-label="$t('home')"
|
||||
>
|
||||
<i class="material-icons">cloud</i>
|
||||
<logo alt class="material-icons" style="height: 24px;"></logo>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
to="/settings"
|
||||
:to="localePath('realtime')"
|
||||
:class="linkActive('/realtime')"
|
||||
v-tooltip.right="$t('realtime')"
|
||||
>
|
||||
<i class="material-icons">settings_input_hdmi</i>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
:to="localePath('graphql')"
|
||||
:class="linkActive('/graphql')"
|
||||
v-tooltip.right="$t('graphql')"
|
||||
:aria-label="$t('graphql')"
|
||||
>
|
||||
<svg
|
||||
class="material-icons"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 400 400"
|
||||
>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="122"
|
||||
y="-0.4"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 163.3196 363.3136)"
|
||||
width="16.6"
|
||||
height="320.3"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="39.8" y="272.2" width="320.3" height="16.6" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="37.9"
|
||||
y="312.2"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 83.0693 663.3409)"
|
||||
width="185"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="177.1"
|
||||
y="71.1"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 463.3409 283.0693)"
|
||||
width="185"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="122.1"
|
||||
y="-13"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 126.7903 232.1221)"
|
||||
width="16.6"
|
||||
height="185"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="109.6"
|
||||
y="151.6"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 266.0828 473.3766)"
|
||||
width="320.3"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g><rect x="52.5" y="107.5" width="16.6" height="185" /></g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="330.9" y="107.5" width="16.6" height="185" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="262.4"
|
||||
y="240.1"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 126.7953 714.2875)"
|
||||
width="14.5"
|
||||
height="160.9"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
d="M369.5,297.9c-9.6,16.7-31,22.4-47.7,12.8c-16.7-9.6-22.4-31-12.8-47.7c9.6-16.7,31-22.4,47.7-12.8C373.5,259.9,379.2,281.2,369.5,297.9"
|
||||
/>
|
||||
<path
|
||||
d="M90.9,137c-9.6,16.7-31,22.4-47.7,12.8c-16.7-9.6-22.4-31-12.8-47.7c9.6-16.7,31-22.4,47.7-12.8C94.8,99,100.5,120.3,90.9,137"
|
||||
/>
|
||||
<path
|
||||
d="M30.5,297.9c-9.6-16.7-3.9-38,12.8-47.7c16.7-9.6,38-3.9,47.7,12.8c9.6,16.7,3.9,38-12.8,47.7C61.4,320.3,40.1,314.6,30.5,297.9"
|
||||
/>
|
||||
<path
|
||||
d="M309.1,137c-9.6-16.7-3.9-38,12.8-47.7c16.7-9.6,38-3.9,47.7,12.8c9.6,16.7,3.9,38-12.8,47.7C340.1,159.4,318.7,153.7,309.1,137"
|
||||
/>
|
||||
<path
|
||||
d="M200,395.8c-19.3,0-34.9-15.6-34.9-34.9c0-19.3,15.6-34.9,34.9-34.9c19.3,0,34.9,15.6,34.9,34.9C234.9,380.1,219.3,395.8,200,395.8"
|
||||
/>
|
||||
<path
|
||||
d="M200,74c-19.3,0-34.9-15.6-34.9-34.9c0-19.3,15.6-34.9,34.9-34.9c19.3,0,34.9,15.6,34.9,34.9C234.9,58.4,219.3,74,200,74"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
:to="localePath('doc')"
|
||||
:class="linkActive('/doc')"
|
||||
v-tooltip.right="$t('documentation')"
|
||||
:aria-label="$t('documentation')"
|
||||
>
|
||||
<i class="material-icons">books</i>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
:to="localePath('settings')"
|
||||
:class="linkActive('/settings')"
|
||||
v-tooltip.right="'Settings'"
|
||||
aria-label="Settings"
|
||||
v-tooltip.right="$t('settings')"
|
||||
:aria-label="$t('settings')"
|
||||
>
|
||||
<i class="material-icons">settings</i>
|
||||
</nuxt-link>
|
||||
</nav>
|
||||
<div v-if="['/'].includes($route.path)">
|
||||
<div v-if="$route.path === '/'">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#request" v-tooltip.right="'Request'">
|
||||
<a href="#request" v-tooltip.right="$t('request')">
|
||||
<i class="material-icons">cloud_upload</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#options" v-tooltip.right="'Options'">
|
||||
<a href="#options" v-tooltip.right="$t('options')">
|
||||
<i class="material-icons">toc</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#response" v-tooltip.right="'Response'">
|
||||
<a href="#response" v-tooltip.right="$t('response')">
|
||||
<i class="material-icons">cloud_download</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#collections" v-tooltip.right="'Collections'">
|
||||
<i class="material-icons">folder_special</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#history" v-tooltip.right="'History'">
|
||||
<i class="material-icons">watch_later</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div v-else-if="['/websocket'].includes($route.path)">
|
||||
<div v-else-if="$route.path === '/realtime'">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#request" v-tooltip.right="'Request'">
|
||||
<a href="#request" v-tooltip.right="$t('request')">
|
||||
<i class="material-icons">cloud_upload</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#response" v-tooltip.right="'Response'">
|
||||
<a href="#response" v-tooltip.right="$t('communication')">
|
||||
<i class="material-icons">cloud_download</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div v-else-if="['/settings'].includes($route.path)">
|
||||
<div v-else-if="$route.path === '/graphql'">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#theme" v-tooltip.right="'Theme'">
|
||||
<a href="#endpoint" v-tooltip.right="$t('endpoint')">
|
||||
<i class="material-icons">cloud</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#schema" v-tooltip.right="$t('schema')">
|
||||
<i class="material-icons">assignment_returned</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#query" v-tooltip.right="$t('query')">
|
||||
<i class="material-icons">cloud_upload</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#response" v-tooltip.right="$t('response')">
|
||||
<i class="material-icons">cloud_download</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div v-else-if="$route.path === '/doc'">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#collections" v-tooltip.right="$t('collections')">
|
||||
<i class="material-icons">folder</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#documentation" v-tooltip.right="'Documentation'">
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div v-else-if="$route.path === '/settings'">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#account" v-tooltip.right="$t('account')">
|
||||
<i class="material-icons">person</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#theme" v-tooltip.right="$t('theme')">
|
||||
<i class="material-icons">brush</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#proxy" v-tooltip.right="'Proxy'">
|
||||
<a href="#extensions" v-tooltip.right="$t('extensions')">
|
||||
<i class="material-icons">extensions</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#proxy" v-tooltip.right="$t('proxy')">
|
||||
<i class="material-icons">public</i>
|
||||
</a>
|
||||
</li>
|
||||
@@ -107,74 +261,467 @@
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
<nuxt id="main" class="main" />
|
||||
<aside class="nav-second"></aside>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<!-- Top section of footer: GitHub/install links -->
|
||||
<div class="main" id="main">
|
||||
<header class="header">
|
||||
<div class="flex-wrap">
|
||||
<button class="icon" id="installPWA" @click.prevent="showInstallPrompt()">
|
||||
<i class="material-icons">add_to_home_screen</i>
|
||||
<span>Install PWA</span>
|
||||
<span class="slide-in">
|
||||
<nuxt-link :to="localePath('index')">
|
||||
<h1 class="logo">Postwoman</h1>
|
||||
</nuxt-link>
|
||||
</span>
|
||||
<span>
|
||||
<a
|
||||
href="https://github.com/liyasthomas/postwoman"
|
||||
target="_blank"
|
||||
aria-label="GitHub"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" aria-label="GitHub" v-tooltip="'GitHub'">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</a>
|
||||
<button
|
||||
class="icon"
|
||||
onClick="window.open('https://twitter.com/share?text=👽 Postwoman • API request builder - Helps you create your requests faster, saving you precious time on your development&url=https://postwoman.io&hashtags=postwoman&via=liyasthomas');"
|
||||
id="installPWA"
|
||||
@click.prevent="showInstallPrompt()"
|
||||
v-tooltip="$t('install_pwa')"
|
||||
>
|
||||
<i class="material-icons">offline_bolt</i>
|
||||
</button>
|
||||
<login v-if="!fb.currentUser" />
|
||||
<span v-if="fb.currentUser">
|
||||
<v-popover>
|
||||
<button
|
||||
class="icon"
|
||||
v-tooltip="
|
||||
(fb.currentUser.displayName ||
|
||||
'<label><i>Name not found</i></label>') +
|
||||
'<br>' +
|
||||
(fb.currentUser.email ||
|
||||
'<label><i>Email not found</i></label>')
|
||||
"
|
||||
aria-label="Account"
|
||||
>
|
||||
<img
|
||||
v-if="fb.currentUser.photoURL"
|
||||
:src="fb.currentUser.photoURL"
|
||||
class="material-icons"
|
||||
alt="Profile image"
|
||||
/>
|
||||
<i v-else class="material-icons">account_circle</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<nuxt-link :to="localePath('settings')" v-close-popover>
|
||||
<button class="icon">
|
||||
<i class="material-icons">settings</i>
|
||||
<span>
|
||||
{{ $t("settings") }}
|
||||
</span>
|
||||
</button>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="logout" v-close-popover>
|
||||
<i class="material-icons">exit_to_app</i>
|
||||
<span>{{ $t("logout") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</span>
|
||||
<v-popover>
|
||||
<button class="icon" v-tooltip="$t('more')">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="showExtensions = true"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">extension</i>
|
||||
<span>{{ $t("extensions") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="showShortcuts = true"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">keyboard</i>
|
||||
<span>{{ $t("shortcuts") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="showSupport = true"
|
||||
v-close-popover
|
||||
>
|
||||
<i class="material-icons">favorite</i>
|
||||
<span>{{ $t("support_us") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
onClick="window.open('https://twitter.com/share?text=👽 Postwoman • A free, fast and beautiful API request builder - Web alternative to Postman - Helps you create requests faster, saving precious time on development.&url=https://postwoman.io&hashtags=postwoman&via=liyasthomas');"
|
||||
v-close-popover
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Tweet</span>
|
||||
<span>{{ $t("tweet") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="navigatorShare"
|
||||
class="icon"
|
||||
@click="nativeShare"
|
||||
v-close-popover
|
||||
v-tooltip="$t('more')"
|
||||
>
|
||||
<i class="material-icons">share</i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Bottom section of footer: version/author information -->
|
||||
<p class="align-center">
|
||||
<span v-if="version.name">
|
||||
</template>
|
||||
</v-popover>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<nuxt />
|
||||
<footer class="footer">
|
||||
<div class="flex-wrap">
|
||||
<span v-if="version.name" class="mono">
|
||||
<a
|
||||
v-bind:href="'https://github.com/liyasthomas/postwoman/releases/tag/' + version.name"
|
||||
class="footer-link"
|
||||
:href="
|
||||
'https://github.com/liyasthomas/postwoman/releases/tag/' +
|
||||
version.name
|
||||
"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{version.name}}</a>
|
||||
<span v-if="version.hash">
|
||||
v-tooltip="'GitHub'"
|
||||
>
|
||||
{{ version.name }}
|
||||
</a>
|
||||
<a
|
||||
class="footer-link hide-on-small-screen"
|
||||
href="https://www.netlify.com"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Powered by Netlify
|
||||
</a>
|
||||
<!-- <span v-if="version.hash">
|
||||
-
|
||||
<a
|
||||
v-bind:href="'https://github.com/liyasthomas/postwoman/commit/' + version.hash"
|
||||
:href="'https://github.com/liyasthomas/postwoman/commit/' + version.hash"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{version.hash}}</a>
|
||||
</span> -->
|
||||
<!-- <span v-if="version.variant">({{version.variant}})</span> -->
|
||||
</span>
|
||||
<span v-if="version.variant">({{version.variant}})</span>
|
||||
•
|
||||
</span> by
|
||||
<a href="https://liyasthomas.web.app" target="_blank" rel="noopener">Liyas Thomas 🦄</a> •
|
||||
<a href="https://postwoman.launchaco.com" target="_blank" rel="noopener">Subscribe</a>
|
||||
</p>
|
||||
<span>
|
||||
<a
|
||||
href="https://liyasthomas.web.app"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" v-tooltip="'Liyas Thomas'">
|
||||
🦄
|
||||
</button>
|
||||
</a>
|
||||
<a
|
||||
href="mailto:liyascthomas@gmail.com"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" v-tooltip="$t('contact_us')">
|
||||
<i class="material-icons">email</i>
|
||||
</button>
|
||||
</a>
|
||||
<v-popover>
|
||||
<button class="icon" v-tooltip="$t('choose_language')">
|
||||
<i class="material-icons">translate</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div v-for="locale in availableLocales" :key="locale.code">
|
||||
<nuxt-link :to="switchLocalePath(locale.code)">
|
||||
<button class="icon" v-close-popover>
|
||||
{{ locale.name }}
|
||||
</button>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<aside class="nav-second"></aside>
|
||||
</div>
|
||||
</div>
|
||||
<modal v-if="showExtensions" @close="showExtensions = false">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">{{ $t("extensions") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="showExtensions = false">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<p class="info">
|
||||
{{ $t("extensions_info1") }}
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
href="https://addons.mozilla.org/en-US/firefox/addon/postwoman"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon">
|
||||
<svg
|
||||
class="material-icons"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm8.003 8.657c-1.276-3.321-4.46-4.605-5.534-4.537 3.529 1.376 4.373 6.059 4.06 7.441-.307-1.621-1.286-3.017-1.872-3.385 3.417 8.005-4.835 10.465-7.353 7.687.649.168 1.931.085 2.891-.557.898-.602.983-.638 1.56-.683.686-.053-.041-1.406-1.539-1.177-.616.094-1.632.819-2.88.341-1.508-.576-1.46-2.634.096-2.015.337-.437.088-1.263.088-1.263.452-.414 1.022-.706 1.37-.911.228-.135.829-.507.795-1.23-.123-.096-.32-.219-.766-.193-1.736.11-1.852-.518-1.967-.808.078-.668.524-1.534 1.361-1.931-1.257-.193-2.28.397-2.789 1.154-.809-.174-1.305-.183-2.118-.031-.316-.24-.666-.67-.878-1.181 1.832-2.066 4.499-3.378 7.472-3.378 5.912 0 8.263 4.283 8.003 6.657z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Firefox</span>
|
||||
<span
|
||||
class="icon"
|
||||
v-if="firefoxExtInstalled"
|
||||
v-tooltip="$t('installed')"
|
||||
>
|
||||
<i class="material-icons">done</i>
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://chrome.google.com/webstore/detail/postwoman-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon">
|
||||
<svg
|
||||
class="material-icons"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M2.897 4.181c2.43-2.828 5.763-4.181 9.072-4.181 4.288 0 8.535 2.273 10.717 6.554-2.722.001-6.984 0-9.293 0-1.674.001-2.755-.037-3.926.579-1.376.724-2.415 2.067-2.777 3.644l-3.793-6.596zm5.11 7.819c0 2.2 1.789 3.99 3.988 3.99s3.988-1.79 3.988-3.99-1.789-3.991-3.988-3.991-3.988 1.791-3.988 3.991zm5.536 5.223c-2.238.666-4.858-.073-6.293-2.549-1.095-1.891-3.989-6.933-5.305-9.225-1.33 2.04-1.945 4.294-1.945 6.507 0 5.448 3.726 10.65 9.673 11.818l3.87-6.551zm2.158-9.214c1.864 1.734 2.271 4.542 1.007 6.719-.951 1.641-3.988 6.766-5.46 9.248 7.189.443 12.752-5.36 12.752-11.972 0-1.313-.22-2.66-.69-3.995h-7.609z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Chrome</span>
|
||||
<span
|
||||
class="icon"
|
||||
v-if="chromeExtInstalled"
|
||||
v-tooltip="$t('installed')"
|
||||
>
|
||||
<i class="material-icons">done</i>
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer"></div>
|
||||
</modal>
|
||||
<modal v-if="showShortcuts" @close="showShortcuts = false">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">{{ $t("shortcuts") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="showShortcuts = false">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<div>
|
||||
<label>{{ $t("send_request") }}</label>
|
||||
<kbd>{{ getSpecialKey() }} G</kbd>
|
||||
</div>
|
||||
<div>
|
||||
<label>{{ $t("save_to_collections") }}</label>
|
||||
<kbd>{{ getSpecialKey() }} S</kbd>
|
||||
</div>
|
||||
<div>
|
||||
<label>{{ $t("copy_request_link") }}</label>
|
||||
<kbd>{{ getSpecialKey() }} K</kbd>
|
||||
</div>
|
||||
<div>
|
||||
<label>{{ $t("reset_request") }}</label>
|
||||
<kbd>{{ getSpecialKey() }} L</kbd>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer"></div>
|
||||
</modal>
|
||||
<modal v-if="showSupport" @close="showSupport = false">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">{{ $t("support_us_on") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="showSupport = false">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<p class="info">
|
||||
{{ $t("donate_info1") }}
|
||||
</p>
|
||||
<p class="info">
|
||||
{{ $t("donate_info2") }}
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
href="https://opencollective.com/postwoman"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('one_time_recurring')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">donut_large</i>
|
||||
<span>{{ $t("open_collective") }}</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://www.paypal.me/liyascthomas"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('one_time')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">payment</i>
|
||||
<span>{{ $t("paypal") }}</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://www.patreon.com/liyasthomas"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('recurring')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">local_parking</i>
|
||||
<span>{{ $t("patreon") }}</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer"></div>
|
||||
</modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
<style scoped lang="scss">
|
||||
.footer-link {
|
||||
margin: 8px 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import intializePwa from "../assets/js/pwa";
|
||||
import logo from "../components/logo";
|
||||
import * as version from "../.postwoman/version.json";
|
||||
import intializePwa from "../assets/js/pwa";
|
||||
import * as version from "../.postwoman/version.json";
|
||||
import { hasExtensionInstalled } from "../functions/strategies/ExtensionStrategy";
|
||||
import firebase from "firebase/app";
|
||||
import { fb } from "../functions/fb";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
components: {
|
||||
logo
|
||||
logo: () => import("../components/logo"),
|
||||
modal: () => import("../components/modal"),
|
||||
login: () => import("../components/firebase/login")
|
||||
},
|
||||
|
||||
methods: {
|
||||
getSpecialKey() {
|
||||
return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? "⌘" : "Ctrl";
|
||||
},
|
||||
linkActive(path) {
|
||||
return {
|
||||
"nuxt-link-exact-active": this.$route.path === path,
|
||||
"nuxt-link-active": this.$route.path === path
|
||||
};
|
||||
},
|
||||
logout() {
|
||||
fb.currentUser = null;
|
||||
firebase
|
||||
.auth()
|
||||
.signOut()
|
||||
.catch(err => {
|
||||
this.$toast.show(err.message || err, {
|
||||
icon: "error"
|
||||
});
|
||||
});
|
||||
this.$toast.info(this.$t("logged_out"), {
|
||||
icon: "vpn_key"
|
||||
});
|
||||
},
|
||||
nativeShare() {
|
||||
if (navigator.share) {
|
||||
navigator
|
||||
.share({
|
||||
title: "Postwoman",
|
||||
text:
|
||||
"Postwoman • A free, fast and beautiful API request builder - Web alternative to Postman - Helps you create requests faster, saving precious time on development.",
|
||||
url: "https://postwoman.io/"
|
||||
})
|
||||
.then(() => {})
|
||||
.catch(console.error);
|
||||
} else {
|
||||
// fallback
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -184,15 +731,13 @@
|
||||
// that can be called to show the user the installation
|
||||
// prompt.
|
||||
showInstallPrompt: null,
|
||||
logoStyle() {
|
||||
return (
|
||||
this.$store.state.postwoman.settings.THEME_CLASS || ""
|
||||
).includes("light")
|
||||
? " filter: invert(100%); -webkit-filter: invert(100%);"
|
||||
: "";
|
||||
},
|
||||
|
||||
version: {}
|
||||
version: {},
|
||||
showExtensions: false,
|
||||
showShortcuts: false,
|
||||
showSupport: false,
|
||||
extensionInstalled: hasExtensionInstalled(),
|
||||
fb,
|
||||
navigatorShare: navigator.share
|
||||
};
|
||||
},
|
||||
|
||||
@@ -207,12 +752,12 @@
|
||||
this.$store.state.postwoman.settings.THEME_CLASS || "";
|
||||
// Load theme color data from settings, or use default color.
|
||||
let color = this.$store.state.postwoman.settings.THEME_COLOR || "#50fa7b";
|
||||
let vibrant = this.$store.state.postwoman.settings.THEME_COLOR_VIBRANT;
|
||||
if (vibrant == null) vibrant = true;
|
||||
let vibrant =
|
||||
this.$store.state.postwoman.settings.THEME_COLOR_VIBRANT || true;
|
||||
document.documentElement.style.setProperty("--ac-color", color);
|
||||
document.documentElement.style.setProperty(
|
||||
"--act-color",
|
||||
vibrant ? "rgb(37, 38, 40)" : "#ffffff"
|
||||
vibrant ? "rgba(32, 33, 36, 1)" : "rgba(255, 255, 255, 1)"
|
||||
);
|
||||
})();
|
||||
},
|
||||
@@ -226,7 +771,7 @@
|
||||
.querySelector("meta[name=theme-color]")
|
||||
.setAttribute(
|
||||
"content",
|
||||
this.$store.state.postwoman.settings.THEME_TAB_COLOR || "#252628"
|
||||
this.$store.state.postwoman.settings.THEME_TAB_COLOR || "#202124"
|
||||
);
|
||||
|
||||
// Initializes the PWA code - checks if the app is installed,
|
||||
@@ -235,13 +780,13 @@
|
||||
this.showInstallPrompt = await intializePwa();
|
||||
let cookiesAllowed = localStorage.getItem("cookiesAllowed") === "yes";
|
||||
if (!cookiesAllowed) {
|
||||
this.$toast.show("We use cookies", {
|
||||
this.$toast.show(this.$t("we_use_cookies"), {
|
||||
icon: "info",
|
||||
duration: 5000,
|
||||
theme: "toasted-primary",
|
||||
action: [
|
||||
{
|
||||
text: "Dismiss",
|
||||
text: this.$t("dismiss"),
|
||||
onClick: (e, toastObject) => {
|
||||
localStorage.setItem("cookiesAllowed", "yes");
|
||||
toastObject.goAway(0);
|
||||
@@ -250,6 +795,44 @@
|
||||
]
|
||||
});
|
||||
}
|
||||
let showExtensionsToast =
|
||||
localStorage.getItem("showExtensionsToast") === "yes";
|
||||
if (
|
||||
!this.extensionInstalled &&
|
||||
!showExtensionsToast
|
||||
) {
|
||||
setTimeout(() => {
|
||||
this.$toast.show(this.$t("extensions_info2"), {
|
||||
icon: "extension",
|
||||
duration: 5000,
|
||||
theme: "toasted-primary",
|
||||
action: [
|
||||
{
|
||||
text: this.$t("yes"),
|
||||
onClick: (e, toastObject) => {
|
||||
this.showExtensions = true;
|
||||
localStorage.setItem("showExtensionsToast", "yes");
|
||||
toastObject.goAway(0);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: this.$t("no"),
|
||||
onClick: (e, toastObject) => {
|
||||
toastObject.goAway(0);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}, 15000);
|
||||
}
|
||||
|
||||
this._keyListener = function(e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
this.showExtensions = this.showShortcuts = this.showSupport = false;
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", this._keyListener.bind(this));
|
||||
})();
|
||||
|
||||
window.addEventListener("scroll", event => {
|
||||
@@ -259,6 +842,7 @@
|
||||
let section = document.querySelector(link.hash);
|
||||
|
||||
if (
|
||||
section &&
|
||||
section.offsetTop <= fromTop &&
|
||||
section.offsetTop + section.offsetHeight > fromTop
|
||||
) {
|
||||
@@ -268,12 +852,31 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log(
|
||||
"%cWe ❤︎ open source!",
|
||||
"background-color:white;padding:8px 16px;border-radius:8px;font-size:32px;color:red;"
|
||||
);
|
||||
console.log(
|
||||
"%cContribute: https://github.com/liyasthomas/postwoman",
|
||||
"background-color:black;padding:4px 8px;border-radius:8px;font-size:16px;color:white;"
|
||||
);
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route() {
|
||||
this.$toast.clear();
|
||||
// this.$toast.clear();
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
availableLocales() {
|
||||
return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<template>
|
||||
<div class="page page-error">
|
||||
<img src="~static/icons/error.svg" alt="Error" class="error_banner" />
|
||||
<img
|
||||
src="~static/icons/error.svg"
|
||||
:alt="$t('error')"
|
||||
class="error_banner"
|
||||
/>
|
||||
<h2>{{ error.statusCode }}</h2>
|
||||
<h3>{{ error.message }}</h3>
|
||||
<p>
|
||||
<nuxt-link to="/">
|
||||
<button>Go Home</button>
|
||||
<button>{{ $t("go_home") }}</button>
|
||||
</nuxt-link>
|
||||
</p>
|
||||
<p>
|
||||
<a href @click.prevent="reloadApplication">Reload</a>
|
||||
<a href @click.prevent="reloadApplication">{{ $t("reload") }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
<style scoped lang="scss">
|
||||
// Center the error page in the viewport.
|
||||
.page-error {
|
||||
display: flex;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
export default function ({
|
||||
route,
|
||||
redirect
|
||||
}) {
|
||||
if (route.fullPath !== '/') {
|
||||
return redirect('/');
|
||||
export default function({ route, redirect }) {
|
||||
if (route.fullPath !== "/") {
|
||||
return redirect("/");
|
||||
}
|
||||
}
|
||||
|
||||
332
nuxt.config.js
332
nuxt.config.js
@@ -2,51 +2,65 @@
|
||||
// TODO: Use these when rendering the pages (rather than just for head/meta tags...)
|
||||
export const meta = {
|
||||
name: "Postwoman",
|
||||
shortDescription: "API request builder",
|
||||
description: "The Postwoman API request builder helps you create your requests faster, saving you precious time on your development."
|
||||
shortDescription: "A free, fast and beautiful API request builder",
|
||||
description:
|
||||
"Web alternative to Postman - Helps you create requests faster, saving precious time on development."
|
||||
};
|
||||
// Sets the base path for the router.
|
||||
// Important for deploying to GitHub pages.
|
||||
// -- Travis includes the author in the repo slug,
|
||||
// so if there's a /, we need to get everything after it.
|
||||
let repoName = (process.env.TRAVIS_REPO_SLUG || '').split('/').pop();
|
||||
export const routerBase = process.env.DEPLOY_ENV === 'GH_PAGES' ? {
|
||||
let repoName = (process.env.TRAVIS_REPO_SLUG || "").split("/").pop();
|
||||
export const routerBase =
|
||||
process.env.DEPLOY_ENV === "GH_PAGES"
|
||||
? {
|
||||
router: {
|
||||
base: `/${repoName}/`
|
||||
}
|
||||
} : {
|
||||
router: {
|
||||
base: '/'
|
||||
}
|
||||
};
|
||||
: {
|
||||
router: {
|
||||
base: "/"
|
||||
}
|
||||
};
|
||||
export default {
|
||||
mode: 'spa',
|
||||
mode: "spa",
|
||||
/*
|
||||
** Headers of the page
|
||||
*/
|
||||
server: {
|
||||
host: '0.0.0.0', // default: localhost
|
||||
host: "0.0.0.0" // default: localhost
|
||||
},
|
||||
render: {
|
||||
bundleRenderer: {
|
||||
shouldPreload: (file, type) => {
|
||||
return ["script", "style", "font"].includes(type);
|
||||
}
|
||||
}
|
||||
},
|
||||
head: {
|
||||
title: `${meta.name} \u2022 ${meta.shortDescription}`,
|
||||
meta: [{
|
||||
charset: 'utf-8'
|
||||
meta: [
|
||||
{
|
||||
charset: "utf-8"
|
||||
},
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no, minimal-ui'
|
||||
name: "viewport",
|
||||
content:
|
||||
"width=device-width, initial-scale=1, minimum-scale=1, viewport-fit=cover, minimal-ui"
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: meta.description || ''
|
||||
hid: "description",
|
||||
name: "description",
|
||||
content: meta.description || ""
|
||||
},
|
||||
{
|
||||
name: 'keywords',
|
||||
content: 'postwoman, postwoman chrome, postwoman online, postwoman for mac, postwoman app, postwoman for windows, postwoman google chrome, postwoman chrome app, get postwoman, postwoman web, postwoman android, postwoman app for chrome, postwoman mobile app, postwoman web app, api, request, testing, tool, rest, websocket'
|
||||
name: "keywords",
|
||||
content:
|
||||
"postwoman, postwoman chrome, postwoman online, postwoman for mac, postwoman app, postwoman for windows, postwoman google chrome, postwoman chrome app, get postwoman, postwoman web, postwoman android, postwoman app for chrome, postwoman mobile app, postwoman web app, api, request, testing, tool, rest, websocket, sse, graphql"
|
||||
},
|
||||
{
|
||||
name: 'X-UA-Compatible',
|
||||
name: "X-UA-Compatible",
|
||||
content: "IE=edge, chrome=1"
|
||||
},
|
||||
{
|
||||
@@ -59,156 +73,158 @@ export default {
|
||||
},
|
||||
{
|
||||
itemprop: "image",
|
||||
content: `${routerBase.router.base}icons/icon-192x192.png`
|
||||
content: `https://postwoman.io/logo.jpg`
|
||||
},
|
||||
// Add to homescreen for Chrome on Android. Fallback for PWA (handled by nuxt)
|
||||
{
|
||||
name: 'application-name',
|
||||
name: "application-name",
|
||||
content: meta.name
|
||||
},
|
||||
// Add to homescreen for Safari on iOS
|
||||
{
|
||||
name: 'apple-mobile-web-app-capable',
|
||||
content: 'yes'
|
||||
name: "apple-mobile-web-app-capable",
|
||||
content: "yes"
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-status-bar-style',
|
||||
content: 'black-translucent'
|
||||
name: "apple-mobile-web-app-status-bar-style",
|
||||
content: "black-translucent"
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-title',
|
||||
name: "apple-mobile-web-app-title",
|
||||
content: meta.name
|
||||
},
|
||||
// Windows phone tile icon
|
||||
{
|
||||
name: 'msapplication-TileImage',
|
||||
name: "msapplication-TileImage",
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
{
|
||||
name: 'msapplication-TileColor',
|
||||
content: '#252628'
|
||||
name: "msapplication-TileColor",
|
||||
content: "#202124"
|
||||
},
|
||||
{
|
||||
name: 'msapplication-tap-highlight',
|
||||
content: 'no'
|
||||
name: "msapplication-tap-highlight",
|
||||
content: "no"
|
||||
},
|
||||
// OpenGraph
|
||||
{
|
||||
property: 'og:site_name',
|
||||
property: "og:site_name",
|
||||
content: meta.name
|
||||
},
|
||||
{
|
||||
property: 'og:url',
|
||||
content: 'https://postwoman.io'
|
||||
},
|
||||
{
|
||||
property: 'og:type',
|
||||
content: 'website'
|
||||
},
|
||||
{
|
||||
property: 'og:title',
|
||||
content: `${meta.name} \u2022 ${meta.shortDescription}`
|
||||
},
|
||||
{
|
||||
property: 'og:description',
|
||||
content: meta.description
|
||||
},
|
||||
{
|
||||
property: 'og:image',
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
// Twitter
|
||||
{
|
||||
name: 'twitter:card',
|
||||
content: "summary"
|
||||
},
|
||||
{
|
||||
name: 'twitter:site',
|
||||
content: "@liyasthomas"
|
||||
},
|
||||
{
|
||||
name: 'twitter:creator',
|
||||
content: "@liyasthomas"
|
||||
},
|
||||
{
|
||||
name: 'twitter:url',
|
||||
property: "og:url",
|
||||
content: "https://postwoman.io"
|
||||
},
|
||||
{
|
||||
name: 'twitter:title',
|
||||
content: meta.name
|
||||
property: "og:type",
|
||||
content: "website"
|
||||
},
|
||||
{
|
||||
name: 'twitter:description',
|
||||
content: meta.shortDescription
|
||||
property: "og:title",
|
||||
content: `${meta.name} \u2022 ${meta.shortDescription}`
|
||||
},
|
||||
{
|
||||
name: 'twitter:image',
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
property: "og:description",
|
||||
content: meta.description
|
||||
},
|
||||
{
|
||||
property: "og:image",
|
||||
content: `https://postwoman.io/logo.jpg`
|
||||
},
|
||||
// Twitter
|
||||
{
|
||||
name: "twitter:card",
|
||||
content: "summary_large_image"
|
||||
},
|
||||
{
|
||||
name: "twitter:site",
|
||||
content: "@liyasthomas"
|
||||
},
|
||||
{
|
||||
name: "twitter:creator",
|
||||
content: "@liyasthomas"
|
||||
},
|
||||
{
|
||||
name: "twitter:url",
|
||||
content: "https://postwoman.io"
|
||||
},
|
||||
{
|
||||
name: "twitter:title",
|
||||
content: `${meta.name} \u2022 ${meta.shortDescription}`
|
||||
},
|
||||
{
|
||||
name: "twitter:description",
|
||||
content: meta.description
|
||||
},
|
||||
{
|
||||
name: "twitter:image",
|
||||
content: "https://postwoman.io/logo.jpg"
|
||||
}
|
||||
],
|
||||
link: [{
|
||||
rel: 'icon',
|
||||
type: 'image/x-icon',
|
||||
link: [
|
||||
{
|
||||
rel: "icon",
|
||||
type: "image/x-icon",
|
||||
href: `${routerBase.router.base}favicon.ico`
|
||||
},
|
||||
// Home-screen icons (iOS)
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
rel: "apple-touch-icon",
|
||||
href: `${routerBase.router.base}icons/icon-48x48.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '72x72',
|
||||
rel: "apple-touch-icon",
|
||||
sizes: "72x72",
|
||||
href: `${routerBase.router.base}icons/icon-72x72.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '96x96',
|
||||
rel: "apple-touch-icon",
|
||||
sizes: "96x96",
|
||||
href: `${routerBase.router.base}icons/icon-96x96.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '144x144',
|
||||
rel: "apple-touch-icon",
|
||||
sizes: "144x144",
|
||||
href: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '192x192',
|
||||
rel: "apple-touch-icon",
|
||||
sizes: "192x192",
|
||||
href: `${routerBase.router.base}icons/icon-192x192.png`
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
/*
|
||||
** Customize the progress-bar color
|
||||
*/
|
||||
loading: {
|
||||
color: 'var(--ac-color)'
|
||||
color: "var(--ac-color)"
|
||||
},
|
||||
/*
|
||||
** Customize the loading indicator
|
||||
*/
|
||||
loadingIndicator: {
|
||||
name: 'pulse',
|
||||
color: 'var(--ac-color)',
|
||||
background: 'var(--bg-color)'
|
||||
name: "pulse",
|
||||
color: "var(--ac-color)",
|
||||
background: "var(--bg-color)"
|
||||
},
|
||||
/*
|
||||
** Global CSS
|
||||
*/
|
||||
css: [
|
||||
'@/assets/css/themes.scss',
|
||||
'@/assets/css/fonts.scss',
|
||||
'@/assets/css/styles.scss'
|
||||
"~/assets/css/styles.scss",
|
||||
"~/assets/css/themes.scss",
|
||||
"~/assets/css/fonts.scss"
|
||||
],
|
||||
/*
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
plugins: [{
|
||||
src: '~/plugins/vuex-persist'
|
||||
plugins: [
|
||||
{
|
||||
src: "~/plugins/vuex-persist"
|
||||
},
|
||||
{
|
||||
src: '~/plugins/v-tooltip'
|
||||
src: "~/plugins/v-tooltip"
|
||||
}
|
||||
],
|
||||
/*
|
||||
@@ -220,15 +236,19 @@ export default {
|
||||
*/
|
||||
modules: [
|
||||
// See https://goo.gl/OOhYW5
|
||||
['@nuxtjs/pwa'],
|
||||
['@nuxtjs/axios'],
|
||||
['@nuxtjs/toast'],
|
||||
['@nuxtjs/google-analytics'],
|
||||
['@nuxtjs/sitemap'],
|
||||
['@nuxtjs/google-tag-manager', {
|
||||
id: process.env.GTM_ID || 'GTM-MXWD8NQ'
|
||||
}],
|
||||
['@nuxtjs/robots']
|
||||
["@nuxtjs/pwa"],
|
||||
["@nuxtjs/axios"],
|
||||
["@nuxtjs/toast"],
|
||||
["@nuxtjs/google-analytics"],
|
||||
["@nuxtjs/sitemap"],
|
||||
[
|
||||
"@nuxtjs/google-tag-manager",
|
||||
{
|
||||
id: process.env.GTM_ID || "GTM-MXWD8NQ"
|
||||
}
|
||||
],
|
||||
["@nuxtjs/robots"],
|
||||
["nuxt-i18n"]
|
||||
],
|
||||
pwa: {
|
||||
manifest: {
|
||||
@@ -237,45 +257,115 @@ export default {
|
||||
|
||||
display: "standalone",
|
||||
|
||||
theme_color: "#252628",
|
||||
background_color: "#252628",
|
||||
theme_color: "#202124",
|
||||
background_color: "#202124",
|
||||
start_url: `${routerBase.router.base}`
|
||||
},
|
||||
|
||||
meta: {
|
||||
description: meta.shortDescription,
|
||||
theme_color: "#252628",
|
||||
theme_color: "#202124"
|
||||
},
|
||||
|
||||
icons: ((sizes) => {
|
||||
icons: (sizes => {
|
||||
let icons = [];
|
||||
for (let size of sizes) {
|
||||
icons.push({
|
||||
"src": `${routerBase.router.base}icons/icon-${size}x${size}.png`,
|
||||
"type": "image/png",
|
||||
"sizes": `${size}x${size}`
|
||||
src: `${routerBase.router.base}icons/icon-${size}x${size}.png`,
|
||||
type: "image/png",
|
||||
sizes: `${size}x${size}`
|
||||
});
|
||||
}
|
||||
return icons;
|
||||
})([48, 72, 96, 144, 192, 512])
|
||||
},
|
||||
toast: {
|
||||
position: 'bottom-center',
|
||||
position: "bottom-center",
|
||||
duration: 3000,
|
||||
theme: 'bubble',
|
||||
theme: "bubble",
|
||||
keepOnHover: true
|
||||
},
|
||||
googleAnalytics: {
|
||||
id: process.env.GA_ID || 'UA-61422507-2'
|
||||
id: process.env.GA_ID || "UA-61422507-2"
|
||||
},
|
||||
sitemap: {
|
||||
hostname: 'https://postwoman.io'
|
||||
hostname: "https://postwoman.io"
|
||||
},
|
||||
robots: {
|
||||
UserAgent: '*',
|
||||
Disallow: '',
|
||||
Allow: '/',
|
||||
Sitemap: 'https://postwoman.io/sitemap.xml'
|
||||
UserAgent: "*",
|
||||
Disallow: "",
|
||||
Allow: "/",
|
||||
Sitemap: "https://postwoman.io/sitemap.xml"
|
||||
},
|
||||
i18n: {
|
||||
locales: [
|
||||
{
|
||||
code: "en",
|
||||
name: "English",
|
||||
iso: "en-US",
|
||||
file: "en-US.js"
|
||||
},
|
||||
{
|
||||
code: "es",
|
||||
name: "Español",
|
||||
iso: "es-ES",
|
||||
file: "es-ES.js"
|
||||
},
|
||||
{
|
||||
code: "fr",
|
||||
name: "Français",
|
||||
iso: "fr-FR",
|
||||
file: "fr-FR.js"
|
||||
},
|
||||
{
|
||||
code: "fa",
|
||||
name: "Farsi",
|
||||
iso: "fa-IR",
|
||||
file: "fa-IR.js"
|
||||
},
|
||||
{
|
||||
code: "pt",
|
||||
name: "Português Brasileiro",
|
||||
iso: "pt-BR",
|
||||
file: "pt-BR.js"
|
||||
},
|
||||
{
|
||||
code: "cn",
|
||||
name: "简体中文",
|
||||
iso: "zh-CN",
|
||||
file: "zh-CN.js"
|
||||
},
|
||||
{
|
||||
code: "id",
|
||||
name: "Bahasa Indonesia",
|
||||
iso: "id-ID",
|
||||
file: "id-ID.js"
|
||||
},
|
||||
{
|
||||
code: "tr",
|
||||
name: "Türkçe",
|
||||
iso: "tr-TR",
|
||||
file: "tr-TR.js"
|
||||
},
|
||||
{
|
||||
code: "de",
|
||||
name: "Deutsch",
|
||||
iso: "de-DE",
|
||||
file: "de-DE.js"
|
||||
},
|
||||
{
|
||||
code: "ja",
|
||||
name: "日本語",
|
||||
iso: "ja-JP",
|
||||
file: "ja-JP.js"
|
||||
}
|
||||
],
|
||||
defaultLocale: "en",
|
||||
vueI18n: {
|
||||
fallbackLocale: "en"
|
||||
},
|
||||
lazy: true,
|
||||
langDir: "lang/"
|
||||
},
|
||||
/*
|
||||
** Build configuration
|
||||
@@ -296,4 +386,4 @@ export default {
|
||||
** Router configuration
|
||||
*/
|
||||
...routerBase
|
||||
}
|
||||
};
|
||||
|
||||
5041
package-lock.json
generated
5041
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
41
package.json
41
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "postwoman",
|
||||
"version": "1.0.0",
|
||||
"description": "A free, fast, and beautiful API request builder",
|
||||
"version": "1.9.0",
|
||||
"description": "A free, fast and beautiful API request builder",
|
||||
"author": "liyasthomas",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -14,29 +14,34 @@
|
||||
"generate": "nuxt generate",
|
||||
"e2e": "cypress run",
|
||||
"e2e:open": "cypress open",
|
||||
"dev:e2e": "start-server-and-test dev http://localhost:3000 e2e:open",
|
||||
"test": "start-server-and-test dev http://localhost:3000 e2e"
|
||||
"dev:e2e": "server-test dev :3000 e2e:open",
|
||||
"test": "start-server-and-test start http-get://localhost:3000 e2e"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/axios": "^5.8.0",
|
||||
"@nuxtjs/google-analytics": "^2.2.1",
|
||||
"@nuxtjs/axios": "^5.9.5",
|
||||
"@nuxtjs/google-analytics": "^2.2.3",
|
||||
"@nuxtjs/google-tag-manager": "^2.3.1",
|
||||
"@nuxtjs/pwa": "^3.0.0-beta.19",
|
||||
"@nuxtjs/pwa": "^3.0.0-beta.20",
|
||||
"@nuxtjs/robots": "^2.4.2",
|
||||
"@nuxtjs/sitemap": "^2.0.0",
|
||||
"@nuxtjs/sitemap": "^2.0.1",
|
||||
"@nuxtjs/toast": "^3.3.0",
|
||||
"highlight.js": "^9.16.2",
|
||||
"nuxt": "^2.10.2",
|
||||
"v-tooltip": "^2.0.2",
|
||||
"vue-virtual-scroll-list": "^1.4.2",
|
||||
"ace-builds": "^1.4.8",
|
||||
"firebase": "^7.9.1",
|
||||
"graphql": "^14.6.0",
|
||||
"graphql-language-service-interface": "^2.3.3",
|
||||
"nuxt": "^2.11.0",
|
||||
"nuxt-i18n": "^6.5.0",
|
||||
"v-tooltip": "^2.0.3",
|
||||
"vue-virtual-scroll-list": "^1.4.6",
|
||||
"vuefire": "^2.2.1",
|
||||
"vuejs-auto-complete": "^0.9.0",
|
||||
"vuex-persist": "^2.1.1",
|
||||
"yargs-parser": "^16.1.0"
|
||||
"vuex-persist": "^2.2.0",
|
||||
"yargs-parser": "^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^3.6.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"sass-loader": "^7.3.1",
|
||||
"start-server-and-test": "^1.10.6"
|
||||
"cypress": "^4.0.2",
|
||||
"node-sass": "^4.13.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"start-server-and-test": "^1.10.8"
|
||||
}
|
||||
}
|
||||
|
||||
394
pages/doc.vue
Normal file
394
pages/doc.vue
Normal file
@@ -0,0 +1,394 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<pw-section class="blue" :label="$t('collections')" ref="collections">
|
||||
<ul>
|
||||
<li>
|
||||
<p class="info">
|
||||
{{ $t("generate_docs_message") }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="collectionUpload">
|
||||
<button
|
||||
class="icon"
|
||||
@click="$refs.collectionUpload.click()"
|
||||
v-tooltip="$t('json')"
|
||||
>
|
||||
<i class="material-icons">folder</i>
|
||||
<span>{{ $t("import_collections") }}</span>
|
||||
</button>
|
||||
</label>
|
||||
<input
|
||||
ref="collectionUpload"
|
||||
name="collectionUpload"
|
||||
type="file"
|
||||
@change="uploadCollection"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<Editor
|
||||
v-model="collectionJSON"
|
||||
:lang="'json'"
|
||||
:options="{
|
||||
maxLines: '16',
|
||||
minLines: '8',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false
|
||||
}"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="getDoc">
|
||||
<i class="material-icons">book</i>
|
||||
<span>{{ $t("generate_docs") }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="green" label="Documentation" ref="documentation">
|
||||
<p v-if="this.items.length === 0" class="info">
|
||||
{{ $t("generate_docs_first") }}
|
||||
</p>
|
||||
<div>
|
||||
<span
|
||||
class="collection"
|
||||
v-for="(collection, index) in this.items"
|
||||
:key="index"
|
||||
>
|
||||
<h2>
|
||||
<i class="material-icons">folder</i>
|
||||
{{ collection.name || $t("none") }}
|
||||
</h2>
|
||||
<span
|
||||
class="folder"
|
||||
v-for="(folder, index) in collection.folders"
|
||||
:key="index"
|
||||
>
|
||||
<h3>
|
||||
<i class="material-icons">folder_open</i>
|
||||
{{ folder.name || $t("none") }}
|
||||
</h3>
|
||||
<span
|
||||
class="request"
|
||||
v-for="(request, index) in folder.requests"
|
||||
:key="index"
|
||||
>
|
||||
<h4>
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
{{ request.name || $t("none") }}
|
||||
</h4>
|
||||
<p class="doc-desc" v-if="request.url">
|
||||
<span>
|
||||
{{ $t("url") }}: <code>{{ request.url || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.path">
|
||||
<span>
|
||||
{{ $t("path") }}:
|
||||
<code>{{ request.path || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.method">
|
||||
<span>
|
||||
{{ $t("method") }}:
|
||||
<code>{{ request.method || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.auth">
|
||||
<span>
|
||||
{{ $t("authentication") }}:
|
||||
<code>{{ request.auth || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.httpUser">
|
||||
<span>
|
||||
{{ $t("username") }}:
|
||||
<code>{{ request.httpUser || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.httpPassword">
|
||||
<span>
|
||||
{{ $t("password") }}:
|
||||
<code>{{ request.httpPassword || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.bearerToken">
|
||||
<span>
|
||||
{{ $t("token") }}:
|
||||
<code>{{ request.bearerToken || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<h4 v-if="request.headers.length > 0">{{ $t("headers") }}</h4>
|
||||
<span v-if="request.headers">
|
||||
<p
|
||||
v-for="header in request.headers"
|
||||
:key="header.key"
|
||||
class="doc-desc"
|
||||
>
|
||||
<span>
|
||||
{{ header.key || $t("none") }}:
|
||||
<code>{{ header.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<h4 v-if="request.params.length > 0">{{ $t("parameters") }}</h4>
|
||||
<span v-if="request.params">
|
||||
<p
|
||||
v-for="parameter in request.params"
|
||||
:key="parameter.key"
|
||||
class="doc-desc"
|
||||
>
|
||||
<span>
|
||||
{{ parameter.key || $t("none") }}:
|
||||
<code>{{ parameter.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<h4 v-if="request.bodyParam">{{ $t("payload") }}</h4>
|
||||
<span v-if="request.bodyParam">
|
||||
<p
|
||||
v-for="payload in request.bodyParam"
|
||||
:key="payload.key"
|
||||
class="doc-desc"
|
||||
>
|
||||
<span>
|
||||
{{ payload.key || $t("none") }}:
|
||||
<code>{{ payload.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<p class="doc-desc" v-if="request.rawParams">
|
||||
<span>
|
||||
{{ $t("parameters") }}:
|
||||
<code>{{ request.rawParams || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.contentType">
|
||||
<span>
|
||||
{{ $t("content_type") }}:
|
||||
<code>{{ request.contentType || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.requestType">
|
||||
<span>
|
||||
{{ $t("request_type") }}:
|
||||
<code>{{ request.requestType || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="request"
|
||||
v-for="(request, index) in collection.requests"
|
||||
:key="`request-${index}`"
|
||||
>
|
||||
<h4>
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
{{ request.name || $t("none") }}
|
||||
</h4>
|
||||
<p class="doc-desc" v-if="request.url">
|
||||
<span>
|
||||
{{ $t("url") }}: <code>{{ request.url || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.path">
|
||||
<span>
|
||||
{{ $t("path") }}: <code>{{ request.path || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.method">
|
||||
<span>
|
||||
{{ $t("method") }}:
|
||||
<code>{{ request.method || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.auth">
|
||||
<span>
|
||||
{{ $t("authentication") }}:
|
||||
<code>{{ request.auth || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.httpUser">
|
||||
<span>
|
||||
{{ $t("username") }}:
|
||||
<code>{{ request.httpUser || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.httpPassword">
|
||||
<span>
|
||||
{{ $t("password") }}:
|
||||
<code>{{ request.httpPassword || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.bearerToken">
|
||||
<span>
|
||||
{{ $t("token") }}:
|
||||
<code>{{ request.bearerToken || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<h4 v-if="request.headers.length > 0">{{ $t("headers") }}</h4>
|
||||
<span v-if="request.headers">
|
||||
<p
|
||||
v-for="header in request.headers"
|
||||
:key="header.key"
|
||||
class="doc-desc"
|
||||
>
|
||||
<span>
|
||||
{{ header.key || $t("none") }}:
|
||||
<code>{{ header.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<h4 v-if="request.params.length > 0">{{ $t("parameters") }}</h4>
|
||||
<span v-if="request.params">
|
||||
<p
|
||||
v-for="parameter in request.params"
|
||||
:key="parameter.key"
|
||||
class="doc-desc"
|
||||
>
|
||||
<span>
|
||||
{{ parameter.key || $t("none") }}:
|
||||
<code>{{ parameter.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<h4 v-if="request.bodyParam">{{ $t("payload") }}</h4>
|
||||
<span v-if="request.bodyParam">
|
||||
<p
|
||||
v-for="payload in request.bodyParam"
|
||||
:key="payload.key"
|
||||
class="doc-desc"
|
||||
>
|
||||
<span>
|
||||
{{ payload.key || $t("none") }}:
|
||||
<code>{{ payload.value || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
<p class="doc-desc" v-if="request.rawParams">
|
||||
<span>
|
||||
{{ $t("parameters") }}:
|
||||
<code>{{ request.rawParams || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.contentType">
|
||||
<span>
|
||||
{{ $t("content_type") }}:
|
||||
<code>{{ request.contentType || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
<p class="doc-desc" v-if="request.requestType">
|
||||
<span>
|
||||
{{ $t("request_type") }}:
|
||||
<code>{{ request.requestType || $t("none") }}</code>
|
||||
</span>
|
||||
</p>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</pw-section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.collection,
|
||||
.folder,
|
||||
.request,
|
||||
.doc-desc {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
|
||||
.material-icons {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.folder {
|
||||
border-left: 1px solid var(--brd-color);
|
||||
margin: 16px 0 0;
|
||||
}
|
||||
|
||||
.request {
|
||||
border: 1px solid var(--brd-color);
|
||||
border-radius: 8px;
|
||||
margin: 16px 0 0;
|
||||
|
||||
h4 {
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.doc-desc {
|
||||
color: var(--fg-light-color);
|
||||
border-bottom: 1px dashed var(--brd-color);
|
||||
margin: 0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import AceEditor from "../components/ace-editor";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": () => import("../components/section"),
|
||||
Editor: AceEditor
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
collectionJSON: "[]",
|
||||
items: []
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
uploadCollection() {
|
||||
this.rawInput = true;
|
||||
let file = this.$refs.collectionUpload.files[0];
|
||||
if (file !== undefined && file !== null) {
|
||||
let reader = new FileReader();
|
||||
reader.onload = ({ target }) => {
|
||||
this.collectionJSON = target.result;
|
||||
};
|
||||
reader.readAsText(file);
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
icon: "attach_file"
|
||||
});
|
||||
} else {
|
||||
this.$toast.error(this.$t("choose_file"), {
|
||||
icon: "attach_file"
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getDoc() {
|
||||
try {
|
||||
this.items = JSON.parse(this.collectionJSON);
|
||||
this.$toast.info(this.$t("docs_generated"), {
|
||||
icon: "book"
|
||||
});
|
||||
} catch (e) {
|
||||
this.$toast.error(e, {
|
||||
icon: "code"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
712
pages/graphql.vue
Normal file
712
pages/graphql.vue
Normal file
@@ -0,0 +1,712 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="content">
|
||||
<div class="page-columns inner-left">
|
||||
<pw-section class="blue" :label="$t('endpoint')" ref="endpoint">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="url">{{ $t("url") }}</label>
|
||||
<input
|
||||
id="url"
|
||||
type="url"
|
||||
v-model="url"
|
||||
@keyup.enter="getSchema()"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<label for="get" class="hide-on-small-screen"> </label>
|
||||
<button id="get" name="get" @click="getSchema">
|
||||
{{ $t("get_schema") }}
|
||||
<span><i class="material-icons">send</i></span>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="orange" :label="$t('headers')" ref="headers">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="headerList">{{ $t("header_list") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="headers = []"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
id="headerList"
|
||||
readonly
|
||||
v-textarea-auto-height="headerString"
|
||||
v-model="headerString"
|
||||
:placeholder="$t('add_one_header')"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(header, index) in headers" :key="index">
|
||||
<li>
|
||||
<autocomplete
|
||||
:placeholder="$t('header_count', { count: index + 1 })"
|
||||
:source="commonHeaders"
|
||||
:spellcheck="false"
|
||||
:value="header.key"
|
||||
@input="
|
||||
$store.commit('setGQLHeaderKey', {
|
||||
index,
|
||||
value: $event
|
||||
})
|
||||
"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="header.value"
|
||||
@change="
|
||||
$store.commit('setGQLHeaderValue', {
|
||||
index,
|
||||
value: $event.target.value
|
||||
})
|
||||
"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="removeRequestHeader(index)"
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
id="header"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addRequestHeader">
|
||||
<i class="material-icons">add</i>
|
||||
<span>{{ $t("add_new") }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="green" :label="$t('schema')" ref="schema">
|
||||
<div class="flex-wrap">
|
||||
<label>{{ $t("response") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="ToggleExpandResponse"
|
||||
ref="ToggleExpandResponse"
|
||||
v-tooltip="{
|
||||
content: !expandResponse
|
||||
? $t('expand_response')
|
||||
: $t('collapse_response')
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !expandResponse ? "unfold_more" : "unfold_less" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="downloadResponse"
|
||||
ref="downloadResponse"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">get_app</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
ref="copySchemaCode"
|
||||
@click="copySchema"
|
||||
v-tooltip="$t('copy_schema')"
|
||||
>
|
||||
<i class="material-icons">file_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Editor
|
||||
:value="schemaString"
|
||||
:lang="'graphqlschema'"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false
|
||||
}"
|
||||
/>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="cyan" :label="$t('query')" ref="query">
|
||||
<div class="flex-wrap">
|
||||
<label for="gqlQuery">{{ $t("query") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="runQuery()"
|
||||
v-tooltip.bottom="$t('run_query')"
|
||||
>
|
||||
<i class="material-icons">play_arrow</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="copyQuery"
|
||||
ref="copyQueryButton"
|
||||
v-tooltip="$t('copy_query')"
|
||||
>
|
||||
<i class="material-icons">file_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<QueryEditor
|
||||
ref="queryEditor"
|
||||
v-model="gqlQueryString"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false
|
||||
}"
|
||||
/>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="yellow" label="Variables" ref="variables">
|
||||
<Editor
|
||||
v-model="variableString"
|
||||
:lang="'json'"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false
|
||||
}"
|
||||
/>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="purple" label="Response" ref="response">
|
||||
<div class="flex-wrap">
|
||||
<label for="responseField">{{ $t("response") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="copyResponse"
|
||||
ref="copyResponseButton"
|
||||
v-tooltip="$t('copy_response')"
|
||||
>
|
||||
<i class="material-icons">file_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Editor
|
||||
:value="responseString"
|
||||
:lang="'json'"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false
|
||||
}"
|
||||
/>
|
||||
</pw-section>
|
||||
</div>
|
||||
<aside class="sticky-inner inner-right">
|
||||
<pw-section class="purple" :label="$t('docs')" ref="docs">
|
||||
<section>
|
||||
<input
|
||||
v-if="queryFields.length > 0"
|
||||
id="queries-tab"
|
||||
type="radio"
|
||||
name="side"
|
||||
checked="checked"
|
||||
/>
|
||||
<label v-if="queryFields.length > 0" for="queries-tab">
|
||||
{{ $t("queries") }}
|
||||
</label>
|
||||
<div v-if="queryFields.length > 0" class="tab">
|
||||
<div v-for="field in queryFields" :key="field.name">
|
||||
<gql-field
|
||||
:gqlField="field"
|
||||
:jumpTypeCallback="handleJumpToType"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
v-if="mutationFields.length > 0"
|
||||
id="mutations-tab"
|
||||
type="radio"
|
||||
name="side"
|
||||
checked="checked"
|
||||
/>
|
||||
<label v-if="mutationFields.length > 0" for="mutations-tab">
|
||||
{{ $t("mutations") }}
|
||||
</label>
|
||||
<div v-if="mutationFields.length > 0" class="tab">
|
||||
<div v-for="field in mutationFields" :key="field.name">
|
||||
<gql-field
|
||||
:gqlField="field"
|
||||
:jumpTypeCallback="handleJumpToType"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
v-if="subscriptionFields.length > 0"
|
||||
id="subscriptions-tab"
|
||||
type="radio"
|
||||
name="side"
|
||||
checked="checked"
|
||||
/>
|
||||
<label v-if="subscriptionFields.length > 0" for="subscriptions-tab">
|
||||
{{ $t("subscriptions") }}
|
||||
</label>
|
||||
<div v-if="subscriptionFields.length > 0" class="tab">
|
||||
<div v-for="field in subscriptionFields" :key="field.name">
|
||||
<gql-field
|
||||
:gqlField="field"
|
||||
:jumpTypeCallback="handleJumpToType"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
v-if="gqlTypes.length > 0"
|
||||
id="gqltypes-tab"
|
||||
type="radio"
|
||||
name="side"
|
||||
checked="checked"
|
||||
/>
|
||||
<label v-if="gqlTypes.length > 0" for="gqltypes-tab">
|
||||
{{ $t("types") }}
|
||||
</label>
|
||||
<div v-if="gqlTypes.length > 0" class="tab">
|
||||
<div
|
||||
v-for="type in gqlTypes"
|
||||
:key="type.name"
|
||||
:id="`type_${type.name}`"
|
||||
>
|
||||
<gql-type
|
||||
:gqlType="type"
|
||||
:jumpTypeCallback="handleJumpToType"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p
|
||||
v-if="
|
||||
queryFields.length === 0 &&
|
||||
mutationFields.length === 0 &&
|
||||
subscriptionFields.length === 0 &&
|
||||
gqlTypes.length === 0
|
||||
"
|
||||
class="info"
|
||||
>
|
||||
{{ $t("send_request_first") }}
|
||||
</p>
|
||||
</pw-section>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tab {
|
||||
max-height: calc(100vh - 186px);
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
import * as gql from "graphql";
|
||||
import textareaAutoHeight from "../directives/textareaAutoHeight";
|
||||
import { commonHeaders } from "../functions/headers";
|
||||
import AceEditor from "../components/ace-editor";
|
||||
import QueryEditor from "../components/graphql/queryeditor";
|
||||
import { sendNetworkRequest } from "../functions/network";
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
textareaAutoHeight
|
||||
},
|
||||
components: {
|
||||
"pw-section": () => import("../components/section"),
|
||||
"gql-field": () => import("../components/graphql/field"),
|
||||
"gql-type": () => import("../components/graphql/type"),
|
||||
autocomplete: () => import("../components/autocomplete"),
|
||||
Editor: AceEditor,
|
||||
QueryEditor: QueryEditor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
schemaString: "",
|
||||
commonHeaders,
|
||||
queryFields: [],
|
||||
mutationFields: [],
|
||||
subscriptionFields: [],
|
||||
gqlTypes: [],
|
||||
responseString: "",
|
||||
copyButton: '<i class="material-icons">file_copy</i>',
|
||||
downloadButton: '<i class="material-icons">get_app</i>',
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
expandResponse: false,
|
||||
responseBodyMaxLines: 16
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
url: {
|
||||
get() {
|
||||
return this.$store.state.gql.url;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("setGQLState", { value, attribute: "url" });
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
get() {
|
||||
return this.$store.state.gql.headers;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("setGQLState", { value, attribute: "headers" });
|
||||
}
|
||||
},
|
||||
gqlQueryString: {
|
||||
get() {
|
||||
return this.$store.state.gql.query;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("setGQLState", { value, attribute: "query" });
|
||||
}
|
||||
},
|
||||
variableString: {
|
||||
get() {
|
||||
return this.$store.state.gql.variablesJSONString;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit("setGQLState", {
|
||||
value,
|
||||
attribute: "variablesJSONString"
|
||||
});
|
||||
}
|
||||
},
|
||||
headerString() {
|
||||
const result = this.headers
|
||||
.filter(({ key }) => !!key)
|
||||
.map(({ key, value }) => `${key}: ${value}`)
|
||||
.join(",\n");
|
||||
return result === "" ? "" : `${result}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleJumpToType(type) {
|
||||
const typesTab = document.getElementById("gqltypes-tab");
|
||||
typesTab.checked = true;
|
||||
|
||||
const rootTypeName = this.resolveRootType(type).name;
|
||||
|
||||
const target = document.getElementById(`type_${rootTypeName}`);
|
||||
if (target) {
|
||||
target.scrollIntoView({
|
||||
behavior: "smooth"
|
||||
});
|
||||
}
|
||||
},
|
||||
resolveRootType(type) {
|
||||
let t = type;
|
||||
while (t.ofType != null) t = t.ofType;
|
||||
return t;
|
||||
},
|
||||
copySchema() {
|
||||
this.$refs.copySchemaCode.innerHTML = this.doneButton;
|
||||
const aux = document.createElement("textarea");
|
||||
aux.innerText = this.schemaString;
|
||||
document.body.appendChild(aux);
|
||||
aux.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(aux);
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done"
|
||||
});
|
||||
setTimeout(
|
||||
() => (this.$refs.copySchemaCode.innerHTML = this.copyButton),
|
||||
1000
|
||||
);
|
||||
},
|
||||
copyQuery() {
|
||||
this.$refs.copyQueryButton.innerHTML = this.doneButton;
|
||||
const aux = document.createElement("textarea");
|
||||
aux.innerText = this.gqlQueryString;
|
||||
document.body.appendChild(aux);
|
||||
aux.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(aux);
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done"
|
||||
});
|
||||
setTimeout(
|
||||
() => (this.$refs.copyQueryButton.innerHTML = this.copyButton),
|
||||
1000
|
||||
);
|
||||
},
|
||||
copyResponse() {
|
||||
this.$refs.copyResponseButton.innerHTML = this.doneButton;
|
||||
const aux = document.createElement("textarea");
|
||||
aux.innerText = this.responseString;
|
||||
document.body.appendChild(aux);
|
||||
aux.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(aux);
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done"
|
||||
});
|
||||
setTimeout(
|
||||
() => (this.$refs.copyResponseButton.innerHTML = this.copyButton),
|
||||
1000
|
||||
);
|
||||
},
|
||||
async runQuery() {
|
||||
const startTime = Date.now();
|
||||
|
||||
this.$nuxt.$loading.start();
|
||||
this.scrollInto("response");
|
||||
|
||||
try {
|
||||
let headers = {};
|
||||
this.headers.forEach(header => {
|
||||
headers[header.key] = header.value;
|
||||
});
|
||||
|
||||
let variables = JSON.parse(this.variableString);
|
||||
|
||||
const gqlQueryString = this.gqlQueryString;
|
||||
|
||||
const reqOptions = {
|
||||
method: "post",
|
||||
url: this.url,
|
||||
headers: {
|
||||
...headers,
|
||||
"content-type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({ query: gqlQueryString, variables })
|
||||
};
|
||||
|
||||
const data = await sendNetworkRequest(reqOptions, this.$store);
|
||||
|
||||
this.responseString = JSON.stringify(data.data, null, 2);
|
||||
|
||||
this.$nuxt.$loading.finish();
|
||||
const duration = Date.now() - startTime;
|
||||
this.$toast.info(this.$t("finished_in", { duration }), {
|
||||
icon: "done"
|
||||
});
|
||||
} catch (error) {
|
||||
this.$nuxt.$loading.finish();
|
||||
|
||||
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
|
||||
icon: "error"
|
||||
});
|
||||
console.log("Error", error);
|
||||
}
|
||||
},
|
||||
async getSchema() {
|
||||
const startTime = Date.now();
|
||||
this.schemaString = this.$t("loading");
|
||||
this.scrollInto("schema");
|
||||
|
||||
// Start showing the loading bar as soon as possible.
|
||||
// The nuxt axios module will hide it when the request is made.
|
||||
this.$nuxt.$loading.start();
|
||||
|
||||
try {
|
||||
const query = JSON.stringify({
|
||||
query: gql.getIntrospectionQuery()
|
||||
});
|
||||
|
||||
let headers = {};
|
||||
this.headers.forEach(header => {
|
||||
headers[header.key] = header.value;
|
||||
});
|
||||
|
||||
const reqOptions = {
|
||||
method: "post",
|
||||
url: this.url,
|
||||
headers: {
|
||||
...headers,
|
||||
"content-type": "application/json"
|
||||
},
|
||||
data: query
|
||||
};
|
||||
|
||||
// console.log(reqOptions);
|
||||
|
||||
const reqConfig = this.$store.state.postwoman.settings.PROXY_ENABLED
|
||||
? {
|
||||
method: "post",
|
||||
url:
|
||||
this.$store.state.postwoman.settings.PROXY_URL ||
|
||||
`https://postwoman.apollotv.xyz/`,
|
||||
data: reqOptions
|
||||
}
|
||||
: reqOptions;
|
||||
|
||||
const res = await axios(reqConfig);
|
||||
|
||||
const data = this.$store.state.postwoman.settings.PROXY_ENABLED
|
||||
? res.data
|
||||
: res;
|
||||
|
||||
const schema = gql.buildClientSchema(data.data.data);
|
||||
this.schemaString = gql.printSchema(schema, {
|
||||
commentDescriptions: true
|
||||
});
|
||||
|
||||
if (schema.getQueryType()) {
|
||||
const fields = schema.getQueryType().getFields();
|
||||
const qFields = [];
|
||||
for (const field in fields) {
|
||||
qFields.push(fields[field]);
|
||||
}
|
||||
this.queryFields = qFields;
|
||||
}
|
||||
|
||||
if (schema.getMutationType()) {
|
||||
const fields = schema.getMutationType().getFields();
|
||||
const mFields = [];
|
||||
for (const field in fields) {
|
||||
mFields.push(fields[field]);
|
||||
}
|
||||
this.mutationFields = mFields;
|
||||
}
|
||||
|
||||
if (schema.getSubscriptionType()) {
|
||||
const fields = schema.getSubscriptionType().getFields();
|
||||
const sFields = [];
|
||||
for (const field in fields) {
|
||||
sFields.push(fields[field]);
|
||||
}
|
||||
this.subscriptionFields = sFields;
|
||||
}
|
||||
|
||||
const typeMap = schema.getTypeMap();
|
||||
const types = [];
|
||||
|
||||
const queryTypeName = schema.getQueryType()
|
||||
? schema.getQueryType().name
|
||||
: "";
|
||||
const mutationTypeName = schema.getMutationType()
|
||||
? schema.getMutationType().name
|
||||
: "";
|
||||
const subscriptionTypeName = schema.getSubscriptionType()
|
||||
? schema.getSubscriptionType().name
|
||||
: "";
|
||||
|
||||
for (const type in typeMap) {
|
||||
if (
|
||||
!typeMap[type].name.startsWith("__") &&
|
||||
![queryTypeName, mutationTypeName, subscriptionTypeName].includes(
|
||||
typeMap[type].name
|
||||
) &&
|
||||
typeMap[type] instanceof gql.GraphQLObjectType
|
||||
) {
|
||||
types.push(typeMap[type]);
|
||||
}
|
||||
}
|
||||
this.gqlTypes = types;
|
||||
this.$refs.queryEditor.setValidationSchema(schema);
|
||||
this.$nuxt.$loading.finish();
|
||||
const duration = Date.now() - startTime;
|
||||
this.$toast.info(this.$t("finished_in", { duration }), {
|
||||
icon: "done"
|
||||
});
|
||||
} catch (error) {
|
||||
this.$nuxt.$loading.finish();
|
||||
this.schemaString = `${error}. ${this.$t("check_console_details")}`;
|
||||
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
|
||||
icon: "error"
|
||||
});
|
||||
console.log("Error", error);
|
||||
}
|
||||
},
|
||||
ToggleExpandResponse() {
|
||||
this.expandResponse = !this.expandResponse;
|
||||
this.responseBodyMaxLines =
|
||||
this.responseBodyMaxLines == Infinity ? 16 : Infinity;
|
||||
},
|
||||
downloadResponse() {
|
||||
const dataToWrite = JSON.stringify(this.schemaString, null, 2);
|
||||
const file = new Blob([dataToWrite], { type: "application/json" });
|
||||
const a = document.createElement("a");
|
||||
const url = URL.createObjectURL(file);
|
||||
a.href = url;
|
||||
a.download = `${this.url} on ${Date()}.graphql`.replace(/\./g, "[dot]");
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
this.$refs.downloadResponse.innerHTML = this.doneButton;
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done"
|
||||
});
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
this.$refs.downloadResponse.innerHTML = this.downloadButton;
|
||||
}, 1000);
|
||||
},
|
||||
addRequestHeader(index) {
|
||||
this.$store.commit("addGQLHeader", {
|
||||
key: "",
|
||||
value: ""
|
||||
});
|
||||
return false;
|
||||
},
|
||||
removeRequestHeader(index) {
|
||||
// .slice() is used so we get a separate array, rather than just a reference
|
||||
const oldHeaders = this.headers.slice();
|
||||
|
||||
this.$store.commit("removeGQLHeader", index);
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
action: {
|
||||
text: this.$t("undo"),
|
||||
duration: 4000,
|
||||
onClick: (e, toastObject) => {
|
||||
this.headers = oldHeaders;
|
||||
toastObject.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
// console.log(oldHeaders);
|
||||
},
|
||||
scrollInto(view) {
|
||||
this.$refs[view].$el.scrollIntoView({
|
||||
behavior: "smooth"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
2177
pages/index.vue
2177
pages/index.vue
File diff suppressed because it is too large
Load Diff
438
pages/realtime.vue
Normal file
438
pages/realtime.vue
Normal file
@@ -0,0 +1,438 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<section id="options">
|
||||
<input id="tab-one" type="radio" name="options" checked="checked" />
|
||||
<label for="tab-one">{{ $t("websocket") }}</label>
|
||||
<div class="tab">
|
||||
<pw-section class="blue" :label="$t('request')" ref="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="url">{{ $t("url") }}</label>
|
||||
<input
|
||||
id="url"
|
||||
type="url"
|
||||
:class="{ error: !urlValid }"
|
||||
v-model="url"
|
||||
@keyup.enter="urlValid ? toggleConnection() : null"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<label for="connect" class="hide-on-small-screen"> </label>
|
||||
<button
|
||||
:disabled="!urlValid"
|
||||
id="connect"
|
||||
name="connect"
|
||||
@click="toggleConnection"
|
||||
>
|
||||
{{ !connectionState ? $t("connect") : $t("disconnect") }}
|
||||
<span>
|
||||
<i class="material-icons">
|
||||
{{ !connectionState ? "sync" : "sync_disabled" }}
|
||||
</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section
|
||||
class="purple"
|
||||
:label="$t('communication')"
|
||||
id="response"
|
||||
ref="response"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="log">{{ $t("log") }}</label>
|
||||
<div id="log" name="log" class="log">
|
||||
<span v-if="communication.log">
|
||||
<span
|
||||
v-for="(logEntry, index) in communication.log"
|
||||
:style="{ color: logEntry.color }"
|
||||
:key="index"
|
||||
>@ {{ logEntry.ts }}{{ getSourcePrefix(logEntry.source)
|
||||
}}{{ logEntry.payload }}</span
|
||||
>
|
||||
</span>
|
||||
<span v-else>{{ $t("waiting_for_connection") }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="message">{{ $t("message") }}</label>
|
||||
<input
|
||||
id="message"
|
||||
name="message"
|
||||
type="text"
|
||||
v-model="communication.input"
|
||||
:readonly="!connectionState"
|
||||
@keyup.enter="connectionState ? sendMessage() : null"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<label for="send" class="hide-on-small-screen"> </label>
|
||||
<button
|
||||
id="send"
|
||||
name="send"
|
||||
:disabled="!connectionState"
|
||||
@click="sendMessage"
|
||||
>
|
||||
{{ $t("send") }}
|
||||
<span>
|
||||
<i class="material-icons">send</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</div>
|
||||
<input id="tab-two" type="radio" name="options" />
|
||||
<label for="tab-two">{{ $t("sse") }}</label>
|
||||
<div class="tab">
|
||||
<pw-section class="blue" :label="$t('request')" ref="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="server">{{ $t("server") }}</label>
|
||||
<input
|
||||
id="server"
|
||||
type="url"
|
||||
:class="{ error: !serverValid }"
|
||||
v-model="server"
|
||||
@keyup.enter="serverValid ? toggleSSEConnection() : null"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<label for="start" class="hide-on-small-screen"> </label>
|
||||
<button
|
||||
:disabled="!serverValid"
|
||||
id="start"
|
||||
name="start"
|
||||
@click="toggleSSEConnection"
|
||||
>
|
||||
{{ !connectionSSEState ? $t("start") : $t("stop") }}
|
||||
<span>
|
||||
<i class="material-icons">
|
||||
{{ !connectionSSEState ? "sync" : "sync_disabled" }}
|
||||
</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section
|
||||
class="purple"
|
||||
:label="$t('communication')"
|
||||
id="response"
|
||||
ref="response"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="log">{{ $t("events") }}</label>
|
||||
<div id="log" name="log" class="log">
|
||||
<span v-if="events.log">
|
||||
<span
|
||||
v-for="(logEntry, index) in events.log"
|
||||
:style="{ color: logEntry.color }"
|
||||
:key="index"
|
||||
>@ {{ logEntry.ts }}{{ getSourcePrefix(logEntry.source)
|
||||
}}{{ logEntry.payload }}</span
|
||||
>
|
||||
</span>
|
||||
<span v-else>{{ $t("waiting_for_connection") }}</span>
|
||||
</div>
|
||||
<div id="result"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
div.log {
|
||||
margin: 4px;
|
||||
padding: 8px 16px;
|
||||
width: calc(100% - 8px);
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
height: 256px;
|
||||
overflow: auto;
|
||||
|
||||
&,
|
||||
span {
|
||||
font-size: 16px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": () => import("../components/section")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
connectionState: false,
|
||||
url: "wss://echo.websocket.org",
|
||||
socket: null,
|
||||
communication: {
|
||||
log: null,
|
||||
input: ""
|
||||
},
|
||||
connectionSSEState: false,
|
||||
server: "https://express-eventsource.herokuapp.com/events",
|
||||
sse: null,
|
||||
events: {
|
||||
log: null,
|
||||
input: ""
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
urlValid() {
|
||||
const protocol = "^(wss?:\\/\\/)?";
|
||||
const validIP = new RegExp(
|
||||
`${protocol}(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`
|
||||
);
|
||||
const validHostname = new RegExp(
|
||||
`${protocol}(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9/])$`
|
||||
);
|
||||
return validIP.test(this.url) || validHostname.test(this.url);
|
||||
},
|
||||
serverValid() {
|
||||
const protocol = "^(https?:\\/\\/)?";
|
||||
const validIP = new RegExp(
|
||||
`${protocol}(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`
|
||||
);
|
||||
const validHostname = new RegExp(
|
||||
`${protocol}(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9/])$`
|
||||
);
|
||||
return validIP.test(this.server) || validHostname.test(this.server);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleConnection() {
|
||||
// If it is connecting:
|
||||
if (!this.connectionState) return this.connect();
|
||||
// Otherwise, it's disconnecting.
|
||||
else return this.disconnect();
|
||||
},
|
||||
connect() {
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: this.$t("connecting_to", { name: this.url }),
|
||||
source: "info",
|
||||
color: "var(--ac-color)"
|
||||
}
|
||||
];
|
||||
try {
|
||||
this.socket = new WebSocket(this.url);
|
||||
this.socket.onopen = event => {
|
||||
this.connectionState = true;
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: this.$t("connected_to", { name: this.url }),
|
||||
source: "info",
|
||||
color: "var(--ac-color)",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
}
|
||||
];
|
||||
this.$toast.success(this.$t("connected"), {
|
||||
icon: "sync"
|
||||
});
|
||||
};
|
||||
this.socket.onerror = event => {
|
||||
this.handleError();
|
||||
};
|
||||
this.socket.onclose = event => {
|
||||
this.connectionState = false;
|
||||
this.communication.log.push({
|
||||
payload: this.$t("disconnected_from", { name: this.url }),
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
this.$toast.error(this.$t("disconnected"), {
|
||||
icon: "sync_disabled"
|
||||
});
|
||||
};
|
||||
this.socket.onmessage = event => {
|
||||
this.communication.log.push({
|
||||
payload: event.data,
|
||||
source: "server",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
};
|
||||
} catch (ex) {
|
||||
this.handleError(ex);
|
||||
this.$toast.error(this.$t("something_went_wrong"), {
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
},
|
||||
disconnect() {
|
||||
this.socket.close();
|
||||
},
|
||||
handleError(error) {
|
||||
this.disconnect();
|
||||
this.connectionState = false;
|
||||
this.communication.log.push({
|
||||
payload: this.$t("error_occurred"),
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
if (error !== null)
|
||||
this.communication.log.push({
|
||||
payload: error,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
},
|
||||
sendMessage() {
|
||||
const message = this.communication.input;
|
||||
this.socket.send(message);
|
||||
this.communication.log.push({
|
||||
payload: message,
|
||||
source: "client",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
this.communication.input = "";
|
||||
},
|
||||
collapse({ target }) {
|
||||
const el = target.parentNode.className;
|
||||
document.getElementsByClassName(el)[0].classList.toggle("hidden");
|
||||
},
|
||||
getSourcePrefix(source) {
|
||||
const sourceEmojis = {
|
||||
// Source used for info messages.
|
||||
info: "\tℹ️ [INFO]:\t",
|
||||
// Source used for client to server messages.
|
||||
client: "\t👽 [SENT]:\t",
|
||||
// Source used for server to client messages.
|
||||
server: "\t📥 [RECEIVED]:\t"
|
||||
};
|
||||
if (Object.keys(sourceEmojis).includes(source))
|
||||
return sourceEmojis[source];
|
||||
return "";
|
||||
},
|
||||
toggleSSEConnection() {
|
||||
// If it is connecting:
|
||||
if (!this.connectionSSEState) return this.start();
|
||||
// Otherwise, it's disconnecting.
|
||||
else return this.stop();
|
||||
},
|
||||
start() {
|
||||
this.events.log = [
|
||||
{
|
||||
payload: this.$t("connecting_to", { name: this.server }),
|
||||
source: "info",
|
||||
color: "var(--ac-color)"
|
||||
}
|
||||
];
|
||||
if (typeof EventSource !== "undefined") {
|
||||
try {
|
||||
this.sse = new EventSource(this.server);
|
||||
this.sse.onopen = event => {
|
||||
this.connectionSSEState = true;
|
||||
this.events.log = [
|
||||
{
|
||||
payload: this.$t("connected_to", { name: this.server }),
|
||||
source: "info",
|
||||
color: "var(--ac-color)",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
}
|
||||
];
|
||||
this.$toast.success(this.$t("connected"), {
|
||||
icon: "sync"
|
||||
});
|
||||
};
|
||||
this.sse.onerror = event => {
|
||||
this.handleSSEError();
|
||||
};
|
||||
this.sse.onclose = event => {
|
||||
this.connectionSSEState = false;
|
||||
this.events.log.push({
|
||||
payload: this.$t("disconnected_from", { name: this.server }),
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
this.$toast.error(this.$t("disconnected"), {
|
||||
icon: "sync_disabled"
|
||||
});
|
||||
};
|
||||
this.sse.onmessage = event => {
|
||||
this.events.log.push({
|
||||
payload: event.data,
|
||||
source: "server",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
};
|
||||
} catch (ex) {
|
||||
this.handleSSEError(ex);
|
||||
this.$toast.error(this.$t("something_went_wrong"), {
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.events.log = [
|
||||
{
|
||||
payload: this.$t("browser_support_sse"),
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
}
|
||||
];
|
||||
}
|
||||
},
|
||||
handleSSEError(error) {
|
||||
this.stop();
|
||||
this.connectionSSEState = false;
|
||||
this.events.log.push({
|
||||
payload: this.$t("error_occurred"),
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
if (error !== null)
|
||||
this.events.log.push({
|
||||
payload: error,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
},
|
||||
stop() {
|
||||
this.sse.onclose();
|
||||
this.sse.close();
|
||||
}
|
||||
},
|
||||
updated: function() {
|
||||
this.$nextTick(function() {
|
||||
const divLog = document.getElementById("log");
|
||||
divLog.scrollBy(0, divLog.scrollHeight + 100);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,13 +1,96 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<pw-section class="cyan" label="Theme" ref="theme">
|
||||
<pw-section class="green" :label="$t('account')" ref="account">
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Background</h3>
|
||||
<div v-if="fb.currentUser">
|
||||
<button class="icon">
|
||||
<img
|
||||
v-if="fb.currentUser.photoURL"
|
||||
:src="fb.currentUser.photoURL"
|
||||
class="material-icons"
|
||||
/>
|
||||
<i v-else class="material-icons">account_circle</i>
|
||||
<span>
|
||||
{{ fb.currentUser.displayName || "Name not found" }}
|
||||
</span>
|
||||
</button>
|
||||
<br />
|
||||
<button class="icon">
|
||||
<i class="material-icons">email</i>
|
||||
<span>
|
||||
{{ fb.currentUser.email || "Email not found" }}
|
||||
</span>
|
||||
</button>
|
||||
<br />
|
||||
<button class="icon" @click="logout">
|
||||
<i class="material-icons">exit_to_app</i>
|
||||
<span>{{ $t("logout") }}</span>
|
||||
</button>
|
||||
<br />
|
||||
<p v-for="setting in fb.currentSettings" :key="setting.id">
|
||||
<pw-toggle
|
||||
:key="setting.name"
|
||||
:on="setting.value"
|
||||
@change="toggleSettings(setting.name, setting.value)"
|
||||
>
|
||||
{{ $t(setting.name) + " " + $t("sync") }}
|
||||
{{ setting.value ? $t("enabled") : $t("disabled") }}
|
||||
</pw-toggle>
|
||||
</p>
|
||||
<p v-if="fb.currentSettings.length !== 3">
|
||||
<button class="" @click="initSettings">
|
||||
<i class="material-icons">sync</i>
|
||||
<span>{{ $t("turn_on") + " " + $t("sync") }}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<label>{{ $t("login_with") }}</label>
|
||||
<p>
|
||||
<button class="icon" @click="signInWithGoogle">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Google</span>
|
||||
</button>
|
||||
<br />
|
||||
<button class="icon" @click="signInWithGithub">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||
/>
|
||||
</svg>
|
||||
<span>GitHub</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="cyan" :label="$t('theme')" ref="theme">
|
||||
<ul>
|
||||
<li>
|
||||
<label>{{ $t("background") }}</label>
|
||||
<div class="backgrounds">
|
||||
<span
|
||||
:key="theme.class"
|
||||
@click="applyTheme(theme.class, theme.color)"
|
||||
@click="applyTheme(theme)"
|
||||
v-for="theme in themes"
|
||||
>
|
||||
<swatch
|
||||
@@ -15,6 +98,7 @@
|
||||
:class="{ vibrant: theme.vibrant }"
|
||||
:color="theme.color"
|
||||
:name="theme.name"
|
||||
class="bg"
|
||||
></swatch>
|
||||
</span>
|
||||
</div>
|
||||
@@ -22,7 +106,7 @@
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Color</h3>
|
||||
<label>{{ $t("color") }}</label>
|
||||
<div class="colors">
|
||||
<span
|
||||
:key="entry.color"
|
||||
@@ -34,6 +118,7 @@
|
||||
:class="{ vibrant: entry.vibrant }"
|
||||
:color="entry.color"
|
||||
:name="entry.name"
|
||||
class="fg"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@@ -41,39 +126,95 @@
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Labels</h3>
|
||||
<span>
|
||||
<pw-toggle
|
||||
:on="settings.FRAME_COLORS_ENABLED"
|
||||
@change="toggleSetting('FRAME_COLORS_ENABLED')"
|
||||
>Multi-color {{ settings.FRAME_COLORS_ENABLED ? "Enabled" : "Disabled" }}</pw-toggle>
|
||||
>
|
||||
{{ $t("multi_color") }}
|
||||
{{
|
||||
settings.FRAME_COLORS_ENABLED ? $t("enabled") : $t("disabled")
|
||||
}}
|
||||
</pw-toggle>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<br />
|
||||
|
||||
<pw-section class="blue" label="Proxy" ref="proxy">
|
||||
<pw-section class="purple" :label="$t('extensions')" ref="extensions">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<pw-toggle
|
||||
:on="settings.EXTENSIONS_ENABLED"
|
||||
@change="toggleSetting('EXTENSIONS_ENABLED')"
|
||||
>
|
||||
{{ $t("extensions_use_toggle") }}
|
||||
</pw-toggle>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="blue" :label="$t('proxy')" ref="proxy">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<span>
|
||||
<pw-toggle
|
||||
:on="settings.PROXY_ENABLED"
|
||||
@change="toggleSetting('PROXY_ENABLED')"
|
||||
>Proxy {{ settings.PROXY_ENABLED ? "enabled" : "disabled" }}</pw-toggle>
|
||||
>
|
||||
{{ $t("proxy") }}
|
||||
{{ settings.PROXY_ENABLED ? $t("enabled") : $t("disabled") }}
|
||||
</pw-toggle>
|
||||
</span>
|
||||
<a
|
||||
href="https://github.com/liyasthomas/postwoman/wiki/Proxy"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" v-tooltip="$t('wiki')">
|
||||
<i class="material-icons">help</i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="url">{{ $t("url") }}</label>
|
||||
<button
|
||||
class="icon"
|
||||
@click="resetProxy"
|
||||
v-tooltip.bottom="$t('reset_default')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
id="url"
|
||||
type="url"
|
||||
v-model="settings.PROXY_URL"
|
||||
:disabled="!settings.PROXY_ENABLED"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="info">
|
||||
<li>
|
||||
<p>
|
||||
Postwoman's Proxy is hosted by ApolloTV.
|
||||
<br />Read the ApolloTV privacy policy
|
||||
{{ $t("postwoman_official_proxy_hosting") }}
|
||||
<br />
|
||||
{{ $t("read_the") }}
|
||||
<a
|
||||
class="link"
|
||||
href="https://apollotv.xyz/legal"
|
||||
target="_blank"
|
||||
>here</a>.
|
||||
rel="noopener"
|
||||
>
|
||||
{{ $t("apollotv_privacy_policy") }} </a
|
||||
>.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -96,23 +237,17 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.info {
|
||||
margin-left: 4px;
|
||||
color: var(--fg-light-color);
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
<script>
|
||||
import section from "../components/section";
|
||||
import swatch from "../components/settings/swatch";
|
||||
import toggle from "../components/toggle";
|
||||
import firebase from "firebase/app";
|
||||
import { fb } from "../functions/fb";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": section,
|
||||
"pw-toggle": toggle,
|
||||
swatch: swatch
|
||||
"pw-section": () => import("../components/section"),
|
||||
"pw-toggle": () => import("../components/toggle"),
|
||||
swatch: () => import("../components/settings/swatch")
|
||||
},
|
||||
|
||||
data() {
|
||||
@@ -122,26 +257,32 @@ export default {
|
||||
// set the relevant values.
|
||||
themes: [
|
||||
{
|
||||
color: "#252628",
|
||||
name: "Kinda Dark",
|
||||
class: ""
|
||||
color: "#202124",
|
||||
name: this.$t("kinda_dark"),
|
||||
class: "",
|
||||
aceEditor: "twilight"
|
||||
},
|
||||
{
|
||||
color: "#ffffff",
|
||||
name: "Clearly White",
|
||||
name: this.$t("clearly_white"),
|
||||
vibrant: true,
|
||||
class: "light"
|
||||
class: "light",
|
||||
aceEditor: "iplastic"
|
||||
},
|
||||
{
|
||||
color: "#000000",
|
||||
name: "Just Black",
|
||||
class: "black"
|
||||
name: this.$t("just_black"),
|
||||
class: "black",
|
||||
aceEditor: "vibrant_ink"
|
||||
},
|
||||
{
|
||||
color: "var(--bg-color)",
|
||||
name: "Auto (system)",
|
||||
color: "var(--ac-color)",
|
||||
name: this.$t("auto_system"),
|
||||
vibrant: window.matchMedia("(prefers-color-scheme: light)").matches,
|
||||
class: "auto"
|
||||
class: "auto",
|
||||
aceEditor: window.matchMedia("(prefers-color-scheme: light)").matches
|
||||
? "iplastic"
|
||||
: "twilight"
|
||||
}
|
||||
],
|
||||
// You can define a new color here! It will simply store the color value.
|
||||
@@ -149,42 +290,42 @@ export default {
|
||||
// If the color is vibrant, black is used as the active foreground color.
|
||||
{
|
||||
color: "#50fa7b",
|
||||
name: "Green",
|
||||
name: this.$t("green"),
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#f1fa8c",
|
||||
name: "Yellow",
|
||||
name: this.$t("yellow"),
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#ff79c6",
|
||||
name: "Pink",
|
||||
name: this.$t("pink"),
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#ff5555",
|
||||
name: "Red",
|
||||
name: this.$t("red"),
|
||||
vibrant: false
|
||||
},
|
||||
{
|
||||
color: "#bd93f9",
|
||||
name: "Purple",
|
||||
name: this.$t("purple"),
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#ffb86c",
|
||||
name: "Orange",
|
||||
name: this.$t("orange"),
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#8be9fd",
|
||||
name: "Cyan",
|
||||
name: this.$t("cyan"),
|
||||
vibrant: true
|
||||
},
|
||||
{
|
||||
color: "#57b5f9",
|
||||
name: "Blue",
|
||||
name: this.$t("blue"),
|
||||
vibrant: false
|
||||
}
|
||||
],
|
||||
@@ -199,9 +340,19 @@ export default {
|
||||
this.$store.state.postwoman.settings.FRAME_COLORS_ENABLED || false,
|
||||
PROXY_ENABLED:
|
||||
this.$store.state.postwoman.settings.PROXY_ENABLED || false,
|
||||
PROXY_URL: this.$store.state.postwoman.settings.PROXY_URL || "",
|
||||
PROXY_KEY: this.$store.state.postwoman.settings.PROXY_KEY || ""
|
||||
}
|
||||
PROXY_URL:
|
||||
this.$store.state.postwoman.settings.PROXY_URL ||
|
||||
"https://postwoman.apollotv.xyz/",
|
||||
PROXY_KEY: this.$store.state.postwoman.settings.PROXY_KEY || "",
|
||||
EXTENSIONS_ENABLED:
|
||||
typeof this.$store.state.postwoman.settings.EXTENSIONS_ENABLED !==
|
||||
"undefined"
|
||||
? this.$store.state.postwoman.settings.EXTENSIONS_ENABLED
|
||||
: true
|
||||
},
|
||||
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
fb
|
||||
};
|
||||
},
|
||||
|
||||
@@ -216,28 +367,22 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
applyTheme(name, color) {
|
||||
applyTheme({ class: name, color, aceEditor }) {
|
||||
this.applySetting("THEME_CLASS", name);
|
||||
this.applySetting("THEME_ACE_EDITOR", aceEditor);
|
||||
document
|
||||
.querySelector("meta[name=theme-color]")
|
||||
.setAttribute("content", color);
|
||||
this.applySetting("THEME_TAB_COLOR", color);
|
||||
document.documentElement.className = name;
|
||||
let imgGitHub = document.getElementById("imgGitHub");
|
||||
imgGitHub.style["filter"] = "";
|
||||
imgGitHub.style["webkit-filter"] = "invert(100%)";
|
||||
if (name.includes("light")) {
|
||||
imgGitHub.style["filter"] = "invert(100%)";
|
||||
imgGitHub.style["webkit-filter"] = "invert(100%)";
|
||||
}
|
||||
},
|
||||
setActiveColor(color, vibrant) {
|
||||
// By default, the color is vibrant.
|
||||
if (vibrant == null) vibrant = true;
|
||||
if (vibrant === null) vibrant = true;
|
||||
document.documentElement.style.setProperty("--ac-color", color);
|
||||
document.documentElement.style.setProperty(
|
||||
"--act-color",
|
||||
vibrant ? "rgb(37, 38, 40)" : "#f8f8f2"
|
||||
vibrant ? "rgba(32, 33, 36, 1)" : "rgba(255, 255, 255, 1)"
|
||||
);
|
||||
this.applySetting("THEME_COLOR", color.toUpperCase());
|
||||
this.applySetting("THEME_COLOR_VIBRANT", vibrant);
|
||||
@@ -258,11 +403,102 @@ export default {
|
||||
toggleSetting(key) {
|
||||
this.settings[key] = !this.settings[key];
|
||||
this.$store.commit("postwoman/applySetting", [key, this.settings[key]]);
|
||||
this.$router.replace("/settings", {
|
||||
force: true
|
||||
},
|
||||
logout() {
|
||||
fb.currentUser = null;
|
||||
firebase
|
||||
.auth()
|
||||
.signOut()
|
||||
.catch(err => {
|
||||
this.$toast.show(err.message || err, {
|
||||
icon: "error"
|
||||
});
|
||||
});
|
||||
this.$toast.info(this.$t("logged_out"), {
|
||||
icon: "vpn_key"
|
||||
});
|
||||
},
|
||||
signInWithGoogle() {
|
||||
const provider = new firebase.auth.GoogleAuthProvider();
|
||||
firebase
|
||||
.auth()
|
||||
.signInWithPopup(provider)
|
||||
.then(({ additionalUserInfo }) => {
|
||||
if (additionalUserInfo.isNewUser) {
|
||||
this.$toast.info(`${this.$t("turn_on")} ${this.$t("sync")}`, {
|
||||
icon: "sync",
|
||||
duration: null,
|
||||
closeOnSwipe: false,
|
||||
action: {
|
||||
text: this.$t("yes"),
|
||||
onClick: (e, toastObject) => {
|
||||
fb.writeSettings("syncHistory", true);
|
||||
fb.writeSettings("syncCollections", true);
|
||||
fb.writeSettings("syncEnvironments", true);
|
||||
this.$router.push({ path: "/settings" });
|
||||
toastObject.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.$toast.show(err.message || err, {
|
||||
icon: "error"
|
||||
});
|
||||
});
|
||||
},
|
||||
signInWithGithub() {
|
||||
const provider = new firebase.auth.GithubAuthProvider();
|
||||
firebase
|
||||
.auth()
|
||||
.signInWithPopup(provider)
|
||||
.then(({ additionalUserInfo }) => {
|
||||
if (additionalUserInfo.isNewUser) {
|
||||
this.$toast.info(`${this.$t("turn_on")} ${this.$t("sync")}`, {
|
||||
icon: "sync",
|
||||
duration: null,
|
||||
closeOnSwipe: false,
|
||||
action: {
|
||||
text: this.$t("yes"),
|
||||
onClick: (e, toastObject) => {
|
||||
fb.writeSettings("syncHistory", true);
|
||||
fb.writeSettings("syncCollections", true);
|
||||
fb.writeSettings("syncEnvironments", true);
|
||||
this.$router.push({ path: "/settings" });
|
||||
toastObject.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.$toast.show(err.message || err, {
|
||||
icon: "error"
|
||||
});
|
||||
});
|
||||
},
|
||||
toggleSettings(s, v) {
|
||||
fb.writeSettings(s, !v);
|
||||
},
|
||||
initSettings() {
|
||||
fb.writeSettings("syncHistory", true);
|
||||
fb.writeSettings("syncCollections", true);
|
||||
fb.writeSettings("syncEnvironments", true);
|
||||
},
|
||||
resetProxy({ target }) {
|
||||
this.settings.PROXY_URL = `https://postwoman.apollotv.xyz/`;
|
||||
target.innerHTML = this.doneButton;
|
||||
this.$toast.info(this.$t("cleared"), {
|
||||
icon: "clear_all"
|
||||
});
|
||||
setTimeout(
|
||||
() => (target.innerHTML = '<i class="material-icons">clear_all</i>'),
|
||||
1000
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
this.settings.THEME_COLOR = this.getActiveColor();
|
||||
},
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<pw-section class="blue" label="Request" ref="request">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
<input
|
||||
id="url"
|
||||
type="url"
|
||||
:class="{ error: !urlValid }"
|
||||
v-model="url"
|
||||
@keyup.enter="urlValid ? toggleConnection() : null"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<label for="connect" class="hide-on-small-screen"> </label>
|
||||
<button :disabled="!urlValid" id="connect" name="connect" @click="toggleConnection">
|
||||
{{ toggleConnectionVerb }}
|
||||
<span>
|
||||
<i class="material-icons" v-if="!connectionState">sync</i>
|
||||
<i class="material-icons" v-if="connectionState">sync_disabled</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<br />
|
||||
|
||||
<pw-section class="purple" label="Communication" id="response" ref="response">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="log">Log</label>
|
||||
<div id="log" name="log" class="log">
|
||||
<span v-if="communication.log">
|
||||
<span
|
||||
v-for="(logEntry, index) in communication.log"
|
||||
:style="{ color: logEntry.color }"
|
||||
:key="index"
|
||||
>@ {{ logEntry.ts }} {{ getSourcePrefix(logEntry.source) }} {{ logEntry.payload }}</span>
|
||||
</span>
|
||||
<span v-else>(waiting for connection)</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="message">Message</label>
|
||||
<input
|
||||
id="message"
|
||||
name="message"
|
||||
type="text"
|
||||
v-model="communication.input"
|
||||
:readonly="!connectionState"
|
||||
@keyup.enter="connectionState ? sendMessage() : null"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<label for="send" class="hide-on-small-screen"> </label>
|
||||
<button id="send" name="send" :disabled="!connectionState" @click="sendMessage">
|
||||
Send
|
||||
<span>
|
||||
<i class="material-icons">send</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
div.log {
|
||||
margin: 4px;
|
||||
padding: 8px 16px;
|
||||
width: calc(100% - 8px);
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
height: 256px;
|
||||
overflow: auto;
|
||||
|
||||
&,
|
||||
span {
|
||||
font-size: 18px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import section from "../components/section";
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": section
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
connectionState: false,
|
||||
url: "wss://echo.websocket.org",
|
||||
socket: null,
|
||||
communication: {
|
||||
log: null,
|
||||
input: ""
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
toggleConnectionVerb() {
|
||||
return !this.connectionState ? "Connect" : "Disconnect";
|
||||
},
|
||||
urlValid() {
|
||||
const pattern = new RegExp(
|
||||
"^(wss?:\\/\\/)?" +
|
||||
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" +
|
||||
"((\\d{1,3}\\.){3}\\d{1,3}))" +
|
||||
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" +
|
||||
"(\\?[;&a-z\\d%_.~+=-]*)?" +
|
||||
"(\\#[-a-z\\d_]*)?$",
|
||||
"i"
|
||||
);
|
||||
return pattern.test(this.url);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleConnection() {
|
||||
// If it is connecting:
|
||||
if (!this.connectionState) return this.connect();
|
||||
// Otherwise, it's disconnecting.
|
||||
else return this.disconnect();
|
||||
},
|
||||
connect() {
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: `Connecting to ${this.url}...`,
|
||||
source: "info",
|
||||
color: "var(--ac-color)"
|
||||
}
|
||||
];
|
||||
try {
|
||||
this.socket = new WebSocket(this.url);
|
||||
this.socket.onopen = event => {
|
||||
this.connectionState = true;
|
||||
this.communication.log = [
|
||||
{
|
||||
payload: `Connected to ${this.url}.`,
|
||||
source: "info",
|
||||
color: "var(--ac-color)",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
}
|
||||
];
|
||||
this.$toast.success("Connected", {
|
||||
icon: "sync"
|
||||
});
|
||||
};
|
||||
this.socket.onerror = event => {
|
||||
this.handleError();
|
||||
};
|
||||
this.socket.onclose = event => {
|
||||
this.connectionState = false;
|
||||
this.communication.log.push({
|
||||
payload: `Disconnected from ${this.url}.`,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
this.$toast.error("Disconnected", {
|
||||
icon: "sync_disabled"
|
||||
});
|
||||
};
|
||||
this.socket.onmessage = event => {
|
||||
this.communication.log.push({
|
||||
payload: event.data,
|
||||
source: "server",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
};
|
||||
} catch (ex) {
|
||||
this.handleError(ex);
|
||||
this.$toast.error("Something went wrong!", {
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
},
|
||||
disconnect() {
|
||||
if (this.socket != null) this.socket.close();
|
||||
},
|
||||
handleError(error) {
|
||||
this.disconnect();
|
||||
this.connectionState = false;
|
||||
this.communication.log.push({
|
||||
payload: `An error has occurred.`,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
if (error != null)
|
||||
this.communication.log.push({
|
||||
payload: error,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
},
|
||||
sendMessage() {
|
||||
const message = this.communication.input;
|
||||
this.socket.send(message);
|
||||
this.communication.log.push({
|
||||
payload: message,
|
||||
source: "client",
|
||||
ts: new Date().toLocaleTimeString()
|
||||
});
|
||||
this.communication.input = "";
|
||||
},
|
||||
collapse({ target }) {
|
||||
const el = target.parentNode.className;
|
||||
document.getElementsByClassName(el)[0].classList.toggle("hidden");
|
||||
},
|
||||
getSourcePrefix(source) {
|
||||
const sourceEmojis = {
|
||||
// Source used for info messages.
|
||||
info: "\tℹ️ [INFO]:\t",
|
||||
// Source used for client to server messages.
|
||||
client: "\t👽 [SENT]:\t",
|
||||
// Source used for server to client messages.
|
||||
server: "\t📥 [RECEIVED]:\t"
|
||||
};
|
||||
if (Object.keys(sourceEmojis).includes(source))
|
||||
return sourceEmojis[source];
|
||||
return "";
|
||||
}
|
||||
},
|
||||
updated: function() {
|
||||
this.$nextTick(function() {
|
||||
var divLog = document.getElementById("log");
|
||||
divLog.scrollBy(0, divLog.scrollHeight + 100);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue';
|
||||
import VTooltip from 'v-tooltip';
|
||||
import Vue from "vue";
|
||||
import VTooltip from "v-tooltip";
|
||||
|
||||
Vue.use(VTooltip);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import VuexPersistence from "vuex-persist";
|
||||
|
||||
export default ({
|
||||
store
|
||||
}) => {
|
||||
export default ({ store }) => {
|
||||
new VuexPersistence().plugin(store);
|
||||
}
|
||||
};
|
||||
|
||||
62
static/icons/alien-head.svg
Normal file
62
static/icons/alien-head.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 612.001 612.001"
|
||||
style="enable-background:new 0 0 612.001 612.001;"
|
||||
xml:space="preserve"
|
||||
width="512px"
|
||||
height="512px"
|
||||
class=""
|
||||
sodipodi:docname="logo.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)"><metadata
|
||||
id="metadata13"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs11" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1533"
|
||||
inkscape:window-height="845"
|
||||
id="namedview9"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.921875"
|
||||
inkscape:cx="380.48543"
|
||||
inkscape:cy="276.92414"
|
||||
inkscape:window-x="67"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" /> <g
|
||||
id="g3826"
|
||||
transform="translate(-516.40798,-163.88978)"><circle
|
||||
transform="scale(1,-1)"
|
||||
style="stroke-width:1.19531453"
|
||||
r="178.70923"
|
||||
cy="-501.55591"
|
||||
cx="822.40845"
|
||||
id="circle3814" /><g
|
||||
id="g3820"
|
||||
transform="translate(516.40798,163.89028)"><g
|
||||
id="g3818"><path
|
||||
id="path3816"
|
||||
data-old_color="#202124"
|
||||
class="active-path"
|
||||
data-original="#202124"
|
||||
d="M 64.601,236.822 C 64.601,394.256 192.786,612 306.001,612 412.582,612 547.4,394.256 547.4,236.822 547.4,79.388 439.322,0 306,0 172.678,0 64.601,79.388 64.601,236.822 Z m 304.12,116.415 c 29.475,-29.475 70.598,-40.195 108.552,-32.173 8.021,37.954 -2.698,79.077 -32.173,108.552 -29.475,29.475 -70.598,40.195 -108.552,32.173 -8.022,-37.955 2.698,-79.078 32.173,-108.552 z M 134.727,321.063 c 37.954,-8.021 79.077,2.698 108.552,32.173 29.475,29.475 40.195,70.598 32.173,108.552 -37.954,8.021 -79.077,-2.698 -108.552,-32.173 -29.475,-29.476 -40.194,-70.598 -32.173,-108.552 z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#50fa7b" /></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 11 KiB |
@@ -13,10 +13,10 @@
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 20 20"
|
||||
viewBox="0 0 24 24"
|
||||
xml:space="preserve"
|
||||
width="20"
|
||||
height="20"
|
||||
width="24"
|
||||
height="24"
|
||||
sodipodi:docname="github.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||
id="metadata13"><rdf:RDF><cc:Work
|
||||
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user