Compare commits
1546 Commits
v1.9.9
...
bug/withDe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
407dad8c7f | ||
|
|
9f944506e0 | ||
|
|
114c37645a | ||
|
|
8f9bb621b8 | ||
|
|
48a6c87d9d | ||
|
|
f28b55dd4d | ||
|
|
8a8b4b0245 | ||
|
|
0afbc57012 | ||
|
|
c651f2440f | ||
|
|
8c05084994 | ||
|
|
6813be47f0 | ||
|
|
30327e8d27 | ||
|
|
8096ed300d | ||
|
|
4a8efbf426 | ||
|
|
7a6d117a76 | ||
|
|
73568043f1 | ||
|
|
76a3b35e9e | ||
|
|
feb1991da3 | ||
|
|
2bee4342b8 | ||
|
|
7e9fc486f2 | ||
|
|
1d99b79926 | ||
|
|
eb8347f942 | ||
|
|
d383b48916 | ||
|
|
e88c40db0a | ||
|
|
f228f37bb8 | ||
|
|
503a54fc5e | ||
|
|
48b21aa0bf | ||
|
|
ca40cc5271 | ||
|
|
1c641c6d11 | ||
|
|
32b362f9cc | ||
|
|
103ef8ee0d | ||
|
|
4a6239e017 | ||
|
|
1f637edd36 | ||
|
|
25878b9bb1 | ||
|
|
521a96bffb | ||
|
|
ead1f3954f | ||
|
|
0ac84b58e3 | ||
|
|
a2f1e37ad2 | ||
|
|
373343fea1 | ||
|
|
2ef99026e5 | ||
|
|
29aff9accc | ||
|
|
7d7f628f6e | ||
|
|
8f6cf07e82 | ||
|
|
245b8a6e3c | ||
|
|
a967100be8 | ||
|
|
a9bca8e1f8 | ||
|
|
7de8e6be5e | ||
|
|
6b70a39f02 | ||
|
|
d538d722d7 | ||
|
|
13bd831c5f | ||
|
|
9b297ba882 | ||
|
|
be6c802745 | ||
|
|
564cce2462 | ||
|
|
8f166b8b3f | ||
|
|
fe7192ae61 | ||
|
|
6d54f21c1e | ||
|
|
87f8f61163 | ||
|
|
510ba376e5 | ||
|
|
40c88b3e35 | ||
|
|
7c65da4cf3 | ||
|
|
0f07c47e9f | ||
|
|
17c45fee11 | ||
|
|
a63c0817cc | ||
|
|
68aa54bdb7 | ||
|
|
41cb6eb190 | ||
|
|
61e5a48b02 | ||
|
|
9e4d7df7d0 | ||
|
|
e83dbc2e5c | ||
|
|
03ab6a208d | ||
|
|
dbd39ba0d8 | ||
|
|
136b1ff63b | ||
|
|
69a6207a4d | ||
|
|
9e74a8c2e7 | ||
|
|
ad76d100ee | ||
|
|
235968073a | ||
|
|
ad7b8da37e | ||
|
|
7d3e1a700f | ||
|
|
d3a1898dad | ||
|
|
45e508fc36 | ||
|
|
8edad7ded7 | ||
|
|
75e1adb7b3 | ||
|
|
22ac13f2f0 | ||
|
|
7f0246eb47 | ||
|
|
5f0800760f | ||
|
|
6db99c9e37 | ||
|
|
3b2cabd3f3 | ||
|
|
b0dd6b0bd6 | ||
|
|
874b846e60 | ||
|
|
dbe2525c6f | ||
|
|
afd414fa3f | ||
|
|
94763dcb31 | ||
|
|
6314740f46 | ||
|
|
d7332120e3 | ||
|
|
8c74fe9925 | ||
|
|
c74ddeb530 | ||
|
|
e31c0a9d02 | ||
|
|
d9e5d4aec5 | ||
|
|
c1ee8f5dd0 | ||
|
|
dd59de3de0 | ||
|
|
511a3c55f3 | ||
|
|
c9021ab3ca | ||
|
|
a765c4a7cc | ||
|
|
ea99732474 | ||
|
|
6c64ffe833 | ||
|
|
5fa6c6cdb3 | ||
|
|
d94759870e | ||
|
|
f0a6fc641a | ||
|
|
7ba00bee0b | ||
|
|
dc2bdf81b9 | ||
|
|
187a30abac | ||
|
|
5b824ccb17 | ||
|
|
3bdf2baf97 | ||
|
|
9af8a24a89 | ||
|
|
57c4759bdb | ||
|
|
d9d7261bc5 | ||
|
|
a12315d81a | ||
|
|
9f0956556f | ||
|
|
748318d44e | ||
|
|
ff3062cdfc | ||
|
|
48d67fe7e1 | ||
|
|
2c9918f9a7 | ||
|
|
ee03952201 | ||
|
|
43dcd3c443 | ||
|
|
8f8c42a92a | ||
|
|
6496aded25 | ||
|
|
eacf8113af | ||
|
|
c4a1527153 | ||
|
|
4a89a6aafc | ||
|
|
52539b084d | ||
|
|
8d5bd051a1 | ||
|
|
3809e9853e | ||
|
|
5f795acd61 | ||
|
|
17c550404f | ||
|
|
a840079119 | ||
|
|
2761894164 | ||
|
|
6b8bc618dc | ||
|
|
258f79604f | ||
|
|
81ae70ee04 | ||
|
|
6b02d290a5 | ||
|
|
7ab1bbaf62 | ||
|
|
079083d0f2 | ||
|
|
0504707aab | ||
|
|
fb4aab875d | ||
|
|
7bb32ecf7e | ||
|
|
e129a5c179 | ||
|
|
8045f26c19 | ||
|
|
86516421b5 | ||
|
|
bce88ccd44 | ||
|
|
66d408b7db | ||
|
|
297bf3205f | ||
|
|
7366b32349 | ||
|
|
b7e0169c9b | ||
|
|
6b6f85cc7e | ||
|
|
2c014a2f4b | ||
|
|
d6df675821 | ||
|
|
427baf4c79 | ||
|
|
4f2b682341 | ||
|
|
e03f888cb2 | ||
|
|
513396d498 | ||
|
|
8f04f0758b | ||
|
|
7a77bfc248 | ||
|
|
ddd29374ea | ||
|
|
4b0d7a6c3d | ||
|
|
20bfc02a4e | ||
|
|
2511724b73 | ||
|
|
a851ee3fab | ||
|
|
40f6e6f8a8 | ||
|
|
e2fd104c2d | ||
|
|
a3eafa54fa | ||
|
|
b90b4a1910 | ||
|
|
247df4d5b9 | ||
|
|
4a3889a76e | ||
|
|
224a6e069c | ||
|
|
2c3097eeb7 | ||
|
|
3289ede0e8 | ||
|
|
ddf21c17d2 | ||
|
|
c9a24a0d28 | ||
|
|
fa4b130b18 | ||
|
|
8561a7547f | ||
|
|
e85f7b8232 | ||
|
|
2f91d25ed4 | ||
|
|
ae304b5af7 | ||
|
|
363d34b5e5 | ||
|
|
1c51f8b32e | ||
|
|
aaff07bba2 | ||
|
|
4bc38d5e0f | ||
|
|
fdfca00886 | ||
|
|
d872e393f8 | ||
|
|
686d8e5be7 | ||
|
|
4e30efd737 | ||
|
|
aa4935c505 | ||
|
|
0e381ab850 | ||
|
|
4a03ee4518 | ||
|
|
be414d8279 | ||
|
|
c47b1f2413 | ||
|
|
ba9ee052a6 | ||
|
|
b1c6708762 | ||
|
|
0ba31b6c79 | ||
|
|
14f402f186 | ||
|
|
b811e97ea5 | ||
|
|
6d167ce1d6 | ||
|
|
9ac1d23fd9 | ||
|
|
dea3a34e3d | ||
|
|
39de34f083 | ||
|
|
02d5f0fdf3 | ||
|
|
0bb7cbe8d9 | ||
|
|
3e3b88b8c2 | ||
|
|
1438beb93b | ||
|
|
c1ec5dc60d | ||
|
|
3f513f2f4d | ||
|
|
d1b573f6f9 | ||
|
|
0e08abc46f | ||
|
|
49bdf9f203 | ||
|
|
b3e9df4f3d | ||
|
|
3b8cf4a60a | ||
|
|
b7ccb9a34c | ||
|
|
b8ffa872c7 | ||
|
|
ef866f7851 | ||
|
|
f27515bf1d | ||
|
|
ddf74c5d7c | ||
|
|
8ea12695b3 | ||
|
|
d6324e6ba6 | ||
|
|
2325982801 | ||
|
|
b103c45e65 | ||
|
|
409989eddb | ||
|
|
3f5fcae280 | ||
|
|
d5123c793a | ||
|
|
e6707c1e4a | ||
|
|
41be5cc4a8 | ||
|
|
e82a4a1d23 | ||
|
|
e30e4edfce | ||
|
|
bd72ef7950 | ||
|
|
539034df2a | ||
|
|
6da6afc5a1 | ||
|
|
fea523972d | ||
|
|
5cfc6c2949 | ||
|
|
6dd0c25d49 | ||
|
|
ab9b3e47b9 | ||
|
|
33ebdf2831 | ||
|
|
ef95939763 | ||
|
|
0394deaeef | ||
|
|
337a60c8a4 | ||
|
|
47b341d50e | ||
|
|
a5fd39adf8 | ||
|
|
5772117dc8 | ||
|
|
f73c8a45d9 | ||
|
|
109d4190ae | ||
|
|
317de82be6 | ||
|
|
3604d69463 | ||
|
|
96cf774652 | ||
|
|
d2b39976ba | ||
|
|
06161bc963 | ||
|
|
2ab1d3dbfa | ||
|
|
ecdc7919ae | ||
|
|
a24541ac2b | ||
|
|
d832690548 | ||
|
|
2844710ea8 | ||
|
|
84ad4071ad | ||
|
|
7f501241f0 | ||
|
|
5dcfa66c5d | ||
|
|
f428a21279 | ||
|
|
ccdd4963cd | ||
|
|
2092a3729c | ||
|
|
a628420adb | ||
|
|
23de147ca1 | ||
|
|
93f55f5619 | ||
|
|
b10933898f | ||
|
|
1727b754d4 | ||
|
|
ffb0c12c08 | ||
|
|
c332808fe4 | ||
|
|
8f0538c886 | ||
|
|
e6bb7e2ca9 | ||
|
|
2a012520d0 | ||
|
|
c71333d9cb | ||
|
|
728515c225 | ||
|
|
e0f88e01f9 | ||
|
|
1aa94a12c0 | ||
|
|
de2d3361a7 | ||
|
|
680937e50b | ||
|
|
6751c50514 | ||
|
|
0c389701fe | ||
|
|
9454d8c100 | ||
|
|
166f9e817b | ||
|
|
d2865c637c | ||
|
|
9698932bde | ||
|
|
44026fcd41 | ||
|
|
d938af0c2c | ||
|
|
fd658400a6 | ||
|
|
dcbb17b164 | ||
|
|
49741875bd | ||
|
|
0fcd9733ff | ||
|
|
62d50169d7 | ||
|
|
1d3d5a1e6a | ||
|
|
d309fa745e | ||
|
|
f7031992d5 | ||
|
|
fcdf68ea15 | ||
|
|
b0a6692179 | ||
|
|
e1e763575d | ||
|
|
4236d1179c | ||
|
|
09365bcabe | ||
|
|
be29ddcbd6 | ||
|
|
522194ca8d | ||
|
|
5af8f584f6 | ||
|
|
adc08f8865 | ||
|
|
0f39d54c3c | ||
|
|
9e6659e842 | ||
|
|
46a0f6e3f8 | ||
|
|
e90b26ebed | ||
|
|
4407f260ae | ||
|
|
d4392416c8 | ||
|
|
2d3cbd26b8 | ||
|
|
98b9660956 | ||
|
|
4e8a4e8914 | ||
|
|
96bcbc80f8 | ||
|
|
1dfc8e2973 | ||
|
|
311886f6c9 | ||
|
|
4a332f40e5 | ||
|
|
93a97a2f4c | ||
|
|
1dee098ca2 | ||
|
|
a07cc7e560 | ||
|
|
c26f7f5ebc | ||
|
|
5d801cf566 | ||
|
|
631b2d869e | ||
|
|
c02f54cc18 | ||
|
|
827a95515d | ||
|
|
9082152f1a | ||
|
|
0efbddeda4 | ||
|
|
b2e186957c | ||
|
|
d855e5cffb | ||
|
|
f3747edaa3 | ||
|
|
752932ef3d | ||
|
|
948cf9dae3 | ||
|
|
b2f93aa549 | ||
|
|
108f228edf | ||
|
|
fe6030140f | ||
|
|
003400cfa8 | ||
|
|
41a02f059d | ||
|
|
b4ed6fd107 | ||
|
|
36246da9e1 | ||
|
|
457b6b982c | ||
|
|
05a07dc4a1 | ||
|
|
85889c2cb9 | ||
|
|
be6ceaab04 | ||
|
|
f1b18688bb | ||
|
|
80c7decb81 | ||
|
|
3ef5a1e21a | ||
|
|
2eb0a4c754 | ||
|
|
10f5af5dda | ||
|
|
8b27ebb96b | ||
|
|
b28f82a881 | ||
|
|
c921606f3f | ||
|
|
c6c08f6c60 | ||
|
|
02cf620090 | ||
|
|
917550ff4d | ||
|
|
4a12cc76fa | ||
|
|
f4f74e223f | ||
|
|
8b4535c131 | ||
|
|
b15fd6c75a | ||
|
|
e1a25fa894 | ||
|
|
2bb3b71a70 | ||
|
|
4c55b9c304 | ||
|
|
639a629809 | ||
|
|
d6e3bd09b4 | ||
|
|
8d67a0d95f | ||
|
|
b9fc0175e7 | ||
|
|
dc5f52cc0d | ||
|
|
602aabdeb8 | ||
|
|
2f8aa79ec1 | ||
|
|
8af90432cf | ||
|
|
61da0733c2 | ||
|
|
33951482d5 | ||
|
|
4e8484ee7c | ||
|
|
071761a61e | ||
|
|
10a11d6725 | ||
|
|
c81178ae26 | ||
|
|
2bafae5397 | ||
|
|
6a1d201e0e | ||
|
|
8de544696d | ||
|
|
66c489da8f | ||
|
|
26c8f35688 | ||
|
|
c3e881ed77 | ||
|
|
2cf55cbb96 | ||
|
|
73f22abf56 | ||
|
|
dbae90a193 | ||
|
|
28aeac4533 | ||
|
|
162b3d6192 | ||
|
|
b016d3fd9d | ||
|
|
f64ff58dbc | ||
|
|
d4d3d96bbb | ||
|
|
a5197ee544 | ||
|
|
8a5fd4f745 | ||
|
|
12cd7940c6 | ||
|
|
0c2cec46a7 | ||
|
|
84457ddc86 | ||
|
|
d6d20e5d49 | ||
|
|
9acdca1059 | ||
|
|
66c4fd4d2f | ||
|
|
f2defb3a31 | ||
|
|
aa66c10608 | ||
|
|
28d20a9c61 | ||
|
|
94fcc0a6a9 | ||
|
|
91abdd5415 | ||
|
|
58e940d193 | ||
|
|
6991dd48f3 | ||
|
|
270a077539 | ||
|
|
ff326acfc8 | ||
|
|
af1446233c | ||
|
|
67d66e2b2f | ||
|
|
753ce5bd6e | ||
|
|
e7d71ef301 | ||
|
|
8430921e4e | ||
|
|
c938abf606 | ||
|
|
3addfe8d4b | ||
|
|
5276556837 | ||
|
|
e47ad94666 | ||
|
|
7065763c7c | ||
|
|
86489d95c2 | ||
|
|
f357c4f171 | ||
|
|
36e34fe667 | ||
|
|
dcc59f42fa | ||
|
|
f6fbff2b42 | ||
|
|
67ce20ef62 | ||
|
|
788e0dc851 | ||
|
|
e2b1c83698 | ||
|
|
15373be63e | ||
|
|
8c9cd079b7 | ||
|
|
6f67a97ade | ||
|
|
ada568cb75 | ||
|
|
b9fa254ab5 | ||
|
|
174ba90fb5 | ||
|
|
de8c7c1ca3 | ||
|
|
407a125533 | ||
|
|
d8881ba6a3 | ||
|
|
755540fb81 | ||
|
|
add358c752 | ||
|
|
91352ade20 | ||
|
|
39fbf4ef33 | ||
|
|
d9547c6654 | ||
|
|
04f9428267 | ||
|
|
ebd8f43219 | ||
|
|
f75b2e26a3 | ||
|
|
2e654c143f | ||
|
|
5bcee265a6 | ||
|
|
647599e5aa | ||
|
|
16b9a2b06e | ||
|
|
7da427c669 | ||
|
|
405e6c1e4e | ||
|
|
efbc21826b | ||
|
|
7a1f1c9df7 | ||
|
|
476bfbaef0 | ||
|
|
8231157cdb | ||
|
|
e397e3fb6f | ||
|
|
8f1cafc80d | ||
|
|
71619cf84b | ||
|
|
a3551c6719 | ||
|
|
e9043e6762 | ||
|
|
6b7d30a701 | ||
|
|
76eab2632e | ||
|
|
bba4d7fcd9 | ||
|
|
46eb7d6786 | ||
|
|
f5c84b57b2 | ||
|
|
0b12901344 | ||
|
|
5e3c303aaf | ||
|
|
f02b1639c3 | ||
|
|
94e0c3ef64 | ||
|
|
17366f5e6e | ||
|
|
f7a6f899d4 | ||
|
|
62205919a3 | ||
|
|
ee44a48994 | ||
|
|
fedc230c9f | ||
|
|
8796cec493 | ||
|
|
b3b76adec4 | ||
|
|
4362760461 | ||
|
|
bc9236c9a7 | ||
|
|
9e16cff62a | ||
|
|
b04d8dde2b | ||
|
|
2a23c95f34 | ||
|
|
cb9aa33ad0 | ||
|
|
31258b1108 | ||
|
|
6e9f16d8f1 | ||
|
|
d1b2539d67 | ||
|
|
c5f8ab3394 | ||
|
|
12c28f4efc | ||
|
|
f20fed444a | ||
|
|
97b92ba35b | ||
|
|
91df36ccca | ||
|
|
294b1a5a7c | ||
|
|
5f27b59dc0 | ||
|
|
af0efc1e7f | ||
|
|
db25346505 | ||
|
|
1a4fc1f539 | ||
|
|
1bea0a42d7 | ||
|
|
22e73a4d83 | ||
|
|
3ab3c09982 | ||
|
|
0fb0ae1826 | ||
|
|
08fe4ae71f | ||
|
|
883c787c0b | ||
|
|
732dc07ec5 | ||
|
|
c00c8f249e | ||
|
|
77d876d443 | ||
|
|
822e7b4630 | ||
|
|
275a7fc113 | ||
|
|
b08b06c4d4 | ||
|
|
7082eb27db | ||
|
|
a121445b1e | ||
|
|
95547108bf | ||
|
|
fc4e945a7e | ||
|
|
1fdb6488fd | ||
|
|
51142e5c77 | ||
|
|
57ce7ccfdf | ||
|
|
c1ce90dad0 | ||
|
|
ceb8bdf0fd | ||
|
|
b9c233cdf9 | ||
|
|
4f5de51104 | ||
|
|
c949783698 | ||
|
|
6af42e5e08 | ||
|
|
f312ed32be | ||
|
|
91fee8d2d0 | ||
|
|
c23a4bf75d | ||
|
|
e38af0bebe | ||
|
|
b968756be2 | ||
|
|
ed5b4b6dc5 | ||
|
|
956ca44ef0 | ||
|
|
3534e133af | ||
|
|
7744e2eb03 | ||
|
|
078c5e8d8a | ||
|
|
6c3607d7e7 | ||
|
|
c04435108b | ||
|
|
e5c8d05850 | ||
|
|
bf0278aa00 | ||
|
|
33d05eaa77 | ||
|
|
5b074409fc | ||
|
|
bdc3cf2150 | ||
|
|
c3b7d45502 | ||
|
|
2ef4bc1b36 | ||
|
|
2f87549dac | ||
|
|
c596012bb5 | ||
|
|
04c6faf5da | ||
|
|
b2f50547a9 | ||
|
|
e221741a6a | ||
|
|
fe43ae9e81 | ||
|
|
c5d4b9f0ae | ||
|
|
b9156cdf7f | ||
|
|
c6a3b784b5 | ||
|
|
33e2afab7b | ||
|
|
5eb6fb38e0 | ||
|
|
c2ae333a12 | ||
|
|
d462242927 | ||
|
|
c1d2f12e48 | ||
|
|
74d472e76e | ||
|
|
bfc0282e49 | ||
|
|
8c70f83297 | ||
|
|
1e6e826426 | ||
|
|
872ba2d48e | ||
|
|
7acde1c174 | ||
|
|
bb6d9a88ec | ||
|
|
29305a8e1e | ||
|
|
9f639378ec | ||
|
|
92a42269e7 | ||
|
|
691629890f | ||
|
|
2051b8788a | ||
|
|
a129d7eb13 | ||
|
|
e9eee0703d | ||
|
|
11816111ea | ||
|
|
08e3cffff8 | ||
|
|
ab63735a89 | ||
|
|
560c6e0d6a | ||
|
|
f688099f2c | ||
|
|
877f1e343a | ||
|
|
32660c5251 | ||
|
|
a1dc224007 | ||
|
|
55bcb34fd5 | ||
|
|
b18f7af28f | ||
|
|
c273ded97d | ||
|
|
971b35a252 | ||
|
|
dad18aabcb | ||
|
|
0738ad1c15 | ||
|
|
829e116e04 | ||
|
|
384e4ce43e | ||
|
|
da3f55c910 | ||
|
|
87833f1e9f | ||
|
|
ba5d1666d6 | ||
|
|
b612feea41 | ||
|
|
da74fb5241 | ||
|
|
aa8b4231e2 | ||
|
|
6b4bd44807 | ||
|
|
058c1090e3 | ||
|
|
6aa6fb5c0a | ||
|
|
cbf99d2daf | ||
|
|
a71f70644f | ||
|
|
b41d08007a | ||
|
|
0e1900b36a | ||
|
|
e6c503253b | ||
|
|
846c1426cf | ||
|
|
c97f8aa148 | ||
|
|
b99c4fe5c1 | ||
|
|
42d2dd284f | ||
|
|
b7e3f2a4ee | ||
|
|
6599c5f5bf | ||
|
|
13f6e5ff43 | ||
|
|
4d76c83328 | ||
|
|
fee19f3d7a | ||
|
|
200fc738e8 | ||
|
|
05294ac556 | ||
|
|
93dfed74f8 | ||
|
|
3d963a7719 | ||
|
|
92abbc4ce6 | ||
|
|
d111893302 | ||
|
|
967ad773eb | ||
|
|
b036863dda | ||
|
|
ead9c23d9e | ||
|
|
8c60a4c032 | ||
|
|
d08f15430c | ||
|
|
f71d93a684 | ||
|
|
44d1ad7422 | ||
|
|
346ece94b7 | ||
|
|
3a96eb003b | ||
|
|
3bd78869a0 | ||
|
|
4b416586b7 | ||
|
|
20ffbe906f | ||
|
|
b2baeb0a5d | ||
|
|
b524af174a | ||
|
|
a4caeac584 | ||
|
|
b03565a816 | ||
|
|
d0c0a543cf | ||
|
|
17192c898b | ||
|
|
7d913b0ee7 | ||
|
|
db4521db5a | ||
|
|
10b7da0558 | ||
|
|
05f19cbb60 | ||
|
|
6d9ac84859 | ||
|
|
d9834d0fd1 | ||
|
|
d84e2a8e9b | ||
|
|
13dbeaba35 | ||
|
|
b615fe7529 | ||
|
|
8a268ee6de | ||
|
|
2ae2acc003 | ||
|
|
13d89b323e | ||
|
|
6acb9be48c | ||
|
|
d9d61b5b1d | ||
|
|
2ee65e69dc | ||
|
|
fd3b5ecf08 | ||
|
|
6d67b1e51f | ||
|
|
79cea34fac | ||
|
|
b0b758a9c6 | ||
|
|
462d17de17 | ||
|
|
9afe415c2d | ||
|
|
1e4bb65db2 | ||
|
|
fcb194d08a | ||
|
|
7354951d5a | ||
|
|
369bca90fc | ||
|
|
5275d365cb | ||
|
|
e2fc056488 | ||
|
|
98d2b2ee9c | ||
|
|
9441c76a12 | ||
|
|
1ef7b52425 | ||
|
|
d88725b998 | ||
|
|
a48ca25fa2 | ||
|
|
28d636fa0d | ||
|
|
2904084853 | ||
|
|
a1eac2f348 | ||
|
|
203b45f4d5 | ||
|
|
2891da3c74 | ||
|
|
09d552b17a | ||
|
|
b524fa7248 | ||
|
|
e138a5f846 | ||
|
|
82a5f56522 | ||
|
|
4f71e801d5 | ||
|
|
d5cb9f135f | ||
|
|
1a4d3dc91a | ||
|
|
78fccc8583 | ||
|
|
37ad8e08fc | ||
|
|
e60e8545a1 | ||
|
|
5258db345e | ||
|
|
c3921bebf2 | ||
|
|
08d1f01e8e | ||
|
|
8bf9a1e821 | ||
|
|
8debb65d89 | ||
|
|
397cab9032 | ||
|
|
449c87802f | ||
|
|
3862a926ab | ||
|
|
56c2e1094d | ||
|
|
4508e309c2 | ||
|
|
f0aaca2639 | ||
|
|
4c2a9c1229 | ||
|
|
4de55c39dd | ||
|
|
84b94f3091 | ||
|
|
2e94969a41 | ||
|
|
09eabbdcea | ||
|
|
233214cb30 | ||
|
|
f694f1ad36 | ||
|
|
dd004c921e | ||
|
|
8597c04ff1 | ||
|
|
5f5f086dfc | ||
|
|
04cd5b0981 | ||
|
|
22772ac10f | ||
|
|
d4234f0837 | ||
|
|
982f467572 | ||
|
|
f4ffb8e357 | ||
|
|
cd908ae1b2 | ||
|
|
1573ddad1b | ||
|
|
2ace83bcaf | ||
|
|
88ccda8fd1 | ||
|
|
ae8cf656a3 | ||
|
|
68c00433f5 | ||
|
|
f1ad7b9ef5 | ||
|
|
27cf8660d8 | ||
|
|
7105e7a86f | ||
|
|
e52ed7a5ce | ||
|
|
393e6896ec | ||
|
|
6ebfb1f109 | ||
|
|
590c21209c | ||
|
|
e7b58b27e8 | ||
|
|
a2a9bae0e3 | ||
|
|
3ef8e677c7 | ||
|
|
86c9e09782 | ||
|
|
fa8662f17d | ||
|
|
dcf34ea92c | ||
|
|
9ece8adda8 | ||
|
|
c302f7e1a9 | ||
|
|
2907d69dfc | ||
|
|
eeddb6a97e | ||
|
|
7ec8539540 | ||
|
|
5e740a6ed6 | ||
|
|
a4032836c3 | ||
|
|
ffc891f38e | ||
|
|
93ea80a3e3 | ||
|
|
68ff422a5f | ||
|
|
54d21c4950 | ||
|
|
bfd9327e74 | ||
|
|
498d0e18b9 | ||
|
|
b5e524e513 | ||
|
|
c512c8d297 | ||
|
|
2e092d34a6 | ||
|
|
d94c8aec51 | ||
|
|
c7062ad613 | ||
|
|
08d0765cfb | ||
|
|
1f0a3e97ae | ||
|
|
36745d79db | ||
|
|
c6490818d0 | ||
|
|
f1ef403c83 | ||
|
|
cad7ecf760 | ||
|
|
8f7fcfa147 | ||
|
|
a3bde5797a | ||
|
|
a070bed7c9 | ||
|
|
207dc2b6a6 | ||
|
|
fee0a8cec1 | ||
|
|
6635d449a5 | ||
|
|
99b1699ade | ||
|
|
20d38e247b | ||
|
|
7c8ab6fd4a | ||
|
|
7bb570cdc7 | ||
|
|
fc5a5aad8d | ||
|
|
93d373c032 | ||
|
|
fde5b0717d | ||
|
|
069b26bdc4 | ||
|
|
8f3e4cfdba | ||
|
|
ba7f5d3dc3 | ||
|
|
42ce183510 | ||
|
|
977bad2156 | ||
|
|
fd4f49cf8e | ||
|
|
1b540c0e57 | ||
|
|
0e443b3a43 | ||
|
|
eaf0da3d00 | ||
|
|
dd41ac3888 | ||
|
|
a0e26c6c4f | ||
|
|
6ac7ce2c73 | ||
|
|
7b3dd697bb | ||
|
|
48e562dcee | ||
|
|
86bd4aa568 | ||
|
|
ed91486d53 | ||
|
|
913b073ba4 | ||
|
|
b1418c081c | ||
|
|
6b373bee47 | ||
|
|
256d4b3e07 | ||
|
|
dc5a09bebc | ||
|
|
ababd79da2 | ||
|
|
5e21210962 | ||
|
|
3e3da2f27b | ||
|
|
b55439ce44 | ||
|
|
ff791098d8 | ||
|
|
5abf837e95 | ||
|
|
6bcf1a3522 | ||
|
|
b0f055567d | ||
|
|
1ee2fecbc2 | ||
|
|
04b0cd2d3b | ||
|
|
0439e6811b | ||
|
|
a130cfcadb | ||
|
|
77fcc14043 | ||
|
|
fb93db6ad4 | ||
|
|
47cf321eba | ||
|
|
b0ab1b048d | ||
|
|
aaec87d7be | ||
|
|
3198bc6b2c | ||
|
|
60be228c33 | ||
|
|
22c3ffcc02 | ||
|
|
b750ccd46f | ||
|
|
0dd0d262d6 | ||
|
|
9a83938c75 | ||
|
|
9b5b3cc202 | ||
|
|
729f341164 | ||
|
|
d5c5fb7435 | ||
|
|
01b8001d4c | ||
|
|
3992650fd6 | ||
|
|
a2354b5e9e | ||
|
|
43b7e94974 | ||
|
|
cdfce9e2b8 | ||
|
|
9bd86f0564 | ||
|
|
1c0b5b25ed | ||
|
|
6d33132705 | ||
|
|
3e5629e738 | ||
|
|
41eb4f8395 | ||
|
|
6ddff58ba5 | ||
|
|
5bb9b1b675 | ||
|
|
f4f29b8520 | ||
|
|
257e2db651 | ||
|
|
a853c13bcd | ||
|
|
96c23200f7 | ||
|
|
7c2becd3a4 | ||
|
|
a85729b4cf | ||
|
|
bd3cec18d8 | ||
|
|
7dcd96350a | ||
|
|
2e1c0ae77a | ||
|
|
6a6754c7da | ||
|
|
1fb2c35984 | ||
|
|
02d3b45efc | ||
|
|
4ce7f0b487 | ||
|
|
3559d98df1 | ||
|
|
0a4251825b | ||
|
|
499cd9d81b | ||
|
|
10a6b96f13 | ||
|
|
4e3d9187f7 | ||
|
|
0921cccb4c | ||
|
|
07a505c365 | ||
|
|
c3411ba0cf | ||
|
|
5246284e32 | ||
|
|
5fe8fce89d | ||
|
|
0cedc9ed51 | ||
|
|
7cbb61bdf8 | ||
|
|
b357dc8e2f | ||
|
|
f745fef6c2 | ||
|
|
dfe810fff4 | ||
|
|
111a947413 | ||
|
|
85c6932f8f | ||
|
|
ced2f1b911 | ||
|
|
685f9d506d | ||
|
|
34cd604a91 | ||
|
|
2ffd0be03f | ||
|
|
58296505a8 | ||
|
|
779f5e9af4 | ||
|
|
2351f64e6f | ||
|
|
03987da05b | ||
|
|
07e2cb5cd9 | ||
|
|
9b43f9a40c | ||
|
|
43b0225fba | ||
|
|
da491a2967 | ||
|
|
be67986123 | ||
|
|
af9c7e0aff | ||
|
|
27e061c355 | ||
|
|
da94a94d71 | ||
|
|
970c2e56d8 | ||
|
|
766c31fa56 | ||
|
|
50fa1c8f0a | ||
|
|
e361d66c88 | ||
|
|
f1203efa62 | ||
|
|
45b9f2e1bf | ||
|
|
538600354d | ||
|
|
5c9093823b | ||
|
|
7b6c4f71a2 | ||
|
|
7e9ae69f38 | ||
|
|
ddbc9ca61b | ||
|
|
d9aa965069 | ||
|
|
6ea9d16f87 | ||
|
|
5bfeb541fc | ||
|
|
c3f713c0bd | ||
|
|
97979a6ab5 | ||
|
|
055adf9fe0 | ||
|
|
e94adc835d | ||
|
|
50bb8c4fdd | ||
|
|
aa0a340b95 | ||
|
|
41d21bc0ff | ||
|
|
2de4b75b89 | ||
|
|
b52cc9e2b4 | ||
|
|
fea3843714 | ||
|
|
5df5d59f50 | ||
|
|
062dd4f889 | ||
|
|
948b027059 | ||
|
|
4d057a39ed | ||
|
|
68c4efb1d3 | ||
|
|
eda8c7da41 | ||
|
|
ac1937f9be | ||
|
|
5f0f7693bb | ||
|
|
9ad22e2600 | ||
|
|
fe8bbc6cc8 | ||
|
|
795c5f8263 | ||
|
|
66bb9dc42b | ||
|
|
70a350fdac | ||
|
|
a795fc4310 | ||
|
|
790b743e42 | ||
|
|
21aeded2ea | ||
|
|
baf6d6bd29 | ||
|
|
2e213a8692 | ||
|
|
455cccebb5 | ||
|
|
9e602394cf | ||
|
|
46ebd49936 | ||
|
|
fcac750ad7 | ||
|
|
8cd3acd205 | ||
|
|
bc95a028ce | ||
|
|
5fb457d331 | ||
|
|
454c11a12c | ||
|
|
3ac9a418e6 | ||
|
|
c98de5988e | ||
|
|
ca5df588b7 | ||
|
|
08a41691af | ||
|
|
665f272a0e | ||
|
|
2591e3ab67 | ||
|
|
44df9b3be8 | ||
|
|
40ddfa8def | ||
|
|
cc27c552af | ||
|
|
7f248da0b3 | ||
|
|
79a0002594 | ||
|
|
a6c015bcc5 | ||
|
|
692d98cb55 | ||
|
|
d9ddc184cb | ||
|
|
e424d06026 | ||
|
|
f9821e5f80 | ||
|
|
5bd53dc093 | ||
|
|
204834872e | ||
|
|
a24049aa17 | ||
|
|
9cb9e9e7b6 | ||
|
|
3e7a766d12 | ||
|
|
02debdc28b | ||
|
|
e104fe3021 | ||
|
|
dda40537cc | ||
|
|
217269467c | ||
|
|
5f193680c9 | ||
|
|
c0ef713c0b | ||
|
|
3533aa391a | ||
|
|
ec90365427 | ||
|
|
78f7a9ba06 | ||
|
|
7793dd8b3e | ||
|
|
76866f78f0 | ||
|
|
8e3ecb4c25 | ||
|
|
1b9b2ac4c9 | ||
|
|
489f14b88f | ||
|
|
8f09c82763 | ||
|
|
2b8cda40a2 | ||
|
|
4656d67fcf | ||
|
|
e7dd67deaa | ||
|
|
b9c3219094 | ||
|
|
f55a995c0a | ||
|
|
ee378a128c | ||
|
|
c1f083d19f | ||
|
|
2ff0f97295 | ||
|
|
dd3b51d0b7 | ||
|
|
853acfda2c | ||
|
|
c756be54a1 | ||
|
|
3bbf334f99 | ||
|
|
c6c8b7da6a | ||
|
|
8a30d3ed42 | ||
|
|
a5a6b864a5 | ||
|
|
97b7320939 | ||
|
|
5c11bcb2c6 | ||
|
|
3888584b58 | ||
|
|
531a9c0bc8 | ||
|
|
84f8048e0a | ||
|
|
fdf96b0b63 | ||
|
|
86ddfe2c9f | ||
|
|
115ae93073 | ||
|
|
aa65cada6f | ||
|
|
ad9cdf3fa8 | ||
|
|
5c0035f4c3 | ||
|
|
37b4c67ead | ||
|
|
63998f4a23 | ||
|
|
efeec13794 | ||
|
|
6c49e5c86a | ||
|
|
ec57392424 | ||
|
|
8713aa7fd3 | ||
|
|
627811f28d | ||
|
|
3f743b4f61 | ||
|
|
d676b5a68d | ||
|
|
a6dfab5fbf | ||
|
|
b8bc110d33 | ||
|
|
597fd2c6d1 | ||
|
|
ab0bc3e927 | ||
|
|
e28373dae1 | ||
|
|
1bc57f159c | ||
|
|
6a8019c7a0 | ||
|
|
9b0dc5a849 | ||
|
|
283e1b0790 | ||
|
|
fc05a4bb78 | ||
|
|
88b32e317a | ||
|
|
6c61a70d58 | ||
|
|
5df3ebae22 | ||
|
|
ad252476ce | ||
|
|
8e5f6a3a96 | ||
|
|
9ff209bd32 | ||
|
|
505e61475d | ||
|
|
2c137a0eec | ||
|
|
b65ae89be6 | ||
|
|
432884a2af | ||
|
|
6878498b2e | ||
|
|
4d5332fef7 | ||
|
|
50a149b662 | ||
|
|
930838d676 | ||
|
|
f6c952ffb0 | ||
|
|
f6db530de2 | ||
|
|
db45f08905 | ||
|
|
516d53f9bf | ||
|
|
c7e1adf638 | ||
|
|
31147975c7 | ||
|
|
b9deec1487 | ||
|
|
de57208bab | ||
|
|
07e966c640 | ||
|
|
56982effcd | ||
|
|
8a52b0fbd6 | ||
|
|
2190a1b6fd | ||
|
|
c18c2ea9d4 | ||
|
|
5fce1118f6 | ||
|
|
64f64b9e31 | ||
|
|
b4ac527638 | ||
|
|
352f3af737 | ||
|
|
c7c221ad5e | ||
|
|
74adbae7ed | ||
|
|
8a5402932c | ||
|
|
e565f9bf72 | ||
|
|
942b86c647 | ||
|
|
1042310038 | ||
|
|
d0c261b9cd | ||
|
|
61396cbb15 | ||
|
|
87c6230ef2 | ||
|
|
347ad94a43 | ||
|
|
f1389cdba0 | ||
|
|
5399ddf6ac | ||
|
|
a724dc1207 | ||
|
|
c6af38f7dc | ||
|
|
4d0008186b | ||
|
|
df3df6697e | ||
|
|
13ff7b9088 | ||
|
|
196d252fec | ||
|
|
53dfdc440d | ||
|
|
988a99efb7 | ||
|
|
17da230d72 | ||
|
|
c8cdfc8885 | ||
|
|
0b00842c50 | ||
|
|
17e405a39e | ||
|
|
51bd3455cc | ||
|
|
e292af75ad | ||
|
|
ef4566eb95 | ||
|
|
e7d1ffb7ae | ||
|
|
4fded3ada3 | ||
|
|
dc5ca76d05 | ||
|
|
37bdd525ea | ||
|
|
3f03806455 | ||
|
|
ff7bb1f303 | ||
|
|
5fe1de170d | ||
|
|
72913ccece | ||
|
|
a6207e7edf | ||
|
|
2972ac6328 | ||
|
|
d90550438f | ||
|
|
aa0f08cba3 | ||
|
|
a1b39d4fbc | ||
|
|
091a160db3 | ||
|
|
03f6336ac5 | ||
|
|
24c4cfe586 | ||
|
|
ca0c858e0f | ||
|
|
2be9a0bdba | ||
|
|
d333a44e11 | ||
|
|
e8e855a36c | ||
|
|
09719a4ad3 | ||
|
|
c9e542d6d5 | ||
|
|
a32ce56295 | ||
|
|
279c8c14cc | ||
|
|
49bc2c2204 | ||
|
|
c036c945bd | ||
|
|
2e2cce9fcb | ||
|
|
7f33798789 | ||
|
|
e299433e6e | ||
|
|
c82cb67bb6 | ||
|
|
075e11a7e1 | ||
|
|
2b2c968a97 | ||
|
|
bffdb39c02 | ||
|
|
3dabd73e95 | ||
|
|
8ed30e7eda | ||
|
|
7e4297d9bf | ||
|
|
99e634711e | ||
|
|
07f370d6d2 | ||
|
|
d2dfb4c8df | ||
|
|
64ee01d9fe | ||
|
|
a8d5ab035d | ||
|
|
f58c6807d0 | ||
|
|
ca66b47014 | ||
|
|
290781e734 | ||
|
|
d403e10018 | ||
|
|
161a0e07f9 | ||
|
|
eb9b0c85a9 | ||
|
|
c7f7b96405 | ||
|
|
c5dff96f57 | ||
|
|
ff418ad55f | ||
|
|
6b0da0a568 | ||
|
|
56151982a7 | ||
|
|
c8185050f6 | ||
|
|
ed20b009a5 | ||
|
|
bf0bcf2f72 | ||
|
|
d81e80f273 | ||
|
|
6afed0e035 | ||
|
|
0a39e7b02f | ||
|
|
01a45f3085 | ||
|
|
c61764cd8d | ||
|
|
ce20f5d0a2 | ||
|
|
ffa9210286 | ||
|
|
025ead4fa3 | ||
|
|
82de03101b | ||
|
|
eae071f9b8 | ||
|
|
aadad482c4 | ||
|
|
cb8b98f707 | ||
|
|
603a888e2e | ||
|
|
9e3083cc5d | ||
|
|
ab9f2c0a66 | ||
|
|
e84888c27e | ||
|
|
4dfc91db0a | ||
|
|
5705df96f3 | ||
|
|
9ac0cf1406 | ||
|
|
71170a1c5d | ||
|
|
353978f115 | ||
|
|
e89065aafc | ||
|
|
ff35ccbaec | ||
|
|
529d5b0535 | ||
|
|
15ecb19c65 | ||
|
|
a64b32d12c | ||
|
|
f53ac25d90 | ||
|
|
de1d06528e | ||
|
|
ea227e09fa | ||
|
|
46cbd6dfd2 | ||
|
|
a61e6efdd4 | ||
|
|
6536d71566 | ||
|
|
d92412175c | ||
|
|
a3b7f9a739 | ||
|
|
5367a23112 | ||
|
|
3614f5b620 | ||
|
|
eac7954570 | ||
|
|
7d08da22b2 | ||
|
|
756acf1395 | ||
|
|
a2a03c6b52 | ||
|
|
8e63a84152 | ||
|
|
38b3197912 | ||
|
|
1d4576c7fd | ||
|
|
29a4dee91b | ||
|
|
9e166774ef | ||
|
|
dd40df4b37 | ||
|
|
afd07562b4 | ||
|
|
81a6d821c0 | ||
|
|
858ab252a6 | ||
|
|
a56e2bb577 | ||
|
|
27980cf7c7 | ||
|
|
5a7bcf32ea | ||
|
|
773423069b | ||
|
|
1e6773deb5 | ||
|
|
a95d37d610 | ||
|
|
bdf72eca70 | ||
|
|
3acb2a56a2 | ||
|
|
2f0e9d0681 | ||
|
|
bfa7eb0c19 | ||
|
|
27d71fb10f | ||
|
|
eeb1ecd2df | ||
|
|
f21b5768a5 | ||
|
|
ba08883623 | ||
|
|
b1587950c6 | ||
|
|
b473ed7cb7 | ||
|
|
2978c8adfe | ||
|
|
1f0111256b | ||
|
|
346b980a1d | ||
|
|
8000791e17 | ||
|
|
66077ea6d7 | ||
|
|
3d10a8f86a | ||
|
|
ebae9880dc | ||
|
|
2e72c16964 | ||
|
|
6ebaea395d | ||
|
|
257ee9928f | ||
|
|
2ac9f5918a | ||
|
|
9c4767bb68 | ||
|
|
cbd82988a8 | ||
|
|
d0a9445dfc | ||
|
|
cd6a9bf9a8 | ||
|
|
8afead4e39 | ||
|
|
f279471858 | ||
|
|
61fe518710 | ||
|
|
b774a59db2 | ||
|
|
63a1f52482 | ||
|
|
ab5ea5948b | ||
|
|
a8fd0fcd19 | ||
|
|
6098d55b57 | ||
|
|
9ba2f80e3a | ||
|
|
3acf71f253 | ||
|
|
ad51597bf5 | ||
|
|
60057eee6a | ||
|
|
3cfd0ba1f8 | ||
|
|
dec386840a | ||
|
|
355b9be3ff | ||
|
|
57627367f5 | ||
|
|
a5a812d3f4 | ||
|
|
5426273cf8 | ||
|
|
d9bab319e3 | ||
|
|
77862cdf9b | ||
|
|
b8d68ee359 | ||
|
|
307e434f91 | ||
|
|
f60b2eb8af | ||
|
|
9da1aa1421 | ||
|
|
4d3964daeb | ||
|
|
b80f9d3517 | ||
|
|
46981f73a9 | ||
|
|
9b6394627b | ||
|
|
3a34331cc2 | ||
|
|
95fe10b312 | ||
|
|
f8d4303531 | ||
|
|
7d49f75682 | ||
|
|
a84a8e1a6e | ||
|
|
1fc2181937 | ||
|
|
7411f01fb5 | ||
|
|
2c9c598dd7 | ||
|
|
3c00a3b1d0 | ||
|
|
660cdb575a | ||
|
|
f20876a028 | ||
|
|
b31866cf32 | ||
|
|
bde27c3880 | ||
|
|
80cb613888 | ||
|
|
23966b1103 | ||
|
|
2fd897064a | ||
|
|
846298210a | ||
|
|
f22e98ccca | ||
|
|
5cb6d10e3d | ||
|
|
5b4dd74503 | ||
|
|
cd3cec5d48 | ||
|
|
621888d6d4 | ||
|
|
3e7fcaf764 | ||
|
|
8fb44df59b | ||
|
|
5b205d6e4a | ||
|
|
dc98ef8b57 | ||
|
|
f9ae242792 | ||
|
|
438640d5b8 | ||
|
|
078b755fb6 | ||
|
|
501e01fe23 | ||
|
|
ce992fc205 | ||
|
|
8609dedafd | ||
|
|
66f124b397 | ||
|
|
21cf439210 | ||
|
|
91a7d20923 | ||
|
|
f49c2138de | ||
|
|
774853af7a | ||
|
|
7e30a4a3d4 | ||
|
|
b179731359 | ||
|
|
d6409d7152 | ||
|
|
eaeefafe39 | ||
|
|
59974edf80 | ||
|
|
37f914d1cc | ||
|
|
da92fd705b | ||
|
|
00505b8af8 | ||
|
|
22a3bba6ab | ||
|
|
b6b3cbcb9a | ||
|
|
de97048424 | ||
|
|
02471e6d60 | ||
|
|
edf58ee897 | ||
|
|
5ecfd278d4 | ||
|
|
3327179270 | ||
|
|
98462e8dca | ||
|
|
2f515164b3 | ||
|
|
3c8ceebedc | ||
|
|
26fe93dfc0 | ||
|
|
9113a826ce | ||
|
|
eca1baf53e | ||
|
|
6b3b8e6204 | ||
|
|
194a4f3adb | ||
|
|
054f27083b | ||
|
|
168d1dc678 | ||
|
|
8cc5c62665 | ||
|
|
82c874afb5 | ||
|
|
1afd0381c1 | ||
|
|
5f5589f1f1 | ||
|
|
1cee408113 | ||
|
|
92854b9e84 | ||
|
|
a4c5817b54 | ||
|
|
6c82680066 | ||
|
|
6135bc716e | ||
|
|
1639ea7552 | ||
|
|
bea8c89d9f | ||
|
|
fe1618d1d8 | ||
|
|
30250e1afa | ||
|
|
16f02e2544 | ||
|
|
123f4a3931 | ||
|
|
ce5fb78bcd | ||
|
|
2ccd053b0a | ||
|
|
3422ccef15 | ||
|
|
630663ec83 | ||
|
|
37c6bf3a3f | ||
|
|
1c2963a1e0 | ||
|
|
50a5b0f7db | ||
|
|
df933bb928 | ||
|
|
aa85108f1a | ||
|
|
c778a28f2b | ||
|
|
48142721ac | ||
|
|
826e3ebc5e | ||
|
|
0f31259c97 | ||
|
|
881d7fa5ee | ||
|
|
d479425333 | ||
|
|
cb944bcfb2 | ||
|
|
745683f2a8 | ||
|
|
e019935545 | ||
|
|
9601477bba | ||
|
|
0702ca3fc9 | ||
|
|
d9a9ae7e72 | ||
|
|
76eba3d638 | ||
|
|
69a448b4c1 | ||
|
|
a8d9a64f79 | ||
|
|
1546727612 | ||
|
|
dfdcc05d3e | ||
|
|
12941a107d | ||
|
|
a94d46e005 | ||
|
|
acb777c236 | ||
|
|
7e87cc1923 | ||
|
|
1ad23f4ed8 | ||
|
|
1bcba17f76 | ||
|
|
cb871f6a88 | ||
|
|
6d2c296a7d | ||
|
|
3434a1f5a6 | ||
|
|
f9c45d95e1 | ||
|
|
0bd051a7a9 | ||
|
|
d4789463ad | ||
|
|
7190a25550 | ||
|
|
04bc8308ac | ||
|
|
68c67fbd32 | ||
|
|
50fd8926d6 | ||
|
|
7e1e61f8af | ||
|
|
ee066d7859 | ||
|
|
54a48f9493 | ||
|
|
70fc0789d6 | ||
|
|
66cd75dce8 | ||
|
|
8b3b6a471b | ||
|
|
e707fcda05 | ||
|
|
a3574acabd | ||
|
|
f36db182f1 | ||
|
|
59e492b2a6 | ||
|
|
6380063978 | ||
|
|
2686873163 | ||
|
|
b2ef26600c | ||
|
|
e94fdcad32 | ||
|
|
2e423b101b | ||
|
|
91677a41d4 | ||
|
|
b747d0273c | ||
|
|
45fb612793 | ||
|
|
db081d3197 | ||
|
|
423eaa5462 | ||
|
|
f38dde160e | ||
|
|
782eb29aac | ||
|
|
5810c5544a | ||
|
|
b6eb581192 | ||
|
|
54171e566a | ||
|
|
b7a44ab11f | ||
|
|
4ea60d3431 | ||
|
|
9840ebfe33 | ||
|
|
af2b726a38 | ||
|
|
d425a1b2c0 | ||
|
|
687b0ac3bd | ||
|
|
3978dd9b07 | ||
|
|
d9f78b9d5c | ||
|
|
81e638a7e0 | ||
|
|
6dc906a604 | ||
|
|
005db4d0e8 | ||
|
|
41127da7d9 | ||
|
|
3111b8d395 | ||
|
|
89f2647ead | ||
|
|
084a432741 | ||
|
|
1f071d1608 | ||
|
|
3f75d0df56 | ||
|
|
ba108fe23a | ||
|
|
ab736cf975 | ||
|
|
c36239772e | ||
|
|
286090d58a | ||
|
|
69fda864db | ||
|
|
2709c5d332 | ||
|
|
da5fdd54bd | ||
|
|
92e7453c31 | ||
|
|
a59e1a2015 | ||
|
|
838eb845d4 | ||
|
|
f69fdc634a | ||
|
|
9c0e7272ff | ||
|
|
515afdc5ae | ||
|
|
f32fa485e0 | ||
|
|
a5187801b4 | ||
|
|
6cb11d6a61 | ||
|
|
1e91ca5eda | ||
|
|
210727ea0e | ||
|
|
f429585447 | ||
|
|
759a3f17cc | ||
|
|
de7d1c2baa | ||
|
|
8223011c3d | ||
|
|
53684d6fbb | ||
|
|
31cefd7dd0 | ||
|
|
993a8f83ff | ||
|
|
fd1e00986c | ||
|
|
62028d1273 | ||
|
|
240ae25a90 | ||
|
|
2bd242a164 | ||
|
|
4b87684611 | ||
|
|
3cd2a2a0f9 | ||
|
|
dc129eeb7d | ||
|
|
fb3f426688 | ||
|
|
f33ebbcce0 | ||
|
|
86922df61a | ||
|
|
2916ab502e | ||
|
|
9c24fcec0d | ||
|
|
8258997f3e | ||
|
|
ef714c3903 | ||
|
|
76c0874f0e | ||
|
|
9166f734c9 | ||
|
|
368421a498 | ||
|
|
c8d8c68b95 | ||
|
|
e68cd3c47f | ||
|
|
1732d7e707 | ||
|
|
da0d778b25 | ||
|
|
b8bd0cc63a | ||
|
|
5150e13121 | ||
|
|
b2f784eeff | ||
|
|
ad98284caf | ||
|
|
6915ea70ca | ||
|
|
26eb99d4a7 | ||
|
|
e1eb1be781 | ||
|
|
b536e191b1 | ||
|
|
f1b475b966 | ||
|
|
23a61d400d | ||
|
|
4ae8308663 | ||
|
|
c3d6343edb | ||
|
|
1059d2ddbd | ||
|
|
10ecc27bb5 | ||
|
|
0c3c540051 | ||
|
|
c44dba5e92 | ||
|
|
82da7d0b24 | ||
|
|
4dd522eda4 | ||
|
|
bbe300e847 | ||
|
|
dd667e5b4a | ||
|
|
6f013de8e4 | ||
|
|
96a6289911 | ||
|
|
e0970fc69d | ||
|
|
5cafc7bb37 | ||
|
|
2b755c0497 | ||
|
|
e3beae244d | ||
|
|
c2ec7be719 | ||
|
|
ed5d9132d3 | ||
|
|
8654db534b | ||
|
|
e20299eb94 | ||
|
|
1f65907f70 | ||
|
|
6a55c09328 | ||
|
|
9734e4d859 | ||
|
|
4f93bc5721 | ||
|
|
73dc0b1cfa | ||
|
|
e31979002f | ||
|
|
4be1e527fc | ||
|
|
45332b3202 | ||
|
|
cb0258cb58 | ||
|
|
f3d80f28fd | ||
|
|
4b73ee3d41 | ||
|
|
e0e2e0c2fb | ||
|
|
f2b977c941 | ||
|
|
9fe10fd9a2 | ||
|
|
6b444d280a | ||
|
|
10c2acf410 | ||
|
|
6bb5908aba | ||
|
|
f54b24b4da | ||
|
|
ef3c548c98 | ||
|
|
820690a073 | ||
|
|
63cf4f0045 | ||
|
|
deecbdd124 | ||
|
|
07f790dda0 | ||
|
|
a91b1ecf1f | ||
|
|
4f75dd30fe | ||
|
|
629ee3e33f | ||
|
|
54e06d26bc | ||
|
|
dda26b7a3f | ||
|
|
e86a17980b | ||
|
|
23068450ed | ||
|
|
3924370b3e | ||
|
|
f57ac978ae | ||
|
|
e68ad7331f | ||
|
|
9b5fb4bde1 | ||
|
|
a8f1fb5148 | ||
|
|
65185ecb77 | ||
|
|
14a68f24e4 | ||
|
|
16a93c2b7e | ||
|
|
925690fb00 | ||
|
|
dc279bc335 | ||
|
|
d1d759e86f | ||
|
|
82a30fbd25 | ||
|
|
12fea29f48 | ||
|
|
b602f7c03f | ||
|
|
a20ab981a7 | ||
|
|
9ae08cecf2 | ||
|
|
c9cd7b4344 | ||
|
|
24b587762a | ||
|
|
18a2fb38d1 | ||
|
|
5f47cdb763 | ||
|
|
e5a18b6e4c | ||
|
|
519976eb0b | ||
|
|
b630fbc641 | ||
|
|
2369a22d23 | ||
|
|
124d9ff269 | ||
|
|
1c95ac7f07 | ||
|
|
54169fe22b | ||
|
|
4707b78c1f | ||
|
|
95d91efe52 | ||
|
|
fc0c7f536b | ||
|
|
80f1cfc72d | ||
|
|
628012fb49 | ||
|
|
a29498e911 | ||
|
|
3d460b5862 | ||
|
|
4acb2a5d92 | ||
|
|
47c800b78b | ||
|
|
a20da8036f | ||
|
|
7897118fbd | ||
|
|
0246e09595 | ||
|
|
72ad37456a | ||
|
|
83ec0a6c76 | ||
|
|
63f77b0dc5 | ||
|
|
798d2e0e6b | ||
|
|
fd098b5440 | ||
|
|
11d30e409c | ||
|
|
6cb5ae246d | ||
|
|
60c76ba854 | ||
|
|
ebf7b32408 | ||
|
|
f343459236 | ||
|
|
d2a57864f0 | ||
|
|
8a07f281ff | ||
|
|
b7b721abd5 | ||
|
|
45f1e386cc | ||
|
|
3c3e8a6c31 | ||
|
|
ec99b48605 | ||
|
|
da9e599e8f | ||
|
|
b1d3fa3f42 | ||
|
|
7954672ef8 | ||
|
|
20a6039bd0 | ||
|
|
d7fe61bc3d | ||
|
|
b10406896a | ||
|
|
1f135e0721 | ||
|
|
e1bdc9ce33 | ||
|
|
e3654bb472 | ||
|
|
6418d6bcff | ||
|
|
9bf7c5013e | ||
|
|
1797e78ebd |
@@ -1,143 +0,0 @@
|
|||||||
{
|
|
||||||
"files": [
|
|
||||||
"README.md"
|
|
||||||
],
|
|
||||||
"imageSize": 100,
|
|
||||||
"commit": false,
|
|
||||||
"contributors": [
|
|
||||||
{
|
|
||||||
"login": "liyasthomas",
|
|
||||||
"name": "Liyas Thomas",
|
|
||||||
"avatar_url": "https://avatars1.githubusercontent.com/u/10395817?v=4",
|
|
||||||
"profile": "https://liyasthomas.web.app",
|
|
||||||
"contributions": [
|
|
||||||
"code",
|
|
||||||
"design"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "NBTX",
|
|
||||||
"name": "John Harker",
|
|
||||||
"avatar_url": "https://avatars3.githubusercontent.com/u/43181178?v=4",
|
|
||||||
"profile": "https://github.com/NBTX",
|
|
||||||
"contributions": [
|
|
||||||
"code",
|
|
||||||
"design"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "larouxn",
|
|
||||||
"name": "Nicholas La Roux",
|
|
||||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1557529?v=4",
|
|
||||||
"profile": "https://nicholaslaroux.com",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "yubathom",
|
|
||||||
"name": "Thomas Yuba",
|
|
||||||
"avatar_url": "https://avatars3.githubusercontent.com/u/4117768?v=4",
|
|
||||||
"profile": "https://github.com/yubathom",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "nickpalenchar",
|
|
||||||
"name": "Nick Palenchar",
|
|
||||||
"avatar_url": "https://avatars1.githubusercontent.com/u/7539781?v=4",
|
|
||||||
"profile": "http://www.linkedin.com/in/nickpalenchar",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "AndrewBastin",
|
|
||||||
"name": "Andrew Bastin",
|
|
||||||
"avatar_url": "https://avatars2.githubusercontent.com/u/9131943?v=4",
|
|
||||||
"profile": "https://github.com/AndrewBastin",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "vlad0337187",
|
|
||||||
"name": "Vladislav",
|
|
||||||
"avatar_url": "https://avatars1.githubusercontent.com/u/12682937?v=4",
|
|
||||||
"profile": "https://github.com/vlad0337187",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "izerozlu",
|
|
||||||
"name": "izerozlu",
|
|
||||||
"avatar_url": "https://avatars3.githubusercontent.com/u/17386157?v=4",
|
|
||||||
"profile": "https://github.com/izerozlu",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "JacobAnavisca",
|
|
||||||
"name": "Jacob Anavisca",
|
|
||||||
"avatar_url": "https://avatars2.githubusercontent.com/u/21232366?v=4",
|
|
||||||
"profile": "https://github.com/JacobAnavisca",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "nityanandagohain",
|
|
||||||
"name": "Nityananda Gohain",
|
|
||||||
"avatar_url": "https://avatars3.githubusercontent.com/u/26831659?v=4",
|
|
||||||
"profile": "http://nityanandagohain.github.io",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "hosseinnedaee",
|
|
||||||
"name": "Hossein Nedaee",
|
|
||||||
"avatar_url": "https://avatars2.githubusercontent.com/u/42691357?v=4",
|
|
||||||
"profile": "https://github.com/hosseinnedaee",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "jamesgeorge007",
|
|
||||||
"name": "James George",
|
|
||||||
"avatar_url": "https://avatars2.githubusercontent.com/u/25279263?v=4",
|
|
||||||
"profile": "https://ghuser.io/jamesgeorge007",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "dmitryyankowski",
|
|
||||||
"name": "Dmitry Yankowski",
|
|
||||||
"avatar_url": "https://avatars0.githubusercontent.com/u/20114263?v=4",
|
|
||||||
"profile": "https://dmitryyankowski.com",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "sboulema",
|
|
||||||
"name": "Samir Boulema",
|
|
||||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1820661?v=4",
|
|
||||||
"profile": "http://www.sboulema.nl",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"contributorsPerLine": 7,
|
|
||||||
"projectName": "postwoman",
|
|
||||||
"projectOwner": "liyasthomas",
|
|
||||||
"repoType": "github",
|
|
||||||
"repoHost": "https://github.com",
|
|
||||||
"skipCi": true
|
|
||||||
}
|
|
||||||
@@ -30,7 +30,7 @@ coverage
|
|||||||
# nyc test coverage
|
# nyc test coverage
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
.grunt
|
.grunt
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
# Bower dependency directory (https://bower.io/)
|
||||||
@@ -97,8 +97,8 @@ sw.*
|
|||||||
# Vim swap files
|
# Vim swap files
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
# Postwoman build data
|
# Build data
|
||||||
.postwoman
|
.hoppscotch
|
||||||
|
|
||||||
# File explorer
|
# File explorer
|
||||||
.directory
|
.directory
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# https://editorconfig.org
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
indent_size = 2
|
|
||||||
indent_style = space
|
indent_style = space
|
||||||
charset = utf-8
|
indent_size = 2
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|||||||
15
.env.example
15
.env.example
@@ -1,15 +0,0 @@
|
|||||||
# Google Analytics
|
|
||||||
GA_ID=UA-XXXXXXXX-X
|
|
||||||
|
|
||||||
# Google Tag Manager
|
|
||||||
GTM_ID=GTM-XXXXXXX
|
|
||||||
|
|
||||||
# Firebase
|
|
||||||
API_KEY=api-key
|
|
||||||
AUTH_DOMAIN=project-id.firebaseapp.com
|
|
||||||
DATABASE_URL=https://project-id.firebaseio.com
|
|
||||||
PROJECT_ID=project-id
|
|
||||||
STORAGE_BUCKET=project-id.appspot.com
|
|
||||||
MESSAGING_SENDER_ID=sender-id
|
|
||||||
APP_ID=app-id
|
|
||||||
MEASUREMENT_ID=G-measurement-id
|
|
||||||
@@ -1,14 +1,5 @@
|
|||||||
{
|
{
|
||||||
"projects": {
|
"projects": {
|
||||||
"default": "postwoman-api"
|
"default": "postwoman-api"
|
||||||
},
|
|
||||||
"targets": {
|
|
||||||
"postwoman-api": {
|
|
||||||
"hosting": {
|
|
||||||
"postwoman": [
|
|
||||||
"postwoman"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
.github/FUNDING.yml
vendored
6
.github/FUNDING.yml
vendored
@@ -1,4 +1,2 @@
|
|||||||
github: postwoman-io
|
github: hoppscotch
|
||||||
open_collective: postwoman
|
open_collective: hoppscotch
|
||||||
patreon: liyasthomas
|
|
||||||
custom: https://www.paypal.me/liyascthomas
|
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/custom.md
vendored
2
.github/ISSUE_TEMPLATE/custom.md
vendored
@@ -6,5 +6,3 @@ labels: ''
|
|||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -3,7 +3,7 @@ updates:
|
|||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: weekly
|
||||||
time: '00:00'
|
time: '00:00'
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
reviewers:
|
reviewers:
|
||||||
|
|||||||
5
.github/semantic.yml
vendored
Normal file
5
.github/semantic.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Always validate the PR title AND all the commits
|
||||||
|
titleAndCommits: true
|
||||||
|
# Allows use of Merge commits (eg on github: "Merge branch 'master' into feature/ride-unicorns")
|
||||||
|
# this is only relevant when using commitsOnly: true (or titleAndCommits: true)
|
||||||
|
allowMergeCommits: true
|
||||||
83
.github/workflows/codeql-analysis.yml
vendored
83
.github/workflows/codeql-analysis.yml
vendored
@@ -1,54 +1,61 @@
|
|||||||
name: "CodeQL"
|
name: "Code Scanning - Action"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master, ]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
branches: [main]
|
||||||
branches: [master]
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 13 * * 0'
|
# ┌───────────── minute (0 - 59)
|
||||||
|
# │ ┌───────────── hour (0 - 23)
|
||||||
|
# │ │ ┌───────────── day of the month (1 - 31)
|
||||||
|
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
|
||||||
|
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
|
||||||
|
# │ │ │ │ │
|
||||||
|
# │ │ │ │ │
|
||||||
|
# │ │ │ │ │
|
||||||
|
# * * * * *
|
||||||
|
- cron: '0 0 * * 6'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyse:
|
CodeQL-Build:
|
||||||
name: Analyse
|
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
# required for all workflows
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
# only required for workflows in private repositories
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
|
||||||
# We must fetch at least the immediate parents so that if this is
|
|
||||||
# a pull request then we can checkout the head.
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
# If this run was triggered by a pull request event, then checkout
|
# Initializes the CodeQL tools for scanning.
|
||||||
# the head of the pull request instead of the merge commit.
|
- name: Initialize CodeQL
|
||||||
- run: git checkout HEAD^2
|
uses: github/codeql-action/init@v1
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
# Override language selection by uncommenting this and choosing your languages
|
||||||
|
# with:
|
||||||
|
# languages: go, javascript, csharp, python, cpp, java
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
- name: Initialize CodeQL
|
# If this step fails, then you should remove it and run the build manually (see below).
|
||||||
uses: github/codeql-action/init@v1
|
- name: Autobuild
|
||||||
# Override language selection by uncommenting this and choosing your languages
|
uses: github/codeql-action/autobuild@v1
|
||||||
# with:
|
|
||||||
# languages: go, javascript, csharp, python, cpp, java
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# 📚 https://git.io/JvXDl
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v1
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following
|
||||||
# 📚 https://git.io/JvXDl
|
# three lines and modify them (or add more) to build your code if your
|
||||||
|
# project uses a compiled language
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
#- run: |
|
||||||
# and modify them (or add more) to build your code if your project
|
# make bootstrap
|
||||||
# uses a compiled language
|
# make release
|
||||||
|
|
||||||
#- run: |
|
- name: Perform CodeQL Analysis
|
||||||
# make bootstrap
|
uses: github/codeql-action/analyze@v1
|
||||||
# make release
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v1
|
|
||||||
|
|||||||
18
.github/workflows/deploy-prod.yml
vendored
Normal file
18
.github/workflows/deploy-prod.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: Deploy to Live Channel
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy_live_website:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: FirebaseExtended/action-hosting-deploy@v0
|
||||||
|
with:
|
||||||
|
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
|
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_POSTWOMAN_API }}'
|
||||||
|
channelId: live
|
||||||
|
projectId: postwoman-api
|
||||||
39
.github/workflows/publish-docker.yml
vendored
Normal file
39
.github/workflows/publish-docker.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Publish Docker image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
push_to_registry:
|
||||||
|
name: Push Docker image to Docker Hub
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||||
|
with:
|
||||||
|
images: hoppscotch/hoppscotch
|
||||||
|
flavor: |
|
||||||
|
latest=true
|
||||||
|
prefix=
|
||||||
|
suffix=
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
28
.github/workflows/tests.yml
vendored
Normal file
28
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Node.js CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [14.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
- name: Install pnpm
|
||||||
|
run: curl -f https://get.pnpm.io/v6.14.js | node - add --global pnpm@6
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: pnpm
|
||||||
|
- name: Run tests
|
||||||
|
run: pnpm i && pnpm -r test
|
||||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -26,7 +26,7 @@ coverage
|
|||||||
# nyc test coverage
|
# nyc test coverage
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
.grunt
|
.grunt
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
# Bower dependency directory (https://bower.io/)
|
||||||
@@ -93,8 +93,8 @@ sw.*
|
|||||||
# Vim swap files
|
# Vim swap files
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
# Postwoman build data
|
# Build data
|
||||||
.postwoman
|
.hoppscotch
|
||||||
|
|
||||||
# File explorer
|
# File explorer
|
||||||
.directory
|
.directory
|
||||||
@@ -104,3 +104,9 @@ tests/*/screenshots
|
|||||||
|
|
||||||
# Tests videos
|
# Tests videos
|
||||||
tests/*/videos
|
tests/*/videos
|
||||||
|
|
||||||
|
# Local Netlify folder
|
||||||
|
.netlify
|
||||||
|
|
||||||
|
# Andrew's crazy Volar shim generator
|
||||||
|
shims-volar.d.ts
|
||||||
4
.husky/commit-msg
Executable file
4
.husky/commit-msg
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx --no-install commitlint --edit ""
|
||||||
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npm run pre-commit
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
.dependabot
|
.dependabot
|
||||||
.github
|
.github
|
||||||
.nuxt
|
.nuxt
|
||||||
.postwoman
|
.hoppscotch
|
||||||
.vscode
|
.vscode
|
||||||
package-lock.json
|
package-lock.json
|
||||||
node_modules
|
node_modules
|
||||||
|
dist
|
||||||
3
.prettierrc.js
Normal file
3
.prettierrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
semi: false
|
||||||
|
}
|
||||||
39
.travis.yml
39
.travis.yml
@@ -1,39 +0,0 @@
|
|||||||
# == INSTRUCTIONS FOR SETTING UP TRAVIS ==
|
|
||||||
#
|
|
||||||
# 1. Find this repository in your Travis-CI dashboard.
|
|
||||||
# open settings and add an environment variable called
|
|
||||||
# GITHUB_ACCESS_TOKEN and set it to your personal access token.addons:
|
|
||||||
# See: https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line
|
|
||||||
#
|
|
||||||
# **DO NOT TURN ON 'Display value in build log'!!!!**
|
|
||||||
#
|
|
||||||
# 2. Push the code to the repository.
|
|
||||||
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- lts/*
|
|
||||||
|
|
||||||
os: linux
|
|
||||||
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- libgconf-2-4 # cypress binary dependency
|
|
||||||
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
install:
|
|
||||||
- npm ci
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- npm run build # use nuxt build and start to run tests
|
|
||||||
|
|
||||||
script:
|
|
||||||
- npm test
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
webhooks: https://www.travisbuddy.com
|
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [v1.12.0](https://github.com/hoppscotch/hoppscotch/tree/v1.12.0) (2020-05-27)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/hoppscotch/hoppscotch/compare/v1.10.0...v1.12.0)
|
||||||
|
|
||||||
|
## [v1.10.0](https://github.com/hoppscotch/hoppscotch/tree/v1.10.0) (2020-04-10)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/hoppscotch/hoppscotch/compare/v1.9.9...v1.10.0)
|
||||||
|
|
||||||
## [v1.9.9](https://github.com/liyasthomas/postwoman/tree/v1.9.9) (2020-07-30)
|
## [v1.9.9](https://github.com/liyasthomas/postwoman/tree/v1.9.9) (2020-07-30)
|
||||||
|
|
||||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.7...v1.9.9)
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.7...v1.9.9)
|
||||||
|
|||||||
@@ -2,75 +2,127 @@
|
|||||||
|
|
||||||
## Our Pledge
|
## Our Pledge
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
contributors and maintainers pledge to making participation in our project and
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
level of experience, education, socio-economic status, nationality, personal
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
appearance, race, religion, or sexual identity and orientation.
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
## Our Standards
|
## Our Standards
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
Examples of behavior that contributes to a positive environment for our
|
||||||
include:
|
community include:
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
* Demonstrating empathy and kindness toward other people
|
||||||
* Being respectful of differing viewpoints and experiences
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
* Gracefully accepting constructive criticism
|
* Giving and gracefully accepting constructive feedback
|
||||||
* Focusing on what is best for the community
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
* Showing empathy towards other community members
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
advances
|
advances of any kind
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
* Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or electronic
|
* Publishing others' private information, such as a physical or email
|
||||||
address, without explicit permission
|
address, without their explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
professional setting
|
professional setting
|
||||||
|
|
||||||
## Our Responsibilities
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
behavior. Maintainers are expected to take appropriate and fair corrective action in
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
response to any instances of unacceptable behavior.
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
that are not aligned with our Code of Conduct, or to ban temporarily or
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
decisions when appropriate.
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
when an individual is representing the project or its community. Examples of
|
an individual is officially representing the community in public spaces.
|
||||||
representing a project or community include using an official project e-mail
|
Examples of representing our community include using an official e-mail address,
|
||||||
address, posting via an official social media account, or acting as an appointed
|
posting via an official social media account, or acting as an appointed
|
||||||
representative at an online or offline event. Representation of a project may be
|
representative at an online or offline event.
|
||||||
further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported by contacting the project team at hello@postwoman.io. All
|
reported to the community leaders responsible for enforcement at
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
support@hoppscotch.io.
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
reporter of any incident.
|
||||||
members of the project's leadership.
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
https://www.contributor-covenant.org/faq
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
|
|||||||
@@ -12,81 +12,6 @@ Please note we have a code of conduct, please follow it in all your interactions
|
|||||||
2. Update the README.md with details of changes to the interface, this includes new environment
|
2. Update the README.md with details of changes to the interface, this includes new environment
|
||||||
variables, exposed ports, useful file locations and container parameters.
|
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
|
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/).
|
Pull Request would represent. The versioning scheme we use is [SemVer](https://semver.org).
|
||||||
4. You may merge the Pull Request once you have the sign-off of two other developers, or if 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.
|
do not have permission to do that, you may request the second reviewer merge it for you.
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
### Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to making participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
|
||||||
nationality, personal appearance, race, religion, or sexual identity and
|
|
||||||
orientation.
|
|
||||||
|
|
||||||
### Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
### Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
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 with our Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
### Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community. Examples of
|
|
||||||
representing a project or community include using an official project e-mail
|
|
||||||
address, posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event. Representation of a project may be
|
|
||||||
further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
### Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
### Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at [http://contributor-covenant.org/version/1/4][version]
|
|
||||||
|
|
||||||
[homepage]: http://contributor-covenant.org
|
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
||||||
|
|||||||
20
Dockerfile
20
Dockerfile
@@ -1,21 +1,27 @@
|
|||||||
FROM node:12.10.0-alpine
|
FROM node:lts-alpine
|
||||||
|
|
||||||
LABEL maintainer="Liyas Thomas (liyascthomas@gmail.com)"
|
LABEL maintainer="Hoppscotch (support@hoppscotch.io)"
|
||||||
|
|
||||||
# Add git as the prebuild target requires it to parse version information
|
# Add git as the prebuild target requires it to parse version information
|
||||||
RUN apk add --update --no-cache \
|
RUN apk add --update --no-cache \
|
||||||
git
|
git
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package*.json ./
|
ADD . /app/
|
||||||
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm install -g pnpm
|
||||||
|
|
||||||
|
RUN pnpm i --unsafe-perm=true
|
||||||
|
|
||||||
|
ENV HOST 0.0.0.0
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["npm", "run", "start"]
|
RUN mv packages/hoppscotch-app/.env.example packages/hoppscotch-app/.env
|
||||||
|
|
||||||
|
RUN pnpm run generate
|
||||||
|
|
||||||
|
CMD ["pnpm", "run", "start"]
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 Liyas Thomas
|
Copyright (c) 2020
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
565
README.md
565
README.md
@@ -1,107 +1,105 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://postwoman.io"><img src="https://raw.githubusercontent.com/liyasthomas/postwoman/master/static/logo.png" alt="Postwoman.io logo" height="160"></a>
|
<a href="https://hoppscotch.io">
|
||||||
<br>
|
<img
|
||||||
<br>
|
src="https://avatars.githubusercontent.com/u/56705483"
|
||||||
|
alt="Hoppscotch Logo"
|
||||||
|
height="64"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
<p>
|
<p>
|
||||||
<b>A free, fast and beautiful API request builder</b>
|
<h3>
|
||||||
|
<b>
|
||||||
|
Hoppscotch
|
||||||
|
</b>
|
||||||
|
</h3>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<i>Web alternative to Postman - Helps you create requests faster, saving precious time on development - <a href="https://postwoman.launchaco.com">Subscribe</a></i>
|
<b>
|
||||||
|
Open source API development ecosystem
|
||||||
|
</b>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
[](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)
|
[](CODE_OF_CONDUCT.md) [](https://hoppscotch.io) [](https://github.com/hoppscotch/hoppscotch/actions) [](https://twitter.com/share?text=%F0%9F%91%BD%20Hoppscotch%20%E2%80%A2%20Open%20source%20API%20development%20ecosystem%20-%20Helps%20you%20create%20requests%20faster,%20saving%20precious%20time%20on%20development.&url=https://hoppscotch.io&hashtags=hoppscotch&via=hoppscotch_io)
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<sub>Built with ❤︎ by
|
<sub>
|
||||||
<a href="https://github.com/liyasthomas">liyasthomas</a> and
|
Built with ❤︎ by
|
||||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors">contributors</a>
|
<a href="https://github.com/hoppscotch/hoppscotch/graphs/contributors">
|
||||||
|
contributors
|
||||||
|
</a>
|
||||||
</sub>
|
</sub>
|
||||||
</p>
|
</p>
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
<a href="https://hoppscotch.io">
|
||||||
|
<img
|
||||||
|
src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/packages/hoppscotch-app/static/banner.png"
|
||||||
|
alt="Screenshot"
|
||||||
|
width="100%"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
#### **Support**
|
||||||
|
|
||||||
|
[](https://hoppscotch.io/discord) [](https://hoppscotch.io/telegram)
|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary><b>Table of contents</b></summary>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Read: [Story behind Postwoman](https://dev.to/liyasthomas/i-created-postwoman-an-online-open-source-api-request-builder-41md)**
|
- [Features](#features)
|
||||||
|
- [Demo](#demo)
|
||||||
**Chat: [Telegram](https://t.me/postwoman_app), [Discord](https://discord.gg/GAMWxmR)**
|
- [Usage](#usage)
|
||||||
|
- [Built with](#built-with)
|
||||||
**Donate: [GitHub Sponsors](https://github.com/sponsors/postwoman-io), [Open Collective](https://opencollective.com/postwoman), [Patreon](https://www.patreon.com/liyasthomas), [PayPal](https://www.paypal.me/liyascthomas)**
|
- [Developing](#developing)
|
||||||
|
- [Docker](#docker)
|
||||||
<div align="center">
|
- [Releasing](#releasing)
|
||||||
<a href="https://postwoman.io"><img src="https://raw.githubusercontent.com/liyasthomas/postwoman/master/static/images/screenshot1.png" alt="Screenshot1" width="100%"></a>
|
- [Contributing](#contributing)
|
||||||
</div>
|
- [Continuous Integration](#continuous-integration)
|
||||||
|
- [Changelog](#changelog)
|
||||||
<details>
|
- [Authors](#authors)
|
||||||
<summary>Table of contents</summary>
|
- [License](#license)
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
- [Features](#features-)
|
|
||||||
- [Demo](#demo--)
|
|
||||||
- [Usage](#usage-)
|
|
||||||
- [Built with](#built-with-)
|
|
||||||
- [Developing](#developing-)
|
|
||||||
- [Browser based development environment](#browser-based-development-environment)
|
|
||||||
- [Local development environment](#local-development-environment)
|
|
||||||
- [Docker compose](#docker-compose)
|
|
||||||
- [Docker](#docker--)
|
|
||||||
- [Releasing](#releasing-)
|
|
||||||
- [Contributing](#contributing-)
|
|
||||||
- [Continuous Integration](#continuous-integration--)
|
|
||||||
- [Versioning](#versioning--)
|
|
||||||
- [Change log](#change-log-)
|
|
||||||
- [Authors](#authors-)
|
|
||||||
- [Lead Developers](#lead-developers)
|
|
||||||
- [Testing and Debugging](#testing-and-debugging)
|
|
||||||
- [Collaborators](#collaborators-)
|
|
||||||
- [Thanks](#thanks)
|
|
||||||
- [Financial Contributors](#financial-contributors)
|
|
||||||
- [Organizations](#organizations)
|
|
||||||
- [Individuals](#individuals)
|
|
||||||
- [Code Contributors](#code-contributors)
|
|
||||||
- [License](#license-)
|
|
||||||
- [Acknowledgements](#acknowledgements-)
|
|
||||||
- [Badges](#badges-)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Features ✨
|
### **Features**
|
||||||
|
|
||||||
❤️ **Lightweight**: Crafted with minimalistic UI design.
|
❤️ **Lightweight:** Crafted with minimalistic UI design.
|
||||||
|
|
||||||
⚡️ **Fast**: Send requests and get/copy responses in real-time.
|
⚡️ **Fast:** Send requests and get/copy responses in real-time.
|
||||||
|
|
||||||
**Methods:**
|
**HTTP Methods**
|
||||||
|
|
||||||
- `GET` - Retrieve information about the REST API resource
|
- `GET` - Requests retrieve resource information
|
||||||
|
- `POST` - The server creates a new entry in a database
|
||||||
|
- `PUT` - Updates an existing resource
|
||||||
|
- `PATCH` - Very similar to `PUT` but makes a partial update on a resource
|
||||||
|
- `DELETE` - Deletes resource or related component
|
||||||
- `HEAD` - Retrieve response headers identical to those of a GET request, but without the response body.
|
- `HEAD` - Retrieve response headers identical to those of a GET request, but without the response body.
|
||||||
- `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
|
- `CONNECT` - Establishes a tunnel to the server identified by the target resource
|
||||||
- `OPTIONS` - Describe the communication options for 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
|
- `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.
|
- `<custom>` - Some APIs use custom request methods such as `LIST`. Type in your custom methods.
|
||||||
|
|
||||||
🌈 **Make it yours**: Customizable combinations for background, foreground and accent colors.
|
🌈 **Make it yours:** Customizable combinations for background, foreground and accent colors — [customize now](https://hoppscotch.io/settings).
|
||||||
|
|
||||||
**Theming:** [Customize now ✨](https://postwoman.io/settings)
|
**Theming**
|
||||||
|
|
||||||
- Choose theme: Kinda Dark (default), Clearly White, Just Black and System theme
|
- Choose theme: System (default), Light, Dark and Black
|
||||||
- Choose accent color: Green (default), Yellow, Pink, Red, Purple, Orange, Cyan and Blue
|
- Choose accent color: Green (default), Teal, Blue, Indigo, Purple, Yellow, Orange, Red and Pink
|
||||||
- Toggle multi-colored headings
|
- Distraction free Zen mode
|
||||||
- Toggle auto-scroll to response
|
|
||||||
|
|
||||||
_Customized themes are synced with local session storage_
|
_Customized themes are synced with cloud / local session_
|
||||||
|
|
||||||
🔥 **PWA**: Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
|
🔥 **PWA:** Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
|
||||||
|
|
||||||
**Features:**
|
|
||||||
|
|
||||||
- Instant loading with Service Workers
|
- Instant loading with Service Workers
|
||||||
- Offline support
|
- Offline support
|
||||||
@@ -109,41 +107,34 @@ _Customized themes are synced with local session storage_
|
|||||||
- Add to Home Screen
|
- Add to Home Screen
|
||||||
- Desktop PWA
|
- Desktop PWA
|
||||||
|
|
||||||
🚀 **Request**: Retrieve response from endpoint instantly.
|
🚀 **Request:** Retrieve response from endpoint instantly.
|
||||||
|
|
||||||
- Choose `method`
|
1. Choose `method`
|
||||||
- Enter `URL`
|
2. Enter `URL`
|
||||||
- Send
|
3. Send
|
||||||
|
|
||||||
**Features:**
|
|
||||||
|
|
||||||
- Copy/share public "Share URL"
|
- Copy/share public "Share URL"
|
||||||
- Generate/copy request code for `JavaScript XHR`, `Fetch` and `cURL`
|
- Generate/copy request code snippets for 10+ languages and frameworks
|
||||||
- Import `cURL`
|
- Import `cURL`
|
||||||
- Label requests
|
- Label requests
|
||||||
|
|
||||||
🔌 **WebSocket**: 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
|
📡 **Server Sent Events:** Receive a stream of updates from a server over a HTTP connection without resorting to polling.
|
||||||
- Basic and Bearer Token authentication
|
|
||||||
|
|
||||||
📡 **Server Sent Events**: Receive a stream of updates from a server over a HTTP connection without resorting to polling.
|
🌩 **Socket.IO:** Send and Receive data with SocketIO server.
|
||||||
|
|
||||||
🌩 **Socket.IO**: Send and Receive data with SocketIO server.
|
🦟 **MQTT:** Subscribe and Publish to topics of a MQTT Broker.
|
||||||
|
|
||||||
🦟 **MQTT**: Subscribe and Publish to topics of a MQTT Broker.
|
🔮 **GraphQL:** GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.
|
||||||
|
|
||||||
🔮 **GraphQL**: GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.
|
- Set endpoint and get schema
|
||||||
|
|
||||||
- Set endpoint and get schemas
|
|
||||||
- Multi-column docs
|
- Multi-column docs
|
||||||
- Set custom request headers
|
- Set custom request headers
|
||||||
- Query schema
|
- Query schema
|
||||||
- Get query response
|
- Get query response
|
||||||
|
|
||||||
🔐 **Authentication**: Allows to identify the end user.
|
🔐 **Authorization:** Allows to identify the end user.
|
||||||
|
|
||||||
**Types:**
|
|
||||||
|
|
||||||
- None
|
- None
|
||||||
- Basic
|
- Basic
|
||||||
@@ -151,386 +142,222 @@ _Customized themes are synced with local session storage_
|
|||||||
- OAuth 2.0
|
- OAuth 2.0
|
||||||
- OIDC Access Token/PKCE
|
- OIDC Access Token/PKCE
|
||||||
|
|
||||||
📢 **Headers**: Describes the format the body of your request is being sent as.
|
📢 **Headers:** Describes the format the body of your request is being sent as.
|
||||||
|
|
||||||
📫 **Parameters**: Use request parameters to set varying parts in simulated requests.
|
📫 **Parameters:** Use request parameters to set varying parts in simulated requests.
|
||||||
|
|
||||||
📃 **Request Body**: Used to send and receive data via the REST API.
|
📃 **Request Body:** Used to send and receive data via the REST API.
|
||||||
|
|
||||||
**Options:**
|
|
||||||
|
|
||||||
- Set `Content Type`
|
- Set `Content Type`
|
||||||
- Add or remove Parameter list
|
- FormData, JSON and many more
|
||||||
- Toggle between key-value and RAW input parameter list
|
- Toggle between key-value and RAW input parameter list
|
||||||
|
|
||||||
👋 **Responses**: Contains the status line, headers and the message/response body.
|
👋 **Response:** Contains the status line, headers and the message/response body.
|
||||||
|
|
||||||
- Copy response to clipboard
|
- Copy response to clipboard
|
||||||
- Download response as a file
|
- Download response as a file
|
||||||
- View preview of HTML responses
|
- View response headers
|
||||||
|
- View raw and preview of HTML, image, JSON, XML responses
|
||||||
|
|
||||||
⏰ **History**: Request entries are synced with cloud / local session storage to restore with a single click.
|
⏰ **History:** Request entries are synced with cloud / local session storage to restore with a single click.
|
||||||
|
|
||||||
📁 **Collections**: Keep your API requests organized with collections and folders. Reuse them with a single click.
|
📁 **Collections:** Keep your API requests organized with collections and folders. Reuse them with a single click.
|
||||||
|
|
||||||
|
- Unlimited collections, folders and requests
|
||||||
|
- Nested folders
|
||||||
|
- Export and import as file or GitHub gist
|
||||||
|
|
||||||
_Collections are synced with cloud / local session storage_
|
_Collections are synced with cloud / local session storage_
|
||||||
|
|
||||||
🌐 **Proxy**: Enable Proxy Mode from Settings to access blocked APIs.
|
🌐 **Proxy:** Enable Proxy Mode from Settings to access blocked APIs.
|
||||||
|
|
||||||
**Features:**
|
|
||||||
|
|
||||||
- Hide your IP address
|
- Hide your IP address
|
||||||
- Fixes [`CORS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross Origin Resource Sharing) issues
|
- Fixes [`CORS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross Origin Resource Sharing) issues
|
||||||
- Access APIs served in non-HTTPS (`http://`)
|
- Access APIs served in non-HTTPS (`http://`) endpoints
|
||||||
- Use custom Proxy URL
|
- Use your own Proxy URL
|
||||||
|
|
||||||
_Official Postwoman Proxy is hosted by Apollo Software - **[Privacy Policy](https://apollosoftware.xyz/legal/postwoman)**_
|
_Official proxy server is hosted by Hoppscotch - **[GitHub](https://github.com/hoppscotch/proxyscotch)** - **[Privacy Policy](https://docs.hoppscotch.io/privacy)**_
|
||||||
|
|
||||||
📜 **Pre-Request Scripts β**: Snippets of code associated with a request that are executed before the request is sent.
|
📜 **Pre-Request Scripts β:** Snippets of code associated with a request that are executed before the request is sent.
|
||||||
|
|
||||||
**Use-cases:**
|
|
||||||
|
|
||||||
|
- Set environment variables
|
||||||
- Include timestamp in the request headers
|
- Include timestamp in the request headers
|
||||||
- Send a random alphanumeric string in the URL parameters
|
- Send a random alphanumeric string in the URL parameters
|
||||||
|
- Any JavaScript functions
|
||||||
|
|
||||||
📄 **API Documentation**: Create and share dynamic API documentation easily, quickly.
|
📄 **API Documentation:** Create and share dynamic API documentation easily, quickly.
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
1. Add your requests to Collections and Folders
|
1. Add your requests to Collections and Folders
|
||||||
2. Export Collections and easily share your APIs with the rest of your team
|
2. Export Collections and easily share your APIs with the rest of your team
|
||||||
3. Import Collections and Generate Documentation on-the-go
|
3. Import Collections and Generate Documentation on-the-go
|
||||||
|
|
||||||
⌨️ **Keyboard Shortcuts**: Optimized for efficiency.
|
⌨️ **Keyboard Shortcuts:** Optimized for efficiency.
|
||||||
|
|
||||||
**Shortcuts:**
|
> **[Read our documentation on Keyboard Shortcuts](https://docs.hoppscotch.io/features/shortcuts)**
|
||||||
|
|
||||||
- Send/Cancel Request <kbd>Ctrl</kbd> + <kbd>G</kbd>
|
🌎 **i18n:** Experience the app in your own language.
|
||||||
- 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.
|
Help us to translate Hoppscotch. Please read [`TRANSLATIONS`](TRANSLATIONS.md) for details on our [`CODE OF CONDUCT`](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
||||||
|
|
||||||
1. Scroll down to the footer
|
📦 **Add-ons:** Official add-ons for hoppscotch.
|
||||||
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_
|
- **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch
|
||||||
|
- **[CLI β](https://github.com/hoppscotch/hopp-cli)** - A CLI solution for Hoppscotch
|
||||||
|
- **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that simplifies access to Hoppscotch
|
||||||
|
|
||||||
**To provide a localized experience for users around the world, you can add you own translations.**
|
[ **Firefox**](https://addons.mozilla.org/en-US/firefox/addon/hoppscotch) | [ **Chrome**](https://chrome.google.com/webstore/detail/hoppscotch-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld)
|
||||||
|
|
||||||
_**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/proxywoman)** - A simple proxy server created for Postwoman
|
|
||||||
- **[CLI β](https://github.com/postwoman-io/postwoman-cli)** - A CLI solution for Postwoman
|
|
||||||
- **[Browser Extensions](https://github.com/AndrewBastin/postwoman-extension)** - 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)
|
|
||||||
|
|
||||||
> **Extensions fixes `CORS` issues.**
|
> **Extensions fixes `CORS` issues.**
|
||||||
|
|
||||||
_Add-ons are developed and maintained under **[Official Postwoman Organization](https://github.com/postwoman-io)**._
|
- **[Hopp-Doc-Gen](https://github.com/hoppscotch/hopp-doc-gen)** - An API doc generator CLI for Hoppscotch
|
||||||
|
|
||||||
☁️ **Auth + Sync**: Sign in and sync in real-time.
|
_Add-ons are developed and maintained under **[Hoppscotch Organization](https://github.com/hoppscotch)**._
|
||||||
|
|
||||||
**Sign in with:**
|
☁️ **Auth + Sync:** Sign in and sync your data in real-time.
|
||||||
|
|
||||||
|
**Sign in with**
|
||||||
|
|
||||||
- Google
|
|
||||||
- GitHub
|
- GitHub
|
||||||
|
- Google
|
||||||
|
- Email
|
||||||
|
|
||||||
**Sync:**
|
**Synchronize your data**
|
||||||
|
|
||||||
- History
|
- History
|
||||||
- Collections
|
- Collections
|
||||||
- Environments
|
- Environments
|
||||||
- Notes
|
- Settings
|
||||||
|
|
||||||
✅ **Post-Request Tests β**: Write tests associated with a request that are executed after the request response.
|
✅ **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
|
- Check the status code as an integer
|
||||||
- Filter response headers
|
- Filter response headers
|
||||||
- Parse the response data
|
- Parse the response data
|
||||||
|
- Any JavaScript functions
|
||||||
📝 **Notes** : Instantly jot down notes, tasks or whatever you feel like as they come to your mind.
|
|
||||||
|
|
||||||
_Notes are only available for signed-in users_
|
|
||||||
|
|
||||||
🌱 **Environments** : Environment variables allow you to store and reuse values in your requests and scripts.
|
🌱 **Environments** : Environment variables allow you to store and reuse values in your requests and scripts.
|
||||||
|
|
||||||
**Use-cases:**
|
- Unlimited environments and variables
|
||||||
|
- Initialize through pre-request script
|
||||||
|
- Export as / import from GitHub gist
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><i>Use-cases</i></summary>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
- By storing a value in a variable, you can reference it throughout your request section
|
- By storing a value in a variable, you can reference it throughout your request section
|
||||||
- If you need to update the value, you only have to change it in one place
|
- If you need to update the value, you only have to change it in one place
|
||||||
- Using variables increases your ability to work efficiently and minimizes the likelihood of error
|
- Using variables increases your ability to work efficiently and minimizes the likelihood of error
|
||||||
|
|
||||||
**To find out more, please check out [Postwoman Wiki](https://github.com/liyasthomas/postwoman/wiki).**
|
---
|
||||||
|
|
||||||
## Demo 🚀 [](https://postwoman.io)
|
</details>
|
||||||
|
|
||||||
[postwoman.io](https://postwoman.io)
|
👨👩👧👦 **Teams β:** Helps you collaborate across your team to design, develop, and test APIs faster.
|
||||||
|
|
||||||
## Usage 💡
|
- Unlimited teams
|
||||||
|
- Unlimited shared collections
|
||||||
|
- Unlimited team members
|
||||||
|
- Role-based access control
|
||||||
|
- Cloud sync
|
||||||
|
- Multiple devices
|
||||||
|
|
||||||
|
🚚 **Bulk Edit:** Edit key-value pairs in bulk.
|
||||||
|
|
||||||
|
- Entries are separated by newline
|
||||||
|
- Keys and values are separated by `:`
|
||||||
|
- Prepend `//` to any row you want to add but keep disabled
|
||||||
|
|
||||||
|
**For more features, please read our [documentation](https://docs.hoppscotch.io).**
|
||||||
|
|
||||||
|
## **Demo**
|
||||||
|
|
||||||
|
[hoppscotch.io](https://hoppscotch.io)
|
||||||
|
|
||||||
|
## **Usage**
|
||||||
|
|
||||||
1. Choose `method`
|
1. Choose `method`
|
||||||
2. Enter `URL`
|
2. Enter `URL`
|
||||||
3. Send request
|
3. Send request
|
||||||
4. Get response
|
4. Get response
|
||||||
|
|
||||||
## Built with 🔧
|
## **Built with**
|
||||||
|
|
||||||
- HTML - For the web framework
|
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)
|
||||||
- CSS - For styling components
|
- [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS), [SCSS](https://sass-lang.com), [Windi CSS](https://windicss.org)
|
||||||
- JavaScript - For magic!
|
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
||||||
- [Vue](https://vuejs.org/)
|
- [TypeScript](https://www.typescriptlang.org)
|
||||||
- [Nuxt](https://nuxtjs.org/)
|
- [Vue](https://vuejs.org)
|
||||||
|
- [Nuxt](https://nuxtjs.org)
|
||||||
|
|
||||||
## Developing 👷
|
## **Developing**
|
||||||
|
|
||||||
0. Update [`.env.example`](https://github.com/liyasthomas/postwoman/blob/master/.env.example) file found in repository's root directory with your own keys and rename it to `.env`.
|
0. Update [`.env.example`](https://github.com/hoppscotch/hoppscotch/blob/main/packages/hoppscotch-app/.env.example) file found in `packages/hoppscotch-app` with your own keys and rename it to `.env`.
|
||||||
|
|
||||||
_Sample keys only works with the [production build](https://postwoman.io)._
|
_Sample keys only works with the [production build](https://hoppscotch.io)._
|
||||||
|
|
||||||
#### Browser based development environment
|
### Browser based development environment
|
||||||
|
|
||||||
[](https://gitpod.io/#https://github.com/liyasthomas/postwoman)
|
- [GitHub codespace](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace)
|
||||||
|
- [Gitpod](https://gitpod.io/#https://github.com/hoppscotch/hoppscotch)
|
||||||
|
|
||||||
#### Local development environment
|
### Local development environment
|
||||||
|
|
||||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
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`).
|
2. Install pnpm using npm by running `npm install -g pnpm`.
|
||||||
3. Start the development server with `npm run dev`.
|
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
||||||
4. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
4. Start the development server with `pnpm run dev`.
|
||||||
|
5. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
||||||
|
|
||||||
#### Docker compose
|
### Docker compose
|
||||||
|
|
||||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||||
2. Run `docker-compose up`
|
2. Run `docker-compose up`
|
||||||
3. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
3. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
||||||
|
|
||||||
## Docker 🐳 [](https://hub.docker.com/r/liyasthomas/postwoman)
|
## **Docker**
|
||||||
|
|
||||||
|
**Official container** [](https://hub.docker.com/r/hoppscotch/hoppscotch)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#pull
|
docker run --rm --name hoppscotch -p 3000:3000 hoppscotch/hoppscotch:latest
|
||||||
docker pull liyasthomas/postwoman
|
|
||||||
|
|
||||||
#run
|
|
||||||
docker run -p 3000:3000 liyasthomas/postwoman:latest
|
|
||||||
|
|
||||||
#build
|
|
||||||
docker build -t postwoman:latest .
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Releasing 🧞
|
## **Releasing**
|
||||||
|
|
||||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
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`).
|
2. Install pnpm using npm by running `npm install -g pnpm`.
|
||||||
3. Build the release files with `npm run build`.
|
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
||||||
4. Find the built project in `./dist`.
|
4. Build the release files with `pnpm run generate`.
|
||||||
|
5. Find the built project in `packages/hoppscotch-app/dist`.
|
||||||
|
|
||||||
## Contributing 🍰
|
## **Contributing**
|
||||||
|
|
||||||
|
Please contribute using [GitHub Flow](https://guides.github.com/introduction/flow). Create a branch, add commits, and [open a pull request](https://github.com/hoppscotch/hoppscotch/compare).
|
||||||
|
|
||||||
Please read [`CONTRIBUTING`](CONTRIBUTING.md) for details on our [`CODE OF CONDUCT`](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
Please read [`CONTRIBUTING`](CONTRIBUTING.md) for details on our [`CODE OF CONDUCT`](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
||||||
|
|
||||||
## Continuous Integration 💚 [](https://travis-ci.com/liyasthomas/postwoman)
|
## **Continuous Integration**
|
||||||
|
|
||||||
We use [Travis CI](https://travis-ci.com) for continuous integration. Check out our [Travis CI Status](https://travis-ci.com/liyasthomas/postwoman).
|
We use [GitHub Actions](https://github.com/features/actions) for continuous integration. Check out our [build workflows](https://github.com/hoppscotch/hoppscotch/actions).
|
||||||
|
|
||||||
## Versioning 🔖 [](https://github.com/liyasthomas/postwoman/releases/latest)
|
## **Changelog**
|
||||||
|
|
||||||
This project is developed by [Liyas Thomas](https://github.com/liyasthomas) using the [Semantic Versioning specification](https://semver.org). For the versions available, see the [releases on this repository](https://github.com/liyasthomas/postwoman/releases).
|
|
||||||
|
|
||||||
## Change log 📝
|
|
||||||
|
|
||||||
See the [`CHANGELOG`](CHANGELOG.md) file for details.
|
See the [`CHANGELOG`](CHANGELOG.md) file for details.
|
||||||
|
|
||||||
## Authors 🧙
|
## **Authors**
|
||||||
|
|
||||||
### Lead Developers
|
This project exists thanks to all the people who contribute — [make a contribution](CONTRIBUTING.md).
|
||||||
|
|
||||||
- **[Liyas Thomas](https://github.com/liyasthomas)** - _Author_
|
|
||||||
- **[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
|
|
||||||
|
|
||||||
- [Contributors](https://github.com/liyasthomas/postwoman/graphs/contributors)
|
|
||||||
|
|
||||||
### Collaborators <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
||||||
|
|
||||||
[](#contributors-)
|
|
||||||
|
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
||||||
|
|
||||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
<!-- markdownlint-disable -->
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://liyasthomas.web.app"><img src="https://avatars1.githubusercontent.com/u/10395817?v=4" width="100px;" alt=""/><br /><sub><b>Liyas Thomas</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=liyasthomas" title="Code">💻</a> <a href="#design-liyasthomas" title="Design">🎨</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/NBTX"><img src="https://avatars3.githubusercontent.com/u/43181178?v=4" width="100px;" alt=""/><br /><sub><b>John Harker</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=NBTX" title="Code">💻</a> <a href="#design-NBTX" title="Design">🎨</a></td>
|
|
||||||
<td align="center"><a href="https://nicholaslaroux.com"><img src="https://avatars0.githubusercontent.com/u/1557529?v=4" width="100px;" alt=""/><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=""/><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=""/><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://github.com/AndrewBastin"><img src="https://avatars2.githubusercontent.com/u/9131943?v=4" width="100px;" alt=""/><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=""/><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/izerozlu"><img src="https://avatars3.githubusercontent.com/u/17386157?v=4" width="100px;" alt=""/><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=""/><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=""/><br /><sub><b>Nityananda Gohain</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=nityanandagohain" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/hosseinnedaee"><img src="https://avatars2.githubusercontent.com/u/42691357?v=4" width="100px;" alt=""/><br /><sub><b>Hossein Nedaee</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=hosseinnedaee" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://ghuser.io/jamesgeorge007"><img src="https://avatars2.githubusercontent.com/u/25279263?v=4" width="100px;" alt=""/><br /><sub><b>James George</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=jamesgeorge007" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://dmitryyankowski.com"><img src="https://avatars0.githubusercontent.com/u/20114263?v=4" width="100px;" alt=""/><br /><sub><b>Dmitry Yankowski</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=dmitryyankowski" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="http://www.sboulema.nl"><img src="https://avatars2.githubusercontent.com/u/1820661?v=4" width="100px;" alt=""/><br /><sub><b>Samir Boulema</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=sboulema" title="Code">💻</a></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- markdownlint-enable -->
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
|
|
||||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
||||||
|
|
||||||
See the list of [contributors](https://github.com/liyasthomas/postwoman/graphs/contributors) who participated in this project.
|
|
||||||
|
|
||||||
### Thanks
|
|
||||||
|
|
||||||
- [dev.to 👩💻👨💻](https://dev.to)
|
|
||||||
|
|
||||||
### Financial Contributors
|
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community [[Contribute](https://opencollective.com/postwoman/contribute)].
|
|
||||||
|
|
||||||
#### Organizations
|
|
||||||
|
|
||||||
Support this project with your organization. Your logo will show up here with a link to your website [[Contribute](https://opencollective.com/postwoman/contribute)].
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/0/website"><img src="https://opencollective.com/postwoman/organization/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/1/website"><img src="https://opencollective.com/postwoman/organization/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/2/website"><img src="https://opencollective.com/postwoman/organization/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/3/website"><img src="https://opencollective.com/postwoman/organization/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/4/website"><img src="https://opencollective.com/postwoman/organization/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/5/website"><img src="https://opencollective.com/postwoman/organization/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/6/website"><img src="https://opencollective.com/postwoman/organization/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/7/website"><img src="https://opencollective.com/postwoman/organization/7/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/8/website"><img src="https://opencollective.com/postwoman/organization/8/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/9/website"><img src="https://opencollective.com/postwoman/organization/9/avatar.svg"></a>
|
|
||||||
|
|
||||||
#### Individuals
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/postwoman"><img src="https://opencollective.com/postwoman/individuals.svg"></a>
|
|
||||||
|
|
||||||
### Code Contributors
|
|
||||||
|
|
||||||
This project exists thanks to all the people who contribute [[Contribute](CONTRIBUTING.md)].
|
|
||||||
|
|
||||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors"><img src="https://opencollective.com/postwoman/contributors.svg?width=890&button=false" /></a>
|
|
||||||
|
|
||||||
## License 📄
|
|
||||||
|
|
||||||
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [`LICENSE`](LICENSE) file for details.
|
|
||||||
|
|
||||||
## Acknowledgements 🙏
|
|
||||||
|
|
||||||
- Hat tip to anyone whose code was used
|
|
||||||
- Inspirations:
|
|
||||||
- [Dribbble](https://dribbble.com)
|
|
||||||
|
|
||||||
## Badges 📛
|
|
||||||
|
|
||||||
<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">
|
<div align="center">
|
||||||
<br>
|
<a href="https://github.com/hoppscotch/hoppscotch/graphs/contributors">
|
||||||
<a href="https://postwoman.io"><img src="https://raw.githubusercontent.com/liyasthomas/templates/master/assets/logo.gif" alt="Postwoman.io" width="200"></a>
|
<img src="https://opencollective.com/hoppscotch/contributors.svg?width=840&button=false"
|
||||||
<br>
|
alt="Contributors"
|
||||||
<h3>Happy Coding ❤︎</h3>
|
width="100%" />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## **License**
|
||||||
|
|
||||||
|
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [`LICENSE`](LICENSE) file for details.
|
||||||
|
|||||||
@@ -1,43 +1,25 @@
|
|||||||
# Translations
|
# Translations
|
||||||
|
|
||||||
Thanks for your interest in helping translating the software!
|
Thanks for showing your interest in helping us to translate the software.
|
||||||
|
|
||||||
## Starting a translation
|
## Creating a new translation
|
||||||
|
|
||||||
Before you start working on a translation, look through the [open pull requests](https://github.com/liyasthomas/postwoman/pulls) to see if anyone else is already working on one for your language.
|
Before you start working on a new language, please look through the [open pull requests](https://github.com/hoppscotch/hoppscotch/pulls) to see if anyone is already working on a translation. If you find one, please join the discussion and help us keep the existing translations up to date.
|
||||||
|
|
||||||
If there's not, then today is your day to lead this effort! Here's how to start:
|
if there is no existing translation, you can create a new one by following these steps:
|
||||||
|
|
||||||
1. [Fork this repository](https://github.com/liyasthomas/postwoman/fork)
|
1. **[Fork the repository](https://github.com/hoppscotch/hoppscotch/fork).**
|
||||||
2. Create a new branch for your translation work e.g. `es`.
|
2. **Create a new branch for your translation.**
|
||||||
3. Copy `lang/en-US.json` to your target language file e.g. `lang/es-ES.json` and translate all the strings.
|
3. **Create target language file in the [`locales`](https://github.com/hoppscotch/hoppscotch/tree/main/packages/hoppscotch-app/locales) directory.**
|
||||||
4. Add your language entry to `nuxt.config.js`.
|
4. **Copy the contents of the source file [`locales/en.json`](https://github.com/hoppscotch/hoppscotch/blob/main/packages/hoppscotch-app/locales/en.json) to the target language file.**
|
||||||
|
5. **Translate the strings in the target language file.**
|
||||||
|
6. **Add your language entry to [`languages.json`](https://github.com/hoppscotch/hoppscotch/blob/main/packages/hoppscotch-app/languages.json).**
|
||||||
|
7. **Save & commit changes.**
|
||||||
|
8. **Send a pull request.**
|
||||||
|
|
||||||
e.g.
|
_You may send a pull request before all steps above are complete: e.g., you may want to ask for help with translations, or getting tests to pass. However, your pull request will not be merged until all steps above are complete._
|
||||||
|
|
||||||
```
|
Completing an initial translation of the whole site is a fairly large task. One way to break that task up is to work with other translators through pull requests on your fork. You can also [add collaborators to your fork](https://help.github.com/en/github/setting-up-and-managing-your-github-user-account/inviting-collaborators-to-a-personal-repository) if you'd like to invite other translators to commit directly to your fork and share responsibility for merging pull requests.
|
||||||
i18n: {
|
|
||||||
locales: [
|
|
||||||
{
|
|
||||||
code: "en",
|
|
||||||
name: "English",
|
|
||||||
iso: "en-US",
|
|
||||||
file: "en-US.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'es',
|
|
||||||
name: 'Español',
|
|
||||||
iso: 'es-ES',
|
|
||||||
file: 'es-ES.js'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Save & commit changes.
|
|
||||||
6. Send a pull request. (You may send a pull request before all steps above are complete: e.g., you may want to ask for help with translations, or getting tests to pass. However your pull request will not be merged until all steps above are complete.)
|
|
||||||
|
|
||||||
Completing an initial translation of the whole site is a fairly large task. One way to break that task up is to work with other translators through pull requests on your fork. You can also [add collaborators to your fork](https://help.github.com/en/github/setting-up-and-managing-your-github-user-account/inviting-collaborators-to-a-personal-repository) if you'd like to inivte other translators to commit directly to your fork and share responsibility for merging pull requests.
|
|
||||||
|
|
||||||
## Updating a translation
|
## Updating a translation
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
// @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-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
// Do not use font-display: swap for the icon font - it looks really bad when the page
|
|
||||||
// loads.
|
|
||||||
font-display: block;
|
|
||||||
src: url("~static/fonts/material-icons-v52.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;
|
|
||||||
text-transform: none;
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
word-wrap: normal;
|
|
||||||
direction: ltr;
|
|
||||||
-webkit-font-feature-settings: "liga";
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-feature-settings: "liga";
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* poppins-500 - latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: "Poppins";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
font-display: swap;
|
|
||||||
src: local("Poppins Medium"), local("Poppins-Medium"),
|
|
||||||
url("~static/fonts/poppins-v9-latin-500.woff2") format("woff2"),
|
|
||||||
url("~static/fonts/poppins-v9-latin-500.woff") format("woff");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* poppins-700 - latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: "Poppins";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: local("Poppins Bold"), local("Poppins-Bold"),
|
|
||||||
url("~static/fonts/poppins-v9-latin-700.woff2") format("woff2"),
|
|
||||||
url("~static/fonts/poppins-v9-latin-700.woff") format("woff");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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"),
|
|
||||||
url("~static/fonts/roboto-mono-v7-latin-regular.woff") format("woff");
|
|
||||||
}
|
|
||||||
@@ -1,784 +0,0 @@
|
|||||||
$responsiveWidth: 768px;
|
|
||||||
|
|
||||||
::selection {
|
|
||||||
background-color: var(--ac-color);
|
|
||||||
color: var(--act-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 4px;
|
|
||||||
height: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: var(--bg-dark-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: var(--fg-light-color);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--fg-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::placeholder {
|
|
||||||
color: var(--fg-light-color);
|
|
||||||
opacity: 0.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
outline: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
background-color: var(--bg-color);
|
|
||||||
color: var(--fg-color);
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
|
||||||
line-height: 1.5;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
user-select: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.afterLoad {
|
|
||||||
transition: background-color 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.sticky-footer footer {
|
|
||||||
opacity: 0.25;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: inline-flex;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
|
|
||||||
&.link {
|
|
||||||
color: var(--ac-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header,
|
|
||||||
footer {
|
|
||||||
& > div {
|
|
||||||
display: flex;
|
|
||||||
padding: 16px 8px;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper .page {
|
|
||||||
min-height: calc(100vh - 153px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header,
|
|
||||||
.content,
|
|
||||||
.columns,
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-first,
|
|
||||||
.sticky-inner {
|
|
||||||
display: flex;
|
|
||||||
order: 1;
|
|
||||||
flex-flow: column;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
flex: 1;
|
|
||||||
order: 2;
|
|
||||||
position: relative;
|
|
||||||
padding: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3.title {
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border-bottom: 1px dashed var(--brd-color);
|
|
||||||
margin: 16px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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: $bgcolor;
|
|
||||||
color: $fgcolor;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
box-shadow: 0 4px 24px rgba(black, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-arrow {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-style: solid;
|
|
||||||
position: absolute;
|
|
||||||
margin: 5px;
|
|
||||||
border-color: $bgcolor;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[x-placement^="top"] {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
.tooltip-arrow {
|
|
||||||
border-width: 5px 5px 0 5px;
|
|
||||||
border-left-color: transparent !important;
|
|
||||||
border-right-color: transparent !important;
|
|
||||||
border-bottom-color: transparent !important;
|
|
||||||
bottom: -5px;
|
|
||||||
left: calc(50% - 5px);
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[x-placement^="bottom"] {
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
.tooltip-arrow {
|
|
||||||
border-width: 0 5px 5px 5px;
|
|
||||||
border-left-color: transparent !important;
|
|
||||||
border-right-color: transparent !important;
|
|
||||||
border-top-color: transparent !important;
|
|
||||||
top: -5px;
|
|
||||||
left: calc(50% - 5px);
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[x-placement^="right"] {
|
|
||||||
margin-left: 5px;
|
|
||||||
|
|
||||||
.tooltip-arrow {
|
|
||||||
border-width: 5px 5px 5px 0;
|
|
||||||
border-left-color: transparent !important;
|
|
||||||
border-top-color: transparent !important;
|
|
||||||
border-bottom-color: transparent !important;
|
|
||||||
left: -5px;
|
|
||||||
top: calc(50% - 5px);
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[x-placement^="left"] {
|
|
||||||
margin-right: 5px;
|
|
||||||
|
|
||||||
.tooltip-arrow {
|
|
||||||
border-width: 5px 0 5px 5px;
|
|
||||||
border-top-color: transparent !important;
|
|
||||||
border-right-color: transparent !important;
|
|
||||||
border-bottom-color: transparent !important;
|
|
||||||
right: -5px;
|
|
||||||
top: calc(50% - 5px);
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.popover {
|
|
||||||
.wrapper {
|
|
||||||
min-height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-inner {
|
|
||||||
background: $bgcolor;
|
|
||||||
color: $fgcolor;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 5px 30px rgba(black, 0.1);
|
|
||||||
max-height: 256px;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
div {
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
justify-content: start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-arrow {
|
|
||||||
border-color: $bgcolor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[aria-hidden="true"] {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.15s, visibility 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[aria-hidden="false"] {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 0.15s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info:not(.toasted) {
|
|
||||||
margin-left: 4px;
|
|
||||||
color: var(--fg-light-color);
|
|
||||||
|
|
||||||
.material-icons {
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-color {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 4px;
|
|
||||||
padding: 6px 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: var(--ac-color);
|
|
||||||
color: var(--act-color);
|
|
||||||
font-size: 16px;
|
|
||||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
|
||||||
font-weight: 700;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
fill: var(--act-color);
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: inline-flex;
|
|
||||||
margin-left: 8px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
&: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:target,
|
|
||||||
section:target {
|
|
||||||
animation: highlight 2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes highlight {
|
|
||||||
50% {
|
|
||||||
box-shadow: 0 0 0 2px var(--ac-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="file"],
|
|
||||||
input[type="radio"],
|
|
||||||
.hide-on-large-screen,
|
|
||||||
#installPWA,
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.method,
|
|
||||||
kbd,
|
|
||||||
select,
|
|
||||||
input,
|
|
||||||
textarea,
|
|
||||||
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-weight: 400;
|
|
||||||
line-height: 1;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
user-select: text;
|
|
||||||
width: calc(100% - 8px);
|
|
||||||
resize: vertical;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
&: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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.ace_editor {
|
|
||||||
font-family: "Roboto Mono", monospace;
|
|
||||||
font-weight: 400;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
kbd,
|
|
||||||
code,
|
|
||||||
pre {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-wrapper {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
input {
|
|
||||||
text-transform: uppercase;
|
|
||||||
min-width: 128px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
option {
|
|
||||||
background-color: var(--bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&,
|
|
||||||
& + label {
|
|
||||||
vertical-align: middle;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: "\2714";
|
|
||||||
border: 1px solid var(--fg-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
display: inline-flex;
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 8px 8px 8px 0;
|
|
||||||
color: transparent;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:checked + label:before {
|
|
||||||
background-color: var(--ac-color);
|
|
||||||
border-color: var(--ac-color);
|
|
||||||
color: var(--act-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.error:not(input),
|
|
||||||
.disabled:not(input),
|
|
||||||
[disabled] {
|
|
||||||
background-color: var(--err-color);
|
|
||||||
color: var(--fg-light-color);
|
|
||||||
fill: var(--fg-light-color);
|
|
||||||
cursor: not-allowed;
|
|
||||||
|
|
||||||
&.icon {
|
|
||||||
color: var(--err-color);
|
|
||||||
fill: var(--err-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
padding: 4px;
|
|
||||||
color: var(--fg-light-color);
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
display: flex;
|
|
||||||
margin: 4px 0 4px;
|
|
||||||
padding: 0;
|
|
||||||
list-style-type: none;
|
|
||||||
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
flex: 1;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
&.shrink {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-wrap {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
* {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-on-small-screen {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-on-large-screen {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-response {
|
|
||||||
color: #ffeb3b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success-response {
|
|
||||||
color: #4bb543;
|
|
||||||
}
|
|
||||||
|
|
||||||
.redir-response {
|
|
||||||
color: #ff5722;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-error-response {
|
|
||||||
color: #a63232;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sv-error-response {
|
|
||||||
color: #b71c1c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.missing-data-response {
|
|
||||||
background-color: var(--err-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mono {
|
|
||||||
font-family: "Roboto Mono", monospace;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
#response-details-wrapper {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.covers-response {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: white;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#send {
|
|
||||||
&.show {
|
|
||||||
display: flex;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 86px;
|
|
||||||
left: 50%;
|
|
||||||
z-index: 10001;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
box-shadow: 0 4px 24px rgba(black, 0.2);
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toasted-container .toasted {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.toasted-ad {
|
|
||||||
background-color: #fefefe;
|
|
||||||
color: #121212;
|
|
||||||
padding: 16px !important;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
.action {
|
|
||||||
text-transform: none !important;
|
|
||||||
background-color: #121212;
|
|
||||||
color: #fefefe;
|
|
||||||
padding: 12px 16px !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
font-size: 16px !important;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 0 !important;
|
|
||||||
margin-left: 8px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.virtual-list {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.truncate {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
/**
|
|
||||||
Main Themes:
|
|
||||||
- dark (default)
|
|
||||||
- light
|
|
||||||
- black
|
|
||||||
- auto
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Dark is the default theme variant.
|
|
||||||
@mixin darkTheme {
|
|
||||||
// Background color
|
|
||||||
--bg-color: rgba(32, 33, 36, 1);
|
|
||||||
// Light Background color
|
|
||||||
--bg-light-color: rgba(255, 255, 255, 0.04);
|
|
||||||
// Dark Background color
|
|
||||||
--bg-dark-color: rgba(0, 0, 0, 0.2);
|
|
||||||
// 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(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.05);
|
|
||||||
// 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.04);
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root.light {
|
|
||||||
@include lightTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root.black {
|
|
||||||
@include blackTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root.auto {
|
|
||||||
@include darkTheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
:root.auto {
|
|
||||||
@include lightTheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
56
build.js
56
build.js
@@ -1,56 +0,0 @@
|
|||||||
const axios = require("axios")
|
|
||||||
const fs = require("fs")
|
|
||||||
const { spawnSync } = require("child_process")
|
|
||||||
const runCommand = (command, args) => spawnSync(command, args).stdout.toString().replace(/\n/g, "")
|
|
||||||
|
|
||||||
const FAIL_ON_ERROR = false
|
|
||||||
const PW_BUILD_DATA_DIR = "./.postwoman"
|
|
||||||
// const IS_DEV_MODE = process.argv.includes("--dev")
|
|
||||||
|
|
||||||
try {
|
|
||||||
;(async () => {
|
|
||||||
// Create the build data directory if it does not exist.
|
|
||||||
if (!fs.existsSync(PW_BUILD_DATA_DIR)) {
|
|
||||||
fs.mkdirSync(PW_BUILD_DATA_DIR)
|
|
||||||
}
|
|
||||||
|
|
||||||
let version = {}
|
|
||||||
// Get the current version name as the tag from Git.
|
|
||||||
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
|
|
||||||
.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: [
|
|
||||||
{
|
|
||||||
tag_name: require("./package.json").version,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}))
|
|
||||||
).data[0]["tag_name"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current version hash as the short hash from Git.
|
|
||||||
// version.hash = runCommand("git", ["rev-parse", "--short", "HEAD"])
|
|
||||||
// Get the 'variant' name as the branch, if it's not master.
|
|
||||||
// version.variant =
|
|
||||||
// process.env.TRAVIS_BRANCH ||
|
|
||||||
// runCommand("git", ["branch"])
|
|
||||||
// .split("* ")[1]
|
|
||||||
// .split(" ")[0] + (IS_DEV_MODE ? " - DEV MODE" : "")
|
|
||||||
// if (["", "master"].includes(version.variant)) {
|
|
||||||
// delete version.variant
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Write version data into a file
|
|
||||||
fs.writeFileSync(`${PW_BUILD_DATA_DIR}/version.json`, JSON.stringify(version))
|
|
||||||
})()
|
|
||||||
} catch (ex) {
|
|
||||||
console.error(ex)
|
|
||||||
process.exit(FAIL_ON_ERROR ? 1 : 0)
|
|
||||||
}
|
|
||||||
3
commitlint.config.js
Normal file
3
commitlint.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ["@commitlint/config-conventional"],
|
||||||
|
}
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
<template>
|
|
||||||
<modal v-if="show" @close="hideModal">
|
|
||||||
<div slot="header">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<h3 class="title">{{ $t("new_collection") }}</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_collection')"
|
|
||||||
@keyup.enter="addNewCollection"
|
|
||||||
/>
|
|
||||||
</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="addNewCollection">
|
|
||||||
{{ $t("save") }}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/modal"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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(this.$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")
|
|
||||||
this.$data.name = undefined
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
<template>
|
|
||||||
<modal v-if="show" @close="show = false">
|
|
||||||
<div slot="header">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<h3 class="title">{{ $t("new_folder") }}</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_folder')"
|
|
||||||
@keyup.enter="addNewFolder"
|
|
||||||
/>
|
|
||||||
</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="addNewFolder">
|
|
||||||
{{ $t("save") }}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
collection: Object,
|
|
||||||
collectionIndex: Number,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/modal"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addNewFolder() {
|
|
||||||
this.$store.commit("postwoman/addNewFolder", {
|
|
||||||
folder: { name: this.$data.name },
|
|
||||||
collectionIndex: this.$props.collectionIndex,
|
|
||||||
})
|
|
||||||
this.hideModal()
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
syncCollections() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[0].value) {
|
|
||||||
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hideModal() {
|
|
||||||
this.$emit("hide-modal")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<button class="icon" @click="toggleShowChildren">
|
|
||||||
<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>
|
|
||||||
</button>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
v-if="doc"
|
|
||||||
class="icon"
|
|
||||||
@click="$emit('select-collection')"
|
|
||||||
v-tooltip.left="$t('import')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">topic</i>
|
|
||||||
</button>
|
|
||||||
<v-popover>
|
|
||||||
<button class="tooltip-target icon" v-tooltip.left="$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="$emit('edit-collection')" v-close-popover>
|
|
||||||
<i class="material-icons">create</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>
|
|
||||||
|
|
||||||
<div v-show="showChildren">
|
|
||||||
<ul>
|
|
||||||
<li v-for="(folder, index) in collection.folders" :key="folder.name">
|
|
||||||
<folder
|
|
||||||
:folder="folder"
|
|
||||||
:folderIndex="index"
|
|
||||||
:collection-index="collectionIndex"
|
|
||||||
:doc="doc"
|
|
||||||
@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>{{ $t("collection_empty") }}</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li v-for="(request, index) in collection.requests" :key="index">
|
|
||||||
<request
|
|
||||||
:request="request"
|
|
||||||
:collection-index="collectionIndex"
|
|
||||||
:folder-index="-1"
|
|
||||||
:request-index="index"
|
|
||||||
:doc="doc"
|
|
||||||
@edit-request="
|
|
||||||
$emit('edit-request', {
|
|
||||||
request,
|
|
||||||
collectionIndex,
|
|
||||||
folderIndex: undefined,
|
|
||||||
requestIndex: index,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
display: flex;
|
|
||||||
margin-left: 32px;
|
|
||||||
border-left: 1px solid var(--brd-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
folder: () => import("./folder"),
|
|
||||||
request: () => import("./request"),
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
collectionIndex: Number,
|
|
||||||
collection: Object,
|
|
||||||
doc: Boolean,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showChildren: false,
|
|
||||||
selectedFolder: {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
syncCollections() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[0].value) {
|
|
||||||
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleShowChildren() {
|
|
||||||
this.showChildren = !this.showChildren
|
|
||||||
},
|
|
||||||
removeCollection() {
|
|
||||||
if (!confirm("Are you sure you want to remove this Collection?")) return
|
|
||||||
this.$store.commit("postwoman/removeCollection", {
|
|
||||||
collectionIndex: this.collectionIndex,
|
|
||||||
})
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
editFolder(collectionIndex, folder, folderIndex) {
|
|
||||||
this.$emit("edit-folder", { collectionIndex, folder, folderIndex })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
<template>
|
|
||||||
<modal v-if="show" @close="hideModal">
|
|
||||||
<div slot="header">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<h3 class="title">{{ $t("edit_collection") }}</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="editingCollection.name"
|
|
||||||
@keyup.enter="saveCollection"
|
|
||||||
/>
|
|
||||||
</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="saveCollection">
|
|
||||||
{{ $t("save") }}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
editingCollection: Object,
|
|
||||||
editingCollectionIndex: Number,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/modal"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
syncCollections() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[0].value) {
|
|
||||||
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveCollection() {
|
|
||||||
if (!this.$data.name) {
|
|
||||||
this.$toast.info(this.$t("invalid_collection_name"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const collectionUpdated = {
|
|
||||||
...this.$props.editingCollection,
|
|
||||||
name: this.$data.name,
|
|
||||||
}
|
|
||||||
this.$store.commit("postwoman/editCollection", {
|
|
||||||
collection: collectionUpdated,
|
|
||||||
collectionIndex: this.$props.editingCollectionIndex,
|
|
||||||
})
|
|
||||||
this.$emit("hide-modal")
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
hideModal() {
|
|
||||||
this.$emit("hide-modal")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
<template>
|
|
||||||
<modal v-if="show" @close="show = false">
|
|
||||||
<div slot="header">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<h3 class="title">{{ $t("edit_folder") }}</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="folder.name" @keyup.enter="editFolder" />
|
|
||||||
</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="editFolder">
|
|
||||||
{{ $t("save") }}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
collection: Object,
|
|
||||||
collectionIndex: Number,
|
|
||||||
folder: Object,
|
|
||||||
folderIndex: Number,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/modal"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
syncCollections() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[0].value) {
|
|
||||||
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
editFolder() {
|
|
||||||
this.$store.commit("postwoman/editFolder", {
|
|
||||||
collectionIndex: this.$props.collectionIndex,
|
|
||||||
folder: { ...this.$props.folder, name: this.$data.name },
|
|
||||||
folderIndex: this.$props.folderIndex,
|
|
||||||
})
|
|
||||||
this.hideModal()
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
hideModal() {
|
|
||||||
this.$emit("hide-modal")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
<template>
|
|
||||||
<modal v-if="show" @close="hideModal">
|
|
||||||
<div slot="header">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<h3 class="title">{{ $t("edit_request") }}</h3>
|
|
||||||
<div>
|
|
||||||
<button class="icon" @click="hideModal">
|
|
||||||
<i class="material-icons">close</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div slot="body">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<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 :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>
|
|
||||||
</select>
|
|
||||||
</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>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</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="saveRequest">
|
|
||||||
{{ $t("save") }}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
collectionIndex: Number,
|
|
||||||
folderIndex: Number,
|
|
||||||
request: Object,
|
|
||||||
requestIndex: Number,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/modal"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
requestUpdateData: {
|
|
||||||
name: undefined,
|
|
||||||
collectionIndex: undefined,
|
|
||||||
folderIndex: undefined,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
"requestUpdateData.collectionIndex": function resetFolderIndex() {
|
|
||||||
// if user choosen some folder, than selected other collection, which doesn't have any folders
|
|
||||||
// than `requestUpdateData.folderIndex` won't be reseted
|
|
||||||
this.$data.requestUpdateData.folderIndex = undefined
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
folders() {
|
|
||||||
const userSelectedAnyCollection = this.$data.requestUpdateData.collectionIndex !== undefined
|
|
||||||
if (!userSelectedAnyCollection) return []
|
|
||||||
|
|
||||||
return this.$store.state.postwoman.collections[this.$data.requestUpdateData.collectionIndex]
|
|
||||||
.folders
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
syncCollections() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[0].value) {
|
|
||||||
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveRequest() {
|
|
||||||
const userSelectedAnyCollection = this.$data.requestUpdateData.collectionIndex !== undefined
|
|
||||||
|
|
||||||
const requestUpdated = {
|
|
||||||
...this.$props.request,
|
|
||||||
name: this.$data.requestUpdateData.name || this.$props.request.name,
|
|
||||||
collection: userSelectedAnyCollection
|
|
||||||
? this.$data.requestUpdateData.collectionIndex
|
|
||||||
: this.$props.collectionIndex,
|
|
||||||
folder: this.$data.requestUpdateData.folderIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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", {
|
|
||||||
requestOldCollectionIndex: this.$props.collectionIndex,
|
|
||||||
requestOldFolderIndex: this.$props.folderIndex,
|
|
||||||
requestOldIndex: this.$props.requestIndex,
|
|
||||||
requestNew: requestUpdated,
|
|
||||||
requestNewCollectionIndex: requestUpdated.collection,
|
|
||||||
requestNewFolderIndex: requestUpdated.folder,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.hideModal()
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
hideModal() {
|
|
||||||
this.$emit("hide-modal")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<div>
|
|
||||||
<button class="icon" @click="toggleShowChildren">
|
|
||||||
<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>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<v-popover>
|
|
||||||
<button class="tooltip-target icon" v-tooltip.left="$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-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="(request, index) in folder.requests" :key="index">
|
|
||||||
<request
|
|
||||||
:request="request"
|
|
||||||
:collection-index="collectionIndex"
|
|
||||||
:folder-index="folderIndex"
|
|
||||||
:request-index="index"
|
|
||||||
:doc="doc"
|
|
||||||
@edit-request="
|
|
||||||
$emit('edit-request', {
|
|
||||||
request,
|
|
||||||
collectionIndex,
|
|
||||||
folderIndex,
|
|
||||||
requestIndex: index,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
<li v-if="folder.requests.length === 0">
|
|
||||||
<label>{{ $t("folder_empty") }}</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
display: flex;
|
|
||||||
margin-left: 32px;
|
|
||||||
border-left: 1px solid var(--brd-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
folder: Object,
|
|
||||||
collectionIndex: Number,
|
|
||||||
folderIndex: Number,
|
|
||||||
doc: Boolean,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
request: () => import("./request"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showChildren: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
syncCollections() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[0].value) {
|
|
||||||
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleShowChildren() {
|
|
||||||
this.showChildren = !this.showChildren
|
|
||||||
},
|
|
||||||
selectRequest(request) {
|
|
||||||
this.$store.commit("postwoman/selectRequest", { request })
|
|
||||||
},
|
|
||||||
removeFolder() {
|
|
||||||
if (!confirm("Are you sure you want to remove this folder?")) return
|
|
||||||
this.$store.commit("postwoman/removeFolder", {
|
|
||||||
collectionIndex: this.collectionIndex,
|
|
||||||
folderIndex: this.folderIndex,
|
|
||||||
})
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
editFolder() {
|
|
||||||
this.$emit("edit-folder")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
<template>
|
|
||||||
<modal v-if="show" @close="hideModal">
|
|
||||||
<div slot="header">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<h3 class="title">{{ $t("import_export") }} {{ $t("collections") }}</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="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>
|
|
||||||
<div slot="body">
|
|
||||||
<textarea v-model="collectionJson" 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 "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
fb,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/modal"),
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
collectionJson() {
|
|
||||||
return JSON.stringify(this.$store.state.postwoman.collections, 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 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/replaceCollections", collections)
|
|
||||||
this.fileImported()
|
|
||||||
this.syncToFBCollections()
|
|
||||||
}
|
|
||||||
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
|
|
||||||
},
|
|
||||||
importFromJSON() {
|
|
||||||
let reader = new FileReader()
|
|
||||||
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()
|
|
||||||
this.syncToFBCollections()
|
|
||||||
}
|
|
||||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
|
||||||
},
|
|
||||||
exportJSON() {
|
|
||||||
let text = this.collectionJson
|
|
||||||
text = text.replace(/\n/g, "\r\n")
|
|
||||||
let blob = new Blob([text], {
|
|
||||||
type: "text/json",
|
|
||||||
})
|
|
||||||
let anchor = document.createElement("a")
|
|
||||||
anchor.download = "postwoman-collection.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",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
syncCollections() {
|
|
||||||
this.$store.commit("postwoman/replaceCollections", fb.currentCollections)
|
|
||||||
this.fileImported()
|
|
||||||
},
|
|
||||||
syncToFBCollections() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[0].value) {
|
|
||||||
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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({ name, request }) {
|
|
||||||
let pwRequest = {
|
|
||||||
url: "",
|
|
||||||
path: "",
|
|
||||||
method: "",
|
|
||||||
auth: "",
|
|
||||||
httpUser: "",
|
|
||||||
httpPassword: "",
|
|
||||||
passwordFieldType: "password",
|
|
||||||
bearerToken: "",
|
|
||||||
headers: [],
|
|
||||||
params: [],
|
|
||||||
bodyParams: [],
|
|
||||||
rawParams: "",
|
|
||||||
rawInput: false,
|
|
||||||
contentType: "",
|
|
||||||
requestType: "",
|
|
||||||
name: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
pwRequest.name = name
|
|
||||||
let requestObjectUrl = request.url.raw.match(/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/)
|
|
||||||
if (requestObjectUrl) {
|
|
||||||
pwRequest.url = requestObjectUrl[1]
|
|
||||||
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
|
|
||||||
}
|
|
||||||
pwRequest.method = request.method
|
|
||||||
let itemAuth = request.auth ? 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 = request.header
|
|
||||||
if (requestObjectHeaders) {
|
|
||||||
pwRequest.headers = requestObjectHeaders
|
|
||||||
for (let header of pwRequest.headers) {
|
|
||||||
delete header.name
|
|
||||||
delete header.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let requestObjectParams = request.url.query
|
|
||||||
if (requestObjectParams) {
|
|
||||||
pwRequest.params = requestObjectParams
|
|
||||||
for (let param of pwRequest.params) {
|
|
||||||
delete param.disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (request.body) {
|
|
||||||
if (request.body.mode === "urlencoded") {
|
|
||||||
let params = request.body.urlencoded
|
|
||||||
pwRequest.bodyParams = params ? params : []
|
|
||||||
for (let param of pwRequest.bodyParams) {
|
|
||||||
delete param.type
|
|
||||||
}
|
|
||||||
} else if (request.body.mode === "raw") {
|
|
||||||
pwRequest.rawInput = true
|
|
||||||
pwRequest.rawParams = request.body.raw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pwRequest
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
<!--
|
|
||||||
TODO:
|
|
||||||
- probably refactor and pass event arguments to modals directly without unpacking
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<pw-section class="yellow" :label="$t('collections')" ref="collections">
|
|
||||||
<addCollection :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
|
|
||||||
<editCollection
|
|
||||||
:show="showModalEdit"
|
|
||||||
:editingCollection="editingCollection"
|
|
||||||
:editingCollectionIndex="editingCollectionIndex"
|
|
||||||
@hide-modal="displayModalEdit(false)"
|
|
||||||
/>
|
|
||||||
<addFolder
|
|
||||||
:show="showModalAddFolder"
|
|
||||||
:collection="editingCollection"
|
|
||||||
:collectionIndex="editingCollectionIndex"
|
|
||||||
@hide-modal="displayModalAddFolder(false)"
|
|
||||||
/>
|
|
||||||
<editFolder
|
|
||||||
:show="showModalEditFolder"
|
|
||||||
:collection="editingCollection"
|
|
||||||
:collectionIndex="editingCollectionIndex"
|
|
||||||
:folder="editingFolder"
|
|
||||||
:folderIndex="editingFolderIndex"
|
|
||||||
@hide-modal="displayModalEditFolder(false)"
|
|
||||||
/>
|
|
||||||
<editRequest
|
|
||||||
:show="showModalEditRequest"
|
|
||||||
:collectionIndex="editingCollectionIndex"
|
|
||||||
:folderIndex="editingFolderIndex"
|
|
||||||
:request="editingRequest"
|
|
||||||
:requestIndex="editingRequestIndex"
|
|
||||||
@hide-modal="displayModalEditRequest(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>{{ $t("new") }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button class="icon" @click="displayModalImportExport(true)">
|
|
||||||
{{ $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_outline</i>
|
|
||||||
</button>
|
|
||||||
</a> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p v-if="collections.length === 0" class="info">
|
|
||||||
<i class="material-icons">help_outline</i> Create new collection
|
|
||||||
</p>
|
|
||||||
<div class="virtual-list">
|
|
||||||
<ul>
|
|
||||||
<li v-for="(collection, index) in collections" :key="collection.name">
|
|
||||||
<collection
|
|
||||||
:collection-index="index"
|
|
||||||
:collection="collection"
|
|
||||||
:doc="doc"
|
|
||||||
@edit-collection="editCollection(collection, index)"
|
|
||||||
@add-folder="addFolder(collection, index)"
|
|
||||||
@edit-folder="editFolder($event)"
|
|
||||||
@edit-request="editRequest($event)"
|
|
||||||
@select-collection="$emit('use-collection', collection)"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</pw-section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.virtual-list {
|
|
||||||
max-height: calc(100vh - 245px);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import collection from "./collection"
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
collection,
|
|
||||||
"pw-section": () => import("../layout/section"),
|
|
||||||
addCollection: () => import("./addCollection"),
|
|
||||||
addFolder: () => import("./addFolder"),
|
|
||||||
editCollection: () => import("./editCollection"),
|
|
||||||
editFolder: () => import("./editFolder"),
|
|
||||||
editRequest: () => import("./editRequest"),
|
|
||||||
importExportCollections: () => import("./importExportCollections"),
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
doc: Boolean,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showModalAdd: false,
|
|
||||||
showModalEdit: false,
|
|
||||||
showModalImportExport: false,
|
|
||||||
showModalAddFolder: false,
|
|
||||||
showModalEditFolder: false,
|
|
||||||
showModalEditRequest: false,
|
|
||||||
editingCollection: undefined,
|
|
||||||
editingCollectionIndex: undefined,
|
|
||||||
editingFolder: undefined,
|
|
||||||
editingFolderIndex: undefined,
|
|
||||||
editingRequest: undefined,
|
|
||||||
editingRequestIndex: undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
collections() {
|
|
||||||
return fb.currentUser !== null
|
|
||||||
? fb.currentCollections
|
|
||||||
: 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
|
|
||||||
},
|
|
||||||
displayModalEdit(shouldDisplay) {
|
|
||||||
this.showModalEdit = shouldDisplay
|
|
||||||
|
|
||||||
if (!shouldDisplay) this.resetSelectedData()
|
|
||||||
},
|
|
||||||
displayModalImportExport(shouldDisplay) {
|
|
||||||
this.showModalImportExport = shouldDisplay
|
|
||||||
},
|
|
||||||
displayModalAddFolder(shouldDisplay) {
|
|
||||||
this.showModalAddFolder = shouldDisplay
|
|
||||||
|
|
||||||
if (!shouldDisplay) this.resetSelectedData()
|
|
||||||
},
|
|
||||||
displayModalEditFolder(shouldDisplay) {
|
|
||||||
this.showModalEditFolder = shouldDisplay
|
|
||||||
|
|
||||||
if (!shouldDisplay) this.resetSelectedData()
|
|
||||||
},
|
|
||||||
displayModalEditRequest(shouldDisplay) {
|
|
||||||
this.showModalEditRequest = shouldDisplay
|
|
||||||
|
|
||||||
if (!shouldDisplay) this.resetSelectedData()
|
|
||||||
},
|
|
||||||
editCollection(collection, collectionIndex) {
|
|
||||||
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
|
|
||||||
this.$data.editingCollection = collection
|
|
||||||
this.$data.editingCollectionIndex = collectionIndex
|
|
||||||
this.$data.editingFolder = folder
|
|
||||||
this.$data.editingFolderIndex = folderIndex
|
|
||||||
this.displayModalEditFolder(true)
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
editRequest(payload) {
|
|
||||||
const { request, collectionIndex, folderIndex, requestIndex } = payload
|
|
||||||
this.$data.editingCollectionIndex = collectionIndex
|
|
||||||
this.$data.editingFolderIndex = folderIndex
|
|
||||||
this.$data.editingRequest = request
|
|
||||||
this.$data.editingRequestIndex = requestIndex
|
|
||||||
this.displayModalEditRequest(true)
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
resetSelectedData() {
|
|
||||||
this.$data.editingCollection = undefined
|
|
||||||
this.$data.editingCollectionIndex = undefined
|
|
||||||
this.$data.editingFolder = undefined
|
|
||||||
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,81 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="!doc ? selectRequest() : {}"
|
|
||||||
v-tooltip="!doc ? $t('use_request') : ''"
|
|
||||||
>
|
|
||||||
<i class="material-icons">insert_drive_file</i>
|
|
||||||
<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-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>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
request: Object,
|
|
||||||
collectionIndex: Number,
|
|
||||||
folderIndex: Number,
|
|
||||||
requestIndex: Number,
|
|
||||||
doc: Boolean,
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
syncCollections() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[0].value) {
|
|
||||||
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectRequest() {
|
|
||||||
this.$store.commit("postwoman/selectRequest", { request: this.request })
|
|
||||||
},
|
|
||||||
removeRequest() {
|
|
||||||
if (!confirm("Are you sure you want to remove this request?")) return
|
|
||||||
this.$store.commit("postwoman/removeRequest", {
|
|
||||||
collectionIndex: this.collectionIndex,
|
|
||||||
folderIndex: this.folderIndex,
|
|
||||||
requestIndex: this.requestIndex,
|
|
||||||
})
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
<template>
|
|
||||||
<modal v-if="show" @close="hideModal">
|
|
||||||
<div slot="header">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<h3 class="title">{{ $t("save_request_as") }}</h3>
|
|
||||||
<div>
|
|
||||||
<button class="icon" @click="hideModal">
|
|
||||||
<i class="material-icons">close</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div slot="body">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="selectLabel">{{ $t("label") }}</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="selectLabel"
|
|
||||||
v-model="requestData.name"
|
|
||||||
:placeholder="defaultRequestName"
|
|
||||||
@keyup.enter="saveRequestAs"
|
|
||||||
/>
|
|
||||||
<label for="selectCollection">{{ $t("collection") }}</label>
|
|
||||||
<span class="select-wrapper">
|
|
||||||
<select type="text" id="selectCollection" v-model="requestData.collectionIndex">
|
|
||||||
<option :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>
|
|
||||||
</select>
|
|
||||||
</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>
|
|
||||||
</select>
|
|
||||||
</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>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</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="saveRequestAs">
|
|
||||||
{{ $t("save") }}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
editingRequest: Object,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/modal"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
defaultRequestName: "My Request",
|
|
||||||
requestData: {
|
|
||||||
name: undefined,
|
|
||||||
collectionIndex: undefined,
|
|
||||||
folderIndex: undefined,
|
|
||||||
requestIndex: undefined,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
|
|
||||||
// if user choosen some folder, than selected other collection, which doesn't have any folders
|
|
||||||
// than `requestUpdateData.folderIndex` won't be reseted
|
|
||||||
this.$data.requestData.folderIndex = undefined
|
|
||||||
this.$data.requestData.requestIndex = undefined
|
|
||||||
},
|
|
||||||
"requestData.folderIndex": function resetRequestIndex() {
|
|
||||||
this.$data.requestData.requestIndex = undefined
|
|
||||||
},
|
|
||||||
editingRequest(request) {
|
|
||||||
this.defaultRequestName = request.label || "My Request"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
folders() {
|
|
||||||
const userSelectedAnyCollection = 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
|
|
||||||
},
|
|
||||||
requests() {
|
|
||||||
const userSelectedAnyCollection = this.$data.requestData.collectionIndex !== undefined
|
|
||||||
if (!userSelectedAnyCollection) return []
|
|
||||||
|
|
||||||
const userSelectedAnyFolder = this.$data.requestData.folderIndex !== undefined
|
|
||||||
if (userSelectedAnyFolder) {
|
|
||||||
const collection = this.$store.state.postwoman.collections[
|
|
||||||
this.$data.requestData.collectionIndex
|
|
||||||
]
|
|
||||||
const folder = collection.folders[this.$data.requestData.folderIndex]
|
|
||||||
const requests = folder.requests
|
|
||||||
return requests
|
|
||||||
} else {
|
|
||||||
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(this.$t("select_collection"), {
|
|
||||||
icon: "error",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestUpdated = {
|
|
||||||
...this.$props.editingRequest,
|
|
||||||
name: this.$data.requestData.name || this.$data.defaultRequestName,
|
|
||||||
collection: this.$data.requestData.collectionIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$store.commit("postwoman/saveRequestAs", {
|
|
||||||
request: requestUpdated,
|
|
||||||
collectionIndex: this.$data.requestData.collectionIndex,
|
|
||||||
folderIndex: this.$data.requestData.folderIndex,
|
|
||||||
requestIndex: this.$data.requestData.requestIndex,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.hideModal()
|
|
||||||
this.syncCollections()
|
|
||||||
},
|
|
||||||
hideModal() {
|
|
||||||
this.$emit("hide-modal")
|
|
||||||
this.$emit("hide-model") // for backward compatibility // TODO: use fixed event
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<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 "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/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.$emit("hide-modal")
|
|
||||||
this.$data.name = undefined
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
<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>
|
|
||||||
</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 { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
editingEnvironment: Object,
|
|
||||||
editingEnvironmentIndex: Number,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/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: {
|
|
||||||
syncEnvironments() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[1].value) {
|
|
||||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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)
|
|
||||||
this.syncEnvironments()
|
|
||||||
},
|
|
||||||
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()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
this.syncEnvironments()
|
|
||||||
},
|
|
||||||
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")
|
|
||||||
this.syncEnvironments()
|
|
||||||
},
|
|
||||||
hideModal() {
|
|
||||||
this.$emit("hide-modal")
|
|
||||||
this.$data.name = undefined
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="$emit('select-environment')"
|
|
||||||
v-tooltip.bottom="$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.left="$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>
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
environment: Object,
|
|
||||||
environmentIndex: Number,
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
syncEnvironments() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[1].value) {
|
|
||||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeEnvironment() {
|
|
||||||
if (!confirm("Are you sure you want to remove this environment?")) return
|
|
||||||
this.$store.commit("postwoman/removeEnvironment", this.environmentIndex)
|
|
||||||
this.syncEnvironments()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
<template>
|
|
||||||
<modal v-if="show" @close="hideModal">
|
|
||||||
<div slot="header">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<h3 class="title">{{ $t("import_export") }} {{ $t("environments") }}</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 "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
fb,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
show: Boolean,
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
modal: () => import("~/components/ui/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()
|
|
||||||
this.syncToFBEnvironments()
|
|
||||||
},
|
|
||||||
importFromJSON() {
|
|
||||||
let reader = new FileReader()
|
|
||||||
reader.onload = (event) => {
|
|
||||||
let content = event.target.result
|
|
||||||
let importFileObj = JSON.parse(content)
|
|
||||||
if (
|
|
||||||
importFileObj["_postman_variable_scope"] === "environment" ||
|
|
||||||
importFileObj["_postman_variable_scope"] === "globals"
|
|
||||||
) {
|
|
||||||
this.importFromPostman(importFileObj)
|
|
||||||
} else {
|
|
||||||
this.importFromPostwoman(importFileObj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
|
||||||
this.syncToFBEnvironments()
|
|
||||||
},
|
|
||||||
importFromPostwoman(environments) {
|
|
||||||
let confirmation = this.$t("file_imported")
|
|
||||||
this.$store.commit("postwoman/importAddEnvironments", {
|
|
||||||
environments,
|
|
||||||
confirmation,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
importFromPostman(importFileObj) {
|
|
||||||
let environment = { name: importFileObj.name, variables: [] }
|
|
||||||
importFileObj.values.forEach((element) =>
|
|
||||||
environment.variables.push({ key: element.key, value: element.value })
|
|
||||||
)
|
|
||||||
let environments = [environment]
|
|
||||||
this.importFromPostwoman(environments)
|
|
||||||
},
|
|
||||||
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()
|
|
||||||
},
|
|
||||||
syncToFBEnvironments() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
if (fb.currentSettings[1].value) {
|
|
||||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fileImported() {
|
|
||||||
this.$toast.info(this.$t("file_imported"), {
|
|
||||||
icon: "folder_shared",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
<template>
|
|
||||||
<pw-section class="green" icon="history" :label="$t('environments')" ref="environments">
|
|
||||||
<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">
|
|
||||||
<i class="material-icons">help_outline</i> Create new environment
|
|
||||||
</p>
|
|
||||||
<div class="virtual-list">
|
|
||||||
<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>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</pw-section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.virtual-list {
|
|
||||||
max-height: calc(100vh - 245px);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import environment from "./environment"
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
environment,
|
|
||||||
"pw-section": () => import("../layout/section"),
|
|
||||||
addEnvironment: () => import("./addEnvironment"),
|
|
||||||
editEnvironment: () => import("./editEnvironment"),
|
|
||||||
importExportEnvironment: () => import("./importExportEnvironment"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showModalImportExport: false,
|
|
||||||
showModalAdd: false,
|
|
||||||
showModalEdit: false,
|
|
||||||
editingEnvironment: undefined,
|
|
||||||
editingEnvironmentIndex: undefined,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
environments() {
|
|
||||||
return fb.currentUser !== null
|
|
||||||
? fb.currentEnvironments
|
|
||||||
: this.$store.state.postwoman.environments
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
this._keyListener = function (e) {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
e.preventDefault()
|
|
||||||
this.showModalImportExport = this.showModalAdd = this.showModalEdit = 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>
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div v-if="fb.currentFeeds.length !== 0" class="virtual-list">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
<ul v-else>
|
|
||||||
<li>
|
|
||||||
<label class="info">{{ $t("empty") }}</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.virtual-list {
|
|
||||||
max-height: calc(100vh - 298px);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
fb,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
deleteFeed(feed) {
|
|
||||||
fb.deleteFeed(feed.id)
|
|
||||||
this.$toast.error(this.$t("deleted"), {
|
|
||||||
icon: "delete",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<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 "~/helpers/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>
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<button class="icon" @click="signInWithGoogle" v-close-popover>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="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" class="material-icons">
|
|
||||||
<path
|
|
||||||
d="M12 0C5.374 0 0 5.373 0 12c0 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.23A11.509 11.509 0 0112 5.803c1.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.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>GitHub</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import firebase from "firebase/app"
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
fb,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
showLoginSuccess() {
|
|
||||||
this.$toast.info(this.$t("login_success"), {
|
|
||||||
icon: "vpn_key",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
signInWithGoogle() {
|
|
||||||
const provider = new firebase.auth.GoogleAuthProvider()
|
|
||||||
const self = this
|
|
||||||
firebase
|
|
||||||
.auth()
|
|
||||||
.signInWithPopup(provider)
|
|
||||||
.then(({ additionalUserInfo }) => {
|
|
||||||
if (additionalUserInfo.isNewUser) {
|
|
||||||
self.$toast.info(`${self.$t("turn_on")} ${self.$t("sync")}`, {
|
|
||||||
icon: "sync",
|
|
||||||
duration: null,
|
|
||||||
closeOnSwipe: false,
|
|
||||||
action: {
|
|
||||||
text: self.$t("yes"),
|
|
||||||
onClick: (e, toastObject) => {
|
|
||||||
fb.writeSettings("syncHistory", true)
|
|
||||||
fb.writeSettings("syncCollections", true)
|
|
||||||
fb.writeSettings("syncEnvironments", true)
|
|
||||||
self.$router.push({ path: "/settings" })
|
|
||||||
toastObject.remove()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
self.showLoginSuccess()
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
// An error happened.
|
|
||||||
if (err.code === "auth/account-exists-with-different-credential") {
|
|
||||||
// Step 2.
|
|
||||||
// User's email already exists.
|
|
||||||
// The pending Google credential.
|
|
||||||
const pendingCred = err.credential
|
|
||||||
// The provider account's email address.
|
|
||||||
const email = err.email
|
|
||||||
// Get sign-in methods for this email.
|
|
||||||
firebase
|
|
||||||
.auth()
|
|
||||||
.fetchSignInMethodsForEmail(email)
|
|
||||||
.then((methods) => {
|
|
||||||
// Step 3.
|
|
||||||
// If the user has several sign-in methods,
|
|
||||||
// the first method in the list will be the "recommended" method to use.
|
|
||||||
if (methods[0] === "password") {
|
|
||||||
// Asks the user their password.
|
|
||||||
// In real scenario, you should handle this asynchronously.
|
|
||||||
const password = promptUserForPassword() // TODO: implement promptUserForPassword.
|
|
||||||
auth
|
|
||||||
.signInWithEmailAndPassword(email, password)
|
|
||||||
.then((
|
|
||||||
user // Step 4a.
|
|
||||||
) => user.linkWithCredential(pendingCred))
|
|
||||||
.then(() => {
|
|
||||||
// Google account successfully linked to the existing Firebase user.
|
|
||||||
self.showLoginSuccess()
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.$toast.info(`${self.$t("login_with")}`, {
|
|
||||||
icon: "vpn_key",
|
|
||||||
duration: null,
|
|
||||||
closeOnSwipe: false,
|
|
||||||
action: {
|
|
||||||
text: self.$t("yes"),
|
|
||||||
onClick: (e, toastObject) => {
|
|
||||||
// All the other cases are external providers.
|
|
||||||
// Construct provider object for that provider.
|
|
||||||
// TODO: implement getProviderForProviderId.
|
|
||||||
const provider = new firebase.auth.GithubAuthProvider()
|
|
||||||
// At this point, you should let the user know that they already has an account
|
|
||||||
// but with a different provider, and let them validate the fact they want to
|
|
||||||
// sign in with this provider.
|
|
||||||
// Sign in to provider. Note: browsers usually block popup triggered asynchronously,
|
|
||||||
// so in real scenario you should ask the user to click on a "continue" button
|
|
||||||
// that will trigger the signInWithPopup.
|
|
||||||
firebase
|
|
||||||
.auth()
|
|
||||||
.signInWithPopup(provider)
|
|
||||||
.then(({ user }) => {
|
|
||||||
// Remember that the user may have signed in with an account that has a different email
|
|
||||||
// address than the first one. This can happen as Firebase doesn't control the provider's
|
|
||||||
// sign in flow and the user is free to login using whichever account they own.
|
|
||||||
// Step 4b.
|
|
||||||
// Link to Google credential.
|
|
||||||
// As we have access to the pending credential, we can directly call the link method.
|
|
||||||
user.linkAndRetrieveDataWithCredential(pendingCred).then((usercred) => {
|
|
||||||
// Google account successfully linked to the existing Firebase user.
|
|
||||||
self.showLoginSuccess()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
toastObject.remove()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
signInWithGithub() {
|
|
||||||
const provider = new firebase.auth.GithubAuthProvider()
|
|
||||||
const self = this
|
|
||||||
firebase
|
|
||||||
.auth()
|
|
||||||
.signInWithPopup(provider)
|
|
||||||
.then(({ additionalUserInfo }) => {
|
|
||||||
if (additionalUserInfo.isNewUser) {
|
|
||||||
self.$toast.info(`${self.$t("turn_on")} ${self.$t("sync")}`, {
|
|
||||||
icon: "sync",
|
|
||||||
duration: null,
|
|
||||||
closeOnSwipe: false,
|
|
||||||
action: {
|
|
||||||
text: self.$t("yes"),
|
|
||||||
onClick: (e, toastObject) => {
|
|
||||||
fb.writeSettings("syncHistory", true)
|
|
||||||
fb.writeSettings("syncCollections", true)
|
|
||||||
fb.writeSettings("syncEnvironments", true)
|
|
||||||
self.$router.push({ path: "/settings" })
|
|
||||||
toastObject.remove()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
self.showLoginSuccess()
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
// An error happened.
|
|
||||||
if (err.code === "auth/account-exists-with-different-credential") {
|
|
||||||
// Step 2.
|
|
||||||
// User's email already exists.
|
|
||||||
// The pending Google credential.
|
|
||||||
const pendingCred = err.credential
|
|
||||||
// The provider account's email address.
|
|
||||||
const email = err.email
|
|
||||||
// Get sign-in methods for this email.
|
|
||||||
firebase
|
|
||||||
.auth()
|
|
||||||
.fetchSignInMethodsForEmail(email)
|
|
||||||
.then((methods) => {
|
|
||||||
// Step 3.
|
|
||||||
// If the user has several sign-in methods,
|
|
||||||
// the first method in the list will be the "recommended" method to use.
|
|
||||||
if (methods[0] === "password") {
|
|
||||||
// Asks the user their password.
|
|
||||||
// In real scenario, you should handle this asynchronously.
|
|
||||||
const password = promptUserForPassword() // TODO: implement promptUserForPassword.
|
|
||||||
firebase
|
|
||||||
.auth()
|
|
||||||
.signInWithEmailAndPassword(email, password)
|
|
||||||
.then((
|
|
||||||
user // Step 4a.
|
|
||||||
) => user.linkWithCredential(pendingCred))
|
|
||||||
.then(() => {
|
|
||||||
// Google account successfully linked to the existing Firebase user.
|
|
||||||
self.showLoginSuccess()
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.$toast.info(`${self.$t("login_with")}`, {
|
|
||||||
icon: "vpn_key",
|
|
||||||
duration: null,
|
|
||||||
closeOnSwipe: false,
|
|
||||||
action: {
|
|
||||||
text: self.$t("yes"),
|
|
||||||
onClick: (e, toastObject) => {
|
|
||||||
// All the other cases are external providers.
|
|
||||||
// Construct provider object for that provider.
|
|
||||||
// TODO: implement getProviderForProviderId.
|
|
||||||
const provider = new firebase.auth.GoogleAuthProvider()
|
|
||||||
// At this point, you should let the user know that they already has an account
|
|
||||||
// but with a different provider, and let them validate the fact they want to
|
|
||||||
// sign in with this provider.
|
|
||||||
// Sign in to provider. Note: browsers usually block popup triggered asynchronously,
|
|
||||||
// so in real scenario you should ask the user to click on a "continue" button
|
|
||||||
// that will trigger the signInWithPopup.
|
|
||||||
firebase
|
|
||||||
.auth()
|
|
||||||
.signInWithPopup(provider)
|
|
||||||
.then(({ user }) => {
|
|
||||||
// Remember that the user may have signed in with an account that has a different email
|
|
||||||
// address than the first one. This can happen as Firebase doesn't control the provider's
|
|
||||||
// sign in flow and the user is free to login using whichever account they own.
|
|
||||||
// Step 4b.
|
|
||||||
// Link to Google credential.
|
|
||||||
// As we have access to the pending credential, we can directly call the link method.
|
|
||||||
user.linkAndRetrieveDataWithCredential(pendingCred).then((usercred) => {
|
|
||||||
self.showLoginSuccess()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
toastObject.remove()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<button class="icon" @click="logout" v-close-popover>
|
|
||||||
<i class="material-icons">exit_to_app</i>
|
|
||||||
<span>{{ $t("logout") }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import firebase from "firebase/app"
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
fb,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
logout() {
|
|
||||||
fb.currentUser = null
|
|
||||||
const self = this
|
|
||||||
firebase
|
|
||||||
.auth()
|
|
||||||
.signOut()
|
|
||||||
.catch((err) => {
|
|
||||||
self.$toast.show(err.message || err, {
|
|
||||||
icon: "error",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
self.$toast.info(this.$t("logged_out"), {
|
|
||||||
icon: "vpn_key",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
<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: 8px;
|
|
||||||
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, { name, type }, index) =>
|
|
||||||
acc + `${name}: ${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>
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="show-if-initialized" :class="{ initialized }">
|
|
||||||
<pre ref="editor"></pre>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.show-if-initialized {
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
&.initialized {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const DEFAULT_THEME = "twilight"
|
|
||||||
|
|
||||||
import ace from "ace-builds"
|
|
||||||
import "ace-builds/webpack-resolver"
|
|
||||||
import "ace-builds/src-noconflict/ext-language_tools"
|
|
||||||
import "ace-builds/src-noconflict/mode-graphqlschema"
|
|
||||||
import { defineGQLLanguageMode } from "~/helpers/syntax/gqlQueryLangMode"
|
|
||||||
|
|
||||||
import * as gql from "graphql"
|
|
||||||
import { getAutocompleteSuggestions } from "graphql-language-service-interface"
|
|
||||||
import debounce from "~/helpers/utils/debounce"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
onRunGQLQuery: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {},
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Object,
|
|
||||||
default: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
initialized: false,
|
|
||||||
editor: null,
|
|
||||||
cacheValue: "",
|
|
||||||
validationSchema: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value(value) {
|
|
||||||
if (value !== this.cacheValue) {
|
|
||||||
this.editor.session.setValue(value, 1)
|
|
||||||
this.cacheValue = value
|
|
||||||
}
|
|
||||||
},
|
|
||||||
theme() {
|
|
||||||
this.initialized = false
|
|
||||||
this.editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
|
||||||
this.$nextTick().then(() => {
|
|
||||||
this.initialized = true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
options(value) {
|
|
||||||
this.editor.setOptions(value)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
defineGQLLanguageMode(ace)
|
|
||||||
|
|
||||||
let langTools = ace.require("ace/ext/language_tools")
|
|
||||||
|
|
||||||
const editor = ace.edit(this.$refs.editor, {
|
|
||||||
mode: `ace/mode/gql-query`,
|
|
||||||
enableBasicAutocompletion: true,
|
|
||||||
enableLiveAutocompletion: true,
|
|
||||||
...this.options,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Set the theme and show the editor only after it's been set to prevent FOUC.
|
|
||||||
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
|
||||||
this.$nextTick().then(() => {
|
|
||||||
this.initialized = true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Set the theme and show the editor only after it's been set to prevent FOUC.
|
|
||||||
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
|
||||||
this.$nextTick().then(() => {
|
|
||||||
this.initialized = true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
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.commands.addCommand({
|
|
||||||
name: "runGQLQuery",
|
|
||||||
exec: () => this.onRunGQLQuery(this.editor.getValue()),
|
|
||||||
bindKey: {
|
|
||||||
mac: "cmd-enter",
|
|
||||||
win: "ctrl-enter",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.commands.addCommand({
|
|
||||||
name: "prettifyGQLQuery",
|
|
||||||
exec: () => this.prettifyQuery(),
|
|
||||||
bindKey: {
|
|
||||||
mac: "cmd-p",
|
|
||||||
win: "ctrl-p",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.on("change", () => {
|
|
||||||
const content = editor.getValue()
|
|
||||||
this.$emit("input", content)
|
|
||||||
this.parseContents(content)
|
|
||||||
this.cacheValue = content
|
|
||||||
})
|
|
||||||
|
|
||||||
this.parseContents(this.value)
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
prettifyQuery() {
|
|
||||||
try {
|
|
||||||
this.value = gql.print(gql.parse(this.editor.getValue()))
|
|
||||||
} catch (e) {
|
|
||||||
this.$toast.error(`${this.$t("gql_prettify_invalid_query")}`, {
|
|
||||||
icon: "error",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<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,174 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<p class="info">
|
|
||||||
{{ $t("donate_info1") }}
|
|
||||||
</p>
|
|
||||||
<p class="info">
|
|
||||||
{{ $t("donate_info2") }}
|
|
||||||
</p>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<span>
|
|
||||||
<a
|
|
||||||
href="https://github.com/sponsors/postwoman-io"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
v-tooltip.right="$t('recurring')"
|
|
||||||
>
|
|
||||||
<button class="icon">
|
|
||||||
<i class="material-icons">
|
|
||||||
<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>
|
|
||||||
</i>
|
|
||||||
<span>GitHub Sponsors</span>
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<span>
|
|
||||||
<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>
|
|
||||||
</span>
|
|
||||||
</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>
|
|
||||||
<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>
|
|
||||||
<hr />
|
|
||||||
<h3 class="title">Financial Contributors</h3>
|
|
||||||
<p class="info">
|
|
||||||
Become a financial contributor and help us sustain our community.
|
|
||||||
<a class="link" href="https://opencollective.com/postwoman/contribute">Contribute</a>.
|
|
||||||
</p>
|
|
||||||
<h3 class="title">Organizations</h3>
|
|
||||||
<p class="info">
|
|
||||||
Support this project with your organization. Your logo will show up here with a link to your
|
|
||||||
website.
|
|
||||||
<a class="link" href="https://opencollective.com/postwoman/contribute">Contribute</a>.
|
|
||||||
</p>
|
|
||||||
<div class="contributors">
|
|
||||||
<a href="https://tyk.io" target="_blank" rel="noopener">
|
|
||||||
<img
|
|
||||||
style="max-width: 320px;"
|
|
||||||
src="~assets/images/Tyk-side-project-logo-tagline-blk.png"
|
|
||||||
alt="Tyk Banner"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="contributors">
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/0/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/0/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/1/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/1/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/2/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/2/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/3/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/3/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/4/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/4/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/5/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/5/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/6/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/6/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/7/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/7/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/8/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/8/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman/organization/9/website">
|
|
||||||
<img src="https://opencollective.com/postwoman/organization/9/avatar.svg" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<h3 class="title">Individuals</h3>
|
|
||||||
<p class="info">
|
|
||||||
Support this project with your organization. Your logo will show up here with a link to your
|
|
||||||
website.
|
|
||||||
<a class="link" href="https://opencollective.com/postwoman/contribute">Contribute</a>.
|
|
||||||
</p>
|
|
||||||
<div class="contributors">
|
|
||||||
<a href="http://tom.preston-werner.com" target="_blank" rel="noopener">
|
|
||||||
<img
|
|
||||||
style="max-width: 64px; max-height: 64px; border-radius: 100%;"
|
|
||||||
src="https://github.com/mojombo.png?size=64"
|
|
||||||
alt="Tom Preston-Werner"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/postwoman">
|
|
||||||
<img src="https://opencollective.com/postwoman/individuals.svg" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<h3 class="title">Code Contributors</h3>
|
|
||||||
<p class="info">
|
|
||||||
This project exists thanks to all the people who contribute.
|
|
||||||
</p>
|
|
||||||
<div class="contributors">
|
|
||||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors">
|
|
||||||
<img src="https://opencollective.com/postwoman/contributors.svg" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.contributors {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
overflow: auto;
|
|
||||||
padding: 8px 0;
|
|
||||||
margin: 8px 0;
|
|
||||||
max-width: calc(100vw - 72px);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {}
|
|
||||||
</script>
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
<template>
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<span v-if="version.name" class="mono">
|
|
||||||
<a
|
|
||||||
class="footer-link"
|
|
||||||
:href="'https://github.com/liyasthomas/postwoman/releases/tag/' + version.name"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
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
|
|
||||||
: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>
|
|
||||||
<a href="https://liyasthomas.web.app" target="_blank" rel="noopener">
|
|
||||||
<button class="icon" v-tooltip="'Liyas Thomas'">
|
|
||||||
🦄
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
<a href="mailto:hello@postwoman.io" 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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.footer {
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-link {
|
|
||||||
margin: 8px 16px;
|
|
||||||
color: var(--fg-light-color);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--fg-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as version from "../../.postwoman/version.json"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
version: {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeMount() {
|
|
||||||
// Set version data
|
|
||||||
this.version = version.default
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
availableLocales() {
|
|
||||||
return this.$i18n.locales.filter(({ code }) => code !== this.$i18n.locale)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,420 +0,0 @@
|
|||||||
<template>
|
|
||||||
<header class="header">
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<span class="slide-in">
|
|
||||||
<nuxt-link :to="localePath('index')">
|
|
||||||
<h1 class="logo">Postwoman</h1>
|
|
||||||
</nuxt-link>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
id="installPWA"
|
|
||||||
@click.prevent="showInstallPrompt()"
|
|
||||||
v-tooltip="$t('install_pwa')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">offline_bolt</i>
|
|
||||||
</button>
|
|
||||||
<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" class="material-icons">
|
|
||||||
<path
|
|
||||||
d="M12 0C5.374 0 0 5.373 0 12c0 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.23A11.509 11.509 0 0112 5.803c1.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.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
<v-popover v-if="fb.currentUser === null">
|
|
||||||
<button class="icon" v-tooltip="$t('login_with')">
|
|
||||||
<i class="material-icons">login</i>
|
|
||||||
</button>
|
|
||||||
<template slot="popover">
|
|
||||||
<login />
|
|
||||||
</template>
|
|
||||||
</v-popover>
|
|
||||||
<v-popover v-else>
|
|
||||||
<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>
|
|
||||||
<logout />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</v-popover>
|
|
||||||
<v-popover>
|
|
||||||
<button class="icon" v-tooltip="$t('more')">
|
|
||||||
<i class="material-icons">drag_indicator</i>
|
|
||||||
</button>
|
|
||||||
<template slot="popover">
|
|
||||||
<button class="icon" @click="showExtensions = true" v-close-popover>
|
|
||||||
<i class="material-icons">extension</i>
|
|
||||||
<span>{{ $t("extensions") }}</span>
|
|
||||||
</button>
|
|
||||||
<button class="icon" @click="showShortcuts = true" v-close-popover>
|
|
||||||
<i class="material-icons">keyboard</i>
|
|
||||||
<span>{{ $t("shortcuts") }}</span>
|
|
||||||
</button>
|
|
||||||
<button class="icon" @click="showSupport = true" v-close-popover>
|
|
||||||
<i class="material-icons">favorite</i>
|
|
||||||
<span>{{ $t("support_us") }}</span>
|
|
||||||
</button>
|
|
||||||
<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">
|
|
||||||
<path
|
|
||||||
d="M24 4.557a9.83 9.83 0 01-2.828.775 4.932 4.932 0 002.165-2.724 9.864 9.864 0 01-3.127 1.195 4.916 4.916 0 00-3.594-1.555c-3.179 0-5.515 2.966-4.797 6.045A13.978 13.978 0 011.671 3.149a4.93 4.93 0 001.523 6.574 4.903 4.903 0 01-2.229-.616c-.054 2.281 1.581 4.415 3.949 4.89a4.935 4.935 0 01-2.224.084 4.928 4.928 0 004.6 3.419A9.9 9.9 0 010 19.54a13.94 13.94 0 007.548 2.212c9.142 0 14.307-7.721 13.995-14.646A10.025 10.025 0 0024 4.557z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<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>
|
|
||||||
<span>Share</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</v-popover>
|
|
||||||
</span>
|
|
||||||
</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">
|
|
||||||
<path
|
|
||||||
d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm8.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.181C6.36 3.312 9.027 2 12 2c5.912 0 8.263 4.283 8.003 6.657z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>Firefox</span>
|
|
||||||
<span class="icon" v-if="hasFirefoxExtInstalled" 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">
|
|
||||||
<path
|
|
||||||
d="M2.897 4.181A11.87 11.87 0 0111.969 0c4.288 0 8.535 2.273 10.717 6.554h-9.293c-1.674.001-2.755-.037-3.926.579-1.376.724-2.415 2.067-2.777 3.644L2.897 4.181zM8.007 12c0 2.2 1.789 3.99 3.988 3.99s3.988-1.79 3.988-3.99-1.789-3.991-3.988-3.991S8.007 9.8 8.007 12zm5.536 5.223c-2.238.666-4.858-.073-6.293-2.549-1.095-1.891-3.989-6.933-5.305-9.225A11.856 11.856 0 000 11.956c0 5.448 3.726 10.65 9.673 11.818l3.87-6.551zm2.158-9.214a5.463 5.463 0 011.007 6.719 1815.43 1815.43 0 01-5.46 9.248C18.437 24.419 24 18.616 24 12.004c0-1.313-.22-2.66-.69-3.995h-7.609z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>Chrome</span>
|
|
||||||
<span class="icon" v-if="hasChromeExtInstalled" 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") }}</h3>
|
|
||||||
<div>
|
|
||||||
<button class="icon" @click="showSupport = false">
|
|
||||||
<i class="material-icons">close</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div slot="body">
|
|
||||||
<contributors />
|
|
||||||
</div>
|
|
||||||
<div slot="footer"></div>
|
|
||||||
</modal>
|
|
||||||
</header>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
@keyframes slideIn {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
left: -16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
left: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-in {
|
|
||||||
position: relative;
|
|
||||||
animation: slideIn 0.2s forwards ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
font-size: 22px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--ac-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import intializePwa from "~/assets/js/pwa"
|
|
||||||
import {
|
|
||||||
hasExtensionInstalled,
|
|
||||||
hasChromeExtensionInstalled,
|
|
||||||
hasFirefoxExtensionInstalled,
|
|
||||||
} from "~/helpers/strategies/ExtensionStrategy"
|
|
||||||
import { getPlatformSpecialKey } from "~/helpers/platformutils"
|
|
||||||
import firebase from "firebase/app"
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
modal: () => import("../ui/modal"),
|
|
||||||
login: () => import("../firebase/login"),
|
|
||||||
logout: () => import("../firebase/logout"),
|
|
||||||
contributors: () => import("./contributors"),
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// Once the PWA code is initialized, this holds a method
|
|
||||||
// that can be called to show the user the installation
|
|
||||||
// prompt.
|
|
||||||
showInstallPrompt: null,
|
|
||||||
showExtensions: false,
|
|
||||||
hasChromeExtInstalled: hasChromeExtensionInstalled(),
|
|
||||||
hasFirefoxExtInstalled: hasFirefoxExtensionInstalled(),
|
|
||||||
showShortcuts: false,
|
|
||||||
showSupport: false,
|
|
||||||
fb,
|
|
||||||
navigatorShare: navigator.share,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
// Initializes the PWA code - checks if the app is installed,
|
|
||||||
// etc.
|
|
||||||
;(async () => {
|
|
||||||
this.showInstallPrompt = await intializePwa()
|
|
||||||
let cookiesAllowed = localStorage.getItem("cookiesAllowed") === "yes"
|
|
||||||
if (!cookiesAllowed) {
|
|
||||||
this.$toast.show(this.$t("we_use_cookies"), {
|
|
||||||
icon: "info",
|
|
||||||
duration: 5000,
|
|
||||||
theme: "toasted-primary",
|
|
||||||
action: [
|
|
||||||
{
|
|
||||||
text: this.$t("dismiss"),
|
|
||||||
onClick: (e, toastObject) => {
|
|
||||||
localStorage.setItem("cookiesAllowed", "yes")
|
|
||||||
toastObject.goAway(0)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// let showAd = localStorage.getItem("showAd") === "no"
|
|
||||||
// if (!showAd) {
|
|
||||||
// setTimeout(() => {
|
|
||||||
// this.$toast.clear()
|
|
||||||
// this.$toast.show(
|
|
||||||
// "<span>Get <u><a href='https://gum.co/keky' target='_blank' rel='noopener'>De-Coding The Passion Project</a></u> book, expertly crafted by the creator of Postwoman. Whoosh this away to dismiss →</span>",
|
|
||||||
// {
|
|
||||||
// icon: "",
|
|
||||||
// duration: 0,
|
|
||||||
// theme: "toasted-ad",
|
|
||||||
// action: [
|
|
||||||
// {
|
|
||||||
// text: "Get",
|
|
||||||
// icon: "chevron_right",
|
|
||||||
// onClick: (e, toastObject) => {
|
|
||||||
// localStorage.setItem("showAd", "no")
|
|
||||||
// toastObject.goAway(0)
|
|
||||||
// window.open("https://gum.co/keky")
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// onComplete() {
|
|
||||||
// localStorage.setItem("showAd", "no")
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }, 11000)
|
|
||||||
// }
|
|
||||||
|
|
||||||
let showExtensionsToast = localStorage.getItem("showExtensionsToast") === "yes"
|
|
||||||
|
|
||||||
// Just return if showExtensionsToast is "no"
|
|
||||||
if (!showExtensionsToast) return
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!hasExtensionInstalled()) {
|
|
||||||
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) => {
|
|
||||||
this.$store.commit("setMiscState", {
|
|
||||||
value: false,
|
|
||||||
attribute: "showExtensionsToast",
|
|
||||||
})
|
|
||||||
localStorage.setItem("showExtensionsToast", "no")
|
|
||||||
toastObject.goAway(0)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, 5000)
|
|
||||||
|
|
||||||
this._keyListener = function (e) {
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
e.preventDefault()
|
|
||||||
this.showExtensions = this.showShortcuts = this.showSupport = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener("keydown", this._keyListener.bind(this))
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
getSpecialKey: getPlatformSpecialKey,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
availableLocales() {
|
|
||||||
return this.$i18n.locales.filter((i) => i.code !== this.$i18n.locale)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,473 +0,0 @@
|
|||||||
<template>
|
|
||||||
<pw-section class="green" icon="history" :label="$t('history')" ref="history">
|
|
||||||
<div class="show-on-large-screen">
|
|
||||||
<input aria-label="Search" type="search" :placeholder="$t('search')" v-model="filterText" />
|
|
||||||
<button class="icon">
|
|
||||||
<i class="material-icons">search</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="virtual-list" :class="{ filled: filteredHistory.length }">
|
|
||||||
<ul v-for="(entry, index) in filteredHistory" :key="index" class="entry">
|
|
||||||
<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">
|
|
||||||
{{ entry.star ? "star" : "star_border" }}
|
|
||||||
</i>
|
|
||||||
</button>
|
|
||||||
<li>
|
|
||||||
<input
|
|
||||||
:aria-label="$t('label')"
|
|
||||||
type="text"
|
|
||||||
readonly
|
|
||||||
:value="entry.label"
|
|
||||||
:placeholder="$t('no_label')"
|
|
||||||
class="bg-color"
|
|
||||||
/>
|
|
||||||
</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="$t('method')"
|
|
||||||
type="text"
|
|
||||||
readonly
|
|
||||||
:value="entry.method"
|
|
||||||
:class="findEntryStatus(entry).className"
|
|
||||||
:style="{ '--status-code': entry.status }"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="entry-status-code"
|
|
||||||
:class="findEntryStatus(entry).className"
|
|
||||||
:style="{ '--status-code': entry.status }"
|
|
||||||
>{{ entry.status }}</span
|
|
||||||
>
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
<ul :class="{ hidden: filteredHistory.length != 0 || history.length === 0 }">
|
|
||||||
<li>
|
|
||||||
<label>{{ $t("nothing_found") }} "{{ filterText }}"</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p v-if="history.length === 0" class="info">
|
|
||||||
<i class="material-icons">schedule</i> {{ $t("history_empty") }}
|
|
||||||
</p>
|
|
||||||
<div v-if="history.length !== 0">
|
|
||||||
<div class="flex-wrap" v-if="!isClearingHistory">
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
id="clear-history-button"
|
|
||||||
:disabled="history.length === 0"
|
|
||||||
@click="enableHistoryClearing"
|
|
||||||
>
|
|
||||||
<i class="material-icons">clear_all</i>
|
|
||||||
<span>{{ $t("clear_all") }}</span>
|
|
||||||
</button>
|
|
||||||
<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" @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">
|
|
||||||
<i class="material-icons">help_outline</i> {{ $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>
|
|
||||||
</pw-section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.virtual-list {
|
|
||||||
max-height: calc(100vh - 290px);
|
|
||||||
|
|
||||||
[readonly] {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active,
|
|
||||||
.fade-leave-active {
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter,
|
|
||||||
.fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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: 320px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.labels {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { findStatusGroup } from "~/pages/index"
|
|
||||||
import { fb } from "~/helpers/fb"
|
|
||||||
|
|
||||||
const updateOnLocalStorage = (propertyName, property) =>
|
|
||||||
window.localStorage.setItem(propertyName, JSON.stringify(property))
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
"pw-section": () => import("../layout/section"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
history:
|
|
||||||
fb.currentUser !== null
|
|
||||||
? fb.currentHistory
|
|
||||||
: JSON.parse(window.localStorage.getItem("history")) || [],
|
|
||||||
filterText: "",
|
|
||||||
showFilter: false,
|
|
||||||
isClearingHistory: false,
|
|
||||||
reverse_sort_label: false,
|
|
||||||
reverse_sort_time: false,
|
|
||||||
reverse_sort_status_code: false,
|
|
||||||
reverse_sort_url: 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) => {
|
|
||||||
let value = entry[key]
|
|
||||||
value = typeof value !== "string" ? value.toString() : value
|
|
||||||
return value.toLowerCase().includes(filterText)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
clearHistory() {
|
|
||||||
if (fb.currentUser !== null) {
|
|
||||||
fb.clearHistory()
|
|
||||||
}
|
|
||||||
this.history = []
|
|
||||||
this.filterText = ""
|
|
||||||
this.disableHistoryClearing()
|
|
||||||
updateOnLocalStorage("history", this.history)
|
|
||||||
this.$toast.error(this.$t("history_deleted"), {
|
|
||||||
icon: "delete",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
useHistory(entry) {
|
|
||||||
this.$emit("useHistory", entry)
|
|
||||||
},
|
|
||||||
findEntryStatus(entry) {
|
|
||||||
const foundStatusGroup = findStatusGroup(entry.status)
|
|
||||||
return (
|
|
||||||
foundStatusGroup || {
|
|
||||||
className: "",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
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(this.$t("deleted"), {
|
|
||||||
icon: "delete",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
addEntry(entry) {
|
|
||||||
this.history.push(entry)
|
|
||||||
updateOnLocalStorage("history", this.history)
|
|
||||||
},
|
|
||||||
enableHistoryClearing() {
|
|
||||||
if (!this.history || !this.history.length) return
|
|
||||||
this.isClearingHistory = true
|
|
||||||
},
|
|
||||||
disableHistoryClearing() {
|
|
||||||
this.isClearingHistory = false
|
|
||||||
},
|
|
||||||
sort_by_time() {
|
|
||||||
let byDate = this.history.slice(0)
|
|
||||||
byDate.sort((a, b) => {
|
|
||||||
let date_a = a.date.split("/")
|
|
||||||
let date_b = b.date.split("/")
|
|
||||||
let time_a = a.time.split(":")
|
|
||||||
let time_b = b.time.split(":")
|
|
||||||
let final_a = new Date(date_a[2], date_a[1], date_a[0], time_a[0], time_a[1], time_a[2])
|
|
||||||
let final_b = new Date(date_b[2], date_b[1], date_b[0], time_b[0], time_b[1], time_b[2])
|
|
||||||
if (this.reverse_sort_time) return final_b - final_a
|
|
||||||
else return final_a - final_b
|
|
||||||
})
|
|
||||||
this.history = byDate
|
|
||||||
this.reverse_sort_time = !this.reverse_sort_time
|
|
||||||
},
|
|
||||||
sort_by_status_code() {
|
|
||||||
let byCode = this.history.slice(0)
|
|
||||||
byCode.sort((a, b) => {
|
|
||||||
if (this.reverse_sort_status_code) return b.status - a.status
|
|
||||||
else return a.status - b.status
|
|
||||||
})
|
|
||||||
this.history = byCode
|
|
||||||
this.reverse_sort_status_code = !this.reverse_sort_status_code
|
|
||||||
},
|
|
||||||
sort_by_url() {
|
|
||||||
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
|
|
||||||
})
|
|
||||||
this.history = byUrl
|
|
||||||
this.reverse_sort_url = !this.reverse_sort_url
|
|
||||||
},
|
|
||||||
sort_by_label() {
|
|
||||||
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
|
|
||||||
})
|
|
||||||
this.history = byLabel
|
|
||||||
this.reverse_sort_label = !this.reverse_sort_label
|
|
||||||
},
|
|
||||||
sort_by_path() {
|
|
||||||
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
|
|
||||||
})
|
|
||||||
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>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg viewBox="0 0 612.001 612.001">
|
|
||||||
<path
|
|
||||||
:fill="color"
|
|
||||||
data-old_color="#202124"
|
|
||||||
class="active-path"
|
|
||||||
data-original="#202124"
|
|
||||||
d="M64.601 236.822C64.601 394.256 192.786 612 306.001 612 412.582 612 547.4 394.256 547.4 236.822S439.322 0 306 0 64.601 79.388 64.601 236.822zm304.12 116.415c29.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.476-70.598 40.196-108.552 32.174-8.022-37.955 2.698-79.078 32.173-108.552zm-233.994-32.174c37.954-8.02 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.552z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
<template>
|
|
||||||
<fieldset :id="label.toLowerCase()" :class="{ 'no-colored-frames': !frameColorsEnabled }">
|
|
||||||
<legend @click.prevent="collapse">
|
|
||||||
<span>{{ label }}</span>
|
|
||||||
<i class="material-icons">
|
|
||||||
{{ isCollapsed(label) ? "expand_more" : "expand_less" }}
|
|
||||||
</i>
|
|
||||||
</legend>
|
|
||||||
<div class="collapsible" :class="{ hidden: isCollapsed(label.toLowerCase()) }">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
fieldset {
|
|
||||||
margin: 16px 0;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: var(--bg-dark-color);
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
|
|
||||||
legend {
|
|
||||||
display: inline-block;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: var(--fg-color);
|
|
||||||
font-weight: 700;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
|
|
||||||
* {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.blue legend {
|
|
||||||
color: #57b5f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.gray legend {
|
|
||||||
color: #bcc2cd;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.green legend {
|
|
||||||
color: #50fa7b;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.cyan legend {
|
|
||||||
color: #8be9fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.purple legend {
|
|
||||||
color: #bd93f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.orange legend {
|
|
||||||
color: #ffb86c;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.pink legend {
|
|
||||||
color: #ff79c6;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.red legend {
|
|
||||||
color: #ff5555;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.yellow legend {
|
|
||||||
color: #f1fa8c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset.no-colored-frames legend {
|
|
||||||
color: var(--fg-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
frameColorsEnabled() {
|
|
||||||
return this.$store.state.postwoman.settings.FRAME_COLORS_ENABLED || false
|
|
||||||
},
|
|
||||||
sectionString() {
|
|
||||||
return `${this.$route.path.replace(/\/+$/, "")}/${this.label}`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
default: "Section",
|
|
||||||
},
|
|
||||||
collapsed: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
collapse({ target }) {
|
|
||||||
const parent = target.parentNode.parentNode
|
|
||||||
parent.querySelector(".collapsible").classList.toggle("hidden")
|
|
||||||
|
|
||||||
// Save collapsed section into the collapsedSections array
|
|
||||||
this.$store.commit("setCollapsedSection", this.sectionString)
|
|
||||||
},
|
|
||||||
isCollapsed(label) {
|
|
||||||
return this.$store.state.theme.collapsedSections.includes(this.sectionString) || false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
<template>
|
|
||||||
<aside class="nav-first">
|
|
||||||
<nav class="primary-nav">
|
|
||||||
<!--
|
|
||||||
We're using manual checks for linkActive because the query string
|
|
||||||
seems to mess up the nuxt-link active class.
|
|
||||||
-->
|
|
||||||
<nuxt-link
|
|
||||||
:to="localePath('index')"
|
|
||||||
:class="linkActive('/')"
|
|
||||||
v-tooltip.right="$t('home')"
|
|
||||||
:aria-label="$t('home')"
|
|
||||||
>
|
|
||||||
<logo alt class="material-icons" style="height: 24px;"></logo>
|
|
||||||
</nuxt-link>
|
|
||||||
<nuxt-link
|
|
||||||
:to="localePath('realtime')"
|
|
||||||
:class="linkActive('/realtime')"
|
|
||||||
v-tooltip.right="$t('realtime')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">language</i>
|
|
||||||
</nuxt-link>
|
|
||||||
<nuxt-link
|
|
||||||
:to="localePath('graphql')"
|
|
||||||
:class="linkActive('/graphql')"
|
|
||||||
v-tooltip.right="$t('graphql')"
|
|
||||||
:aria-label="$t('graphql')"
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 29.999 30">
|
|
||||||
<path d="M4.08 22.864l-1.1-.636L15.248.98l1.1.636z" />
|
|
||||||
<path d="M2.727 20.53h24.538v1.272H2.727z" />
|
|
||||||
<path
|
|
||||||
d="M15.486 28.332L3.213 21.246l.636-1.1 12.273 7.086zm10.662-18.47L13.874 2.777l.636-1.1 12.273 7.086z"
|
|
||||||
/>
|
|
||||||
<path d="M3.852 9.858l-.636-1.1L15.5 1.67l.636 1.1z" />
|
|
||||||
<path
|
|
||||||
d="M25.922 22.864l-12.27-21.25 1.1-.636 12.27 21.25zM3.7 7.914h1.272v14.172H3.7zm21.328 0H26.3v14.172h-1.272z"
|
|
||||||
/>
|
|
||||||
<path d="M15.27 27.793l-.555-.962 10.675-6.163.555.962z" />
|
|
||||||
<path
|
|
||||||
d="M27.985 22.5a2.68 2.68 0 01-3.654.981 2.68 2.68 0 01-.981-3.654 2.68 2.68 0 013.654-.981 2.665 2.665 0 01.98 3.654M6.642 10.174a2.68 2.68 0 01-3.654.981A2.68 2.68 0 012.007 7.5a2.68 2.68 0 013.654-.981 2.68 2.68 0 01.981 3.654M2.015 22.5a2.68 2.68 0 01.981-3.654 2.68 2.68 0 013.654.981 2.68 2.68 0 01-.981 3.654c-1.287.735-2.92.3-3.654-.98m21.343-12.326a2.68 2.68 0 01.981-3.654 2.68 2.68 0 013.654.981 2.68 2.68 0 01-.981 3.654 2.68 2.68 0 01-3.654-.981M15 30a2.674 2.674 0 112.674-2.673A2.68 2.68 0 0115 30m0-24.652a2.67 2.67 0 01-2.674-2.674 2.67 2.67 0 115.347 0A2.67 2.67 0 0115 5.347"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</nuxt-link>
|
|
||||||
<nuxt-link
|
|
||||||
:to="localePath('doc')"
|
|
||||||
:class="linkActive('/doc')"
|
|
||||||
v-tooltip.right="$t('documentation')"
|
|
||||||
:aria-label="$t('documentation')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">topic</i>
|
|
||||||
</nuxt-link>
|
|
||||||
<nuxt-link
|
|
||||||
:to="localePath('settings')"
|
|
||||||
:class="linkActive('/settings')"
|
|
||||||
v-tooltip.right="$t('settings')"
|
|
||||||
:aria-label="$t('settings')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">settings</i>
|
|
||||||
</nuxt-link>
|
|
||||||
</nav>
|
|
||||||
<div v-if="$route.path == '/'">
|
|
||||||
<nav class="secondary-nav">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="#request" v-tooltip.right="$t('request')">
|
|
||||||
<i class="material-icons">cloud_upload</i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#options" v-tooltip.right="$t('options')">
|
|
||||||
<i class="material-icons">toc</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.includes('/realtime')">
|
|
||||||
<nav class="secondary-nav">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="#request" v-tooltip.right="$t('request')">
|
|
||||||
<i class="material-icons">cloud_upload</i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#response" v-tooltip.right="$t('communication')">
|
|
||||||
<i class="material-icons">cloud_download</i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="$route.path.includes('/graphql')">
|
|
||||||
<nav class="secondary-nav">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<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.includes('/doc')">
|
|
||||||
<nav class="secondary-nav">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="#import" v-tooltip.right="$t('import')">
|
|
||||||
<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.includes('/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="#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>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
$responsiveWidth: 768px;
|
|
||||||
|
|
||||||
.nav-first {
|
|
||||||
z-index: 1;
|
|
||||||
height: 100vh;
|
|
||||||
padding: 0 8px;
|
|
||||||
background-color: var(--bg-dark-color);
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.primary-nav {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: var(--fg-light-color);
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding: 14px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: var(--bg-light-color);
|
|
||||||
color: var(--fg-light-color);
|
|
||||||
fill: var(--fg-light-color);
|
|
||||||
margin: 8px 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--fg-color);
|
|
||||||
fill: var(--fg-color);
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: var(--fg-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.nuxt-link-exact-active {
|
|
||||||
background-color: var(--ac-color);
|
|
||||||
color: var(--act-color);
|
|
||||||
fill: var(--act-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: var(--act-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.secondary-nav {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-top: 2px dashed var(--brd-color);
|
|
||||||
margin-top: 4px;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding: 14px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: var(--bg-dark-color);
|
|
||||||
color: var(--fg-light-color);
|
|
||||||
fill: var(--fg-light-color);
|
|
||||||
margin: 8px 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--fg-color);
|
|
||||||
fill: var(--fg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.current {
|
|
||||||
color: var(--ac-color);
|
|
||||||
fill: var(--ac-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $responsiveWidth) {
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
logo: () => import("./logo"),
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
linkActive(path) {
|
|
||||||
return {
|
|
||||||
"nuxt-link-exact-active": this.$route.path === path,
|
|
||||||
"nuxt-link-active": this.$route.path === path,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
window.addEventListener("scroll", (event) => {
|
|
||||||
let mainNavLinks = document.querySelectorAll("nav ul li a")
|
|
||||||
let fromTop = window.scrollY
|
|
||||||
mainNavLinks.forEach(({ hash, classList }) => {
|
|
||||||
let section = document.querySelector(hash)
|
|
||||||
|
|
||||||
if (
|
|
||||||
section &&
|
|
||||||
section.offsetTop <= fromTop &&
|
|
||||||
section.offsetTop + section.offsetHeight > fromTop
|
|
||||||
) {
|
|
||||||
classList.add("current")
|
|
||||||
} else {
|
|
||||||
classList.remove("current")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
$route() {
|
|
||||||
// this.$toast.clear();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<tabs>
|
|
||||||
<tab
|
|
||||||
v-for="(lens, index) in validLenses"
|
|
||||||
:key="lens.lensName"
|
|
||||||
:id="lens.lensName"
|
|
||||||
:label="lens.lensName"
|
|
||||||
:selected="index === 0"
|
|
||||||
>
|
|
||||||
<component :is="lens.renderer" :response="response" />
|
|
||||||
</tab>
|
|
||||||
<tab
|
|
||||||
v-if="Object.keys(response.headers).length !== 0"
|
|
||||||
id="headers"
|
|
||||||
:label="`Headers \xA0 • \xA0 ${Object.keys(response.headers).length}`"
|
|
||||||
>
|
|
||||||
<headers :headers="response.headers" />
|
|
||||||
</tab>
|
|
||||||
</tabs>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { getSuitableLenses, getLensRenderers } from "~/helpers/lenses/lenses"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
tabs: () => import("../ui/tabs"),
|
|
||||||
tab: () => import("../ui/tab"),
|
|
||||||
headers: () => import("./headers"),
|
|
||||||
// Lens Renderers
|
|
||||||
...getLensRenderers(),
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
validLenses() {
|
|
||||||
return getSuitableLenses(this.response)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<ul v-for="(value, key) in headers" :key="key">
|
|
||||||
<li>
|
|
||||||
<input :value="`${key} → ${value}`" :name="key" class="bg-color" readonly />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
headers: {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<label for="body">{{ $t("response") }}</label>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="ToggleExpandResponse"
|
|
||||||
ref="ToggleExpandResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
v-tooltip="{
|
|
||||||
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<i class="material-icons">
|
|
||||||
{{ !expandResponse ? "unfold_more" : "unfold_less" }}
|
|
||||||
</i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="response.body"
|
|
||||||
class="icon"
|
|
||||||
@click.prevent="togglePreview"
|
|
||||||
v-tooltip="{
|
|
||||||
content: previewEnabled ? $t('hide_preview') : $t('preview_html'),
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<i class="material-icons">
|
|
||||||
{{ !previewEnabled ? "visibility" : "visibility_off" }}
|
|
||||||
</i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="downloadResponse"
|
|
||||||
ref="downloadResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
v-tooltip="$t('download_file')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">save_alt</i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="copyResponse"
|
|
||||||
ref="copyResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
v-tooltip="$t('copy_response')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">content_copy</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="response-details-wrapper">
|
|
||||||
<Editor
|
|
||||||
:value="responseBodyText"
|
|
||||||
:lang="'html'"
|
|
||||||
:options="{
|
|
||||||
maxLines: responseBodyMaxLines,
|
|
||||||
minLines: '16',
|
|
||||||
fontSize: '16px',
|
|
||||||
autoScrollEditorIntoView: true,
|
|
||||||
readOnly: true,
|
|
||||||
showPrintMargin: false,
|
|
||||||
useWorker: false,
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
<iframe
|
|
||||||
:class="{ hidden: !previewEnabled }"
|
|
||||||
class="covers-response"
|
|
||||||
ref="previewFrame"
|
|
||||||
src="about:blank"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import AceEditor from "../../ui/ace-editor"
|
|
||||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Editor: AceEditor,
|
|
||||||
},
|
|
||||||
mixins: [TextContentRendererMixin],
|
|
||||||
props: {
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
expandResponse: false,
|
|
||||||
responseBodyMaxLines: 16,
|
|
||||||
doneButton: '<i class="material-icons">done</i>',
|
|
||||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
|
||||||
copyButton: '<i class="material-icons">content_copy</i>',
|
|
||||||
previewEnabled: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
ToggleExpandResponse() {
|
|
||||||
this.expandResponse = !this.expandResponse
|
|
||||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
|
||||||
},
|
|
||||||
downloadResponse() {
|
|
||||||
const dataToWrite = this.responseBodyText
|
|
||||||
const file = new Blob([dataToWrite], { type: "text/html" })
|
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
a.href = url
|
|
||||||
// TODO get uri from meta
|
|
||||||
a.download = `response on ${Date()}`.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)
|
|
||||||
},
|
|
||||||
copyResponse() {
|
|
||||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
|
||||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
|
||||||
icon: "done",
|
|
||||||
})
|
|
||||||
const aux = document.createElement("textarea")
|
|
||||||
const copy = this.responseBodyText
|
|
||||||
aux.innerText = copy
|
|
||||||
document.body.appendChild(aux)
|
|
||||||
aux.select()
|
|
||||||
document.execCommand("copy")
|
|
||||||
document.body.removeChild(aux)
|
|
||||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
|
||||||
},
|
|
||||||
togglePreview() {
|
|
||||||
this.previewEnabled = !this.previewEnabled
|
|
||||||
if (this.previewEnabled) {
|
|
||||||
if (this.$refs.previewFrame.getAttribute("data-previewing-url") === this.url) return
|
|
||||||
// Use DOMParser to parse document HTML.
|
|
||||||
const previewDocument = new DOMParser().parseFromString(this.responseBodyText, "text/html")
|
|
||||||
// Inject <base href="..."> tag to head, to fix relative CSS/HTML paths.
|
|
||||||
previewDocument.head.innerHTML =
|
|
||||||
`<base href="${this.url}">` + previewDocument.head.innerHTML
|
|
||||||
// Finally, set the iframe source to the resulting HTML.
|
|
||||||
this.$refs.previewFrame.srcdoc = previewDocument.documentElement.outerHTML
|
|
||||||
this.$refs.previewFrame.setAttribute("data-previewing-url", this.url)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<label for="body">{{ $t("response") }}</label>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="downloadResponse"
|
|
||||||
ref="downloadResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
v-tooltip="$t('download_file')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">save_alt</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="response-details-wrapper">
|
|
||||||
<img class="response-image" :src="imageSource" />
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.response-image {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
imageSource: "",
|
|
||||||
doneButton: '<i class="material-icons">done</i>',
|
|
||||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
responseType() {
|
|
||||||
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
response: {
|
|
||||||
immediate: true,
|
|
||||||
handler(newValue) {
|
|
||||||
this.imageSource = ""
|
|
||||||
|
|
||||||
const buf = this.response.body
|
|
||||||
const bytes = new Uint8Array(buf)
|
|
||||||
const blob = new Blob([bytes.buffer])
|
|
||||||
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.onload = (e) => {
|
|
||||||
this.imageSource = e.target.result
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(blob)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.imageSource = ""
|
|
||||||
|
|
||||||
const buf = this.response.body
|
|
||||||
const bytes = new Uint8Array(buf)
|
|
||||||
const blob = new Blob([bytes.buffer])
|
|
||||||
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.onload = (e) => {
|
|
||||||
this.imageSource = e.target.result
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(blob)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
downloadResponse() {
|
|
||||||
const dataToWrite = this.response.body
|
|
||||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
a.href = url
|
|
||||||
// TODO get uri from meta
|
|
||||||
a.download = `response on ${Date()}`.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)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<label for="body">{{ $t("response") }}</label>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="ToggleExpandResponse"
|
|
||||||
ref="ToggleExpandResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
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-if="response.body && canDownloadResponse"
|
|
||||||
v-tooltip="$t('download_file')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">save_alt</i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="copyResponse"
|
|
||||||
ref="copyResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
v-tooltip="$t('copy_response')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">content_copy</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="valid-warning" v-if="jsonInvalid">
|
|
||||||
<p class="info"><i class="material-icons">error_outline</i> Invalid JSON</p>
|
|
||||||
</div>
|
|
||||||
<div id="response-details-wrapper">
|
|
||||||
<Editor
|
|
||||||
:value="jsonBodyText"
|
|
||||||
:lang="'json'"
|
|
||||||
:options="{
|
|
||||||
maxLines: responseBodyMaxLines,
|
|
||||||
minLines: '16',
|
|
||||||
fontSize: '16px',
|
|
||||||
autoScrollEditorIntoView: true,
|
|
||||||
readOnly: true,
|
|
||||||
showPrintMargin: false,
|
|
||||||
useWorker: false,
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import AceEditor from "../../ui/ace-editor"
|
|
||||||
import { isJSONContentType } from "~/helpers/utils/contenttypes"
|
|
||||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Editor: AceEditor,
|
|
||||||
},
|
|
||||||
mixins: [TextContentRendererMixin],
|
|
||||||
props: {
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
expandResponse: false,
|
|
||||||
jsonInvalid: false,
|
|
||||||
responseBodyMaxLines: 16,
|
|
||||||
doneButton: '<i class="material-icons">done</i>',
|
|
||||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
|
||||||
copyButton: '<i class="material-icons">content_copy</i>',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
jsonBodyText() {
|
|
||||||
try {
|
|
||||||
this.jsonInvalid = false
|
|
||||||
return JSON.stringify(JSON.parse(this.responseBodyText), null, 2)
|
|
||||||
} catch (e) {
|
|
||||||
// Most probs invalid JSON was returned, so drop prettification (should we warn ?)
|
|
||||||
this.jsonInvalid = true
|
|
||||||
return this.responseBodyText
|
|
||||||
}
|
|
||||||
},
|
|
||||||
responseType() {
|
|
||||||
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase()
|
|
||||||
},
|
|
||||||
canDownloadResponse() {
|
|
||||||
return (
|
|
||||||
this.response &&
|
|
||||||
this.response.headers &&
|
|
||||||
this.response.headers["content-type"] &&
|
|
||||||
isJSONContentType(this.response.headers["content-type"])
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
ToggleExpandResponse() {
|
|
||||||
this.expandResponse = !this.expandResponse
|
|
||||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
|
||||||
},
|
|
||||||
downloadResponse() {
|
|
||||||
const dataToWrite = this.responseBodyText
|
|
||||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
a.href = url
|
|
||||||
// TODO get uri from meta
|
|
||||||
a.download = `response on ${Date()}`.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)
|
|
||||||
},
|
|
||||||
copyResponse() {
|
|
||||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
|
||||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
|
||||||
icon: "done",
|
|
||||||
})
|
|
||||||
const aux = document.createElement("textarea")
|
|
||||||
const copy = this.responseBodyText
|
|
||||||
aux.innerText = copy
|
|
||||||
document.body.appendChild(aux)
|
|
||||||
aux.select()
|
|
||||||
document.execCommand("copy")
|
|
||||||
document.body.removeChild(aux)
|
|
||||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<label for="body">{{ $t("response") }}</label>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="ToggleExpandResponse"
|
|
||||||
ref="ToggleExpandResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
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-if="response.body && canDownloadResponse"
|
|
||||||
v-tooltip="$t('download_file')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">save_alt</i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="copyResponse"
|
|
||||||
ref="copyResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
v-tooltip="$t('copy_response')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">content_copy</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="response-details-wrapper">
|
|
||||||
<Editor
|
|
||||||
:value="responseBodyText"
|
|
||||||
:lang="'plain_text'"
|
|
||||||
:options="{
|
|
||||||
maxLines: responseBodyMaxLines,
|
|
||||||
minLines: '16',
|
|
||||||
fontSize: '16px',
|
|
||||||
autoScrollEditorIntoView: true,
|
|
||||||
readOnly: true,
|
|
||||||
showPrintMargin: false,
|
|
||||||
useWorker: false,
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import AceEditor from "../../ui/ace-editor"
|
|
||||||
import { isJSONContentType } from "~/helpers/utils/contenttypes"
|
|
||||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Editor: AceEditor,
|
|
||||||
},
|
|
||||||
mixins: [TextContentRendererMixin],
|
|
||||||
props: {
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
expandResponse: false,
|
|
||||||
responseBodyMaxLines: 16,
|
|
||||||
doneButton: '<i class="material-icons">done</i>',
|
|
||||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
|
||||||
copyButton: '<i class="material-icons">content_copy</i>',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
responseType() {
|
|
||||||
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase()
|
|
||||||
},
|
|
||||||
canDownloadResponse() {
|
|
||||||
return (
|
|
||||||
this.response &&
|
|
||||||
this.response.headers &&
|
|
||||||
this.response.headers["content-type"] &&
|
|
||||||
isJSONContentType(this.response.headers["content-type"])
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
ToggleExpandResponse() {
|
|
||||||
this.expandResponse = !this.expandResponse
|
|
||||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
|
||||||
},
|
|
||||||
downloadResponse() {
|
|
||||||
const dataToWrite = this.responseBodyText
|
|
||||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
a.href = url
|
|
||||||
// TODO get uri from meta
|
|
||||||
a.download = `response on ${Date()}`.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)
|
|
||||||
},
|
|
||||||
copyResponse() {
|
|
||||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
|
||||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
|
||||||
icon: "done",
|
|
||||||
})
|
|
||||||
const aux = document.createElement("textarea")
|
|
||||||
const copy = this.responseBodyText
|
|
||||||
aux.innerText = copy
|
|
||||||
document.body.appendChild(aux)
|
|
||||||
aux.select()
|
|
||||||
document.execCommand("copy")
|
|
||||||
document.body.removeChild(aux)
|
|
||||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<div class="flex-wrap">
|
|
||||||
<label for="body">{{ $t("response") }}</label>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="ToggleExpandResponse"
|
|
||||||
ref="ToggleExpandResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
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-if="response.body"
|
|
||||||
v-tooltip="$t('download_file')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">save_alt</i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="icon"
|
|
||||||
@click="copyResponse"
|
|
||||||
ref="copyResponse"
|
|
||||||
v-if="response.body"
|
|
||||||
v-tooltip="$t('copy_response')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">content_copy</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="response-details-wrapper">
|
|
||||||
<Editor
|
|
||||||
:value="responseBodyText"
|
|
||||||
:lang="'xml'"
|
|
||||||
:options="{
|
|
||||||
maxLines: responseBodyMaxLines,
|
|
||||||
minLines: '16',
|
|
||||||
fontSize: '16px',
|
|
||||||
autoScrollEditorIntoView: true,
|
|
||||||
readOnly: true,
|
|
||||||
showPrintMargin: false,
|
|
||||||
useWorker: false,
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import AceEditor from "../../ui/ace-editor"
|
|
||||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Editor: AceEditor,
|
|
||||||
},
|
|
||||||
mixins: [TextContentRendererMixin],
|
|
||||||
props: {
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
expandResponse: false,
|
|
||||||
responseBodyMaxLines: 16,
|
|
||||||
doneButton: '<i class="material-icons">done</i>',
|
|
||||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
|
||||||
copyButton: '<i class="material-icons">content_copy</i>',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
responseType() {
|
|
||||||
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
ToggleExpandResponse() {
|
|
||||||
this.expandResponse = !this.expandResponse
|
|
||||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
|
||||||
},
|
|
||||||
downloadResponse() {
|
|
||||||
const dataToWrite = this.responseBodyText
|
|
||||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
a.href = url
|
|
||||||
// TODO get uri from meta
|
|
||||||
a.download = `response on ${Date()}`.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)
|
|
||||||
},
|
|
||||||
copyResponse() {
|
|
||||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
|
||||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
|
||||||
icon: "done",
|
|
||||||
})
|
|
||||||
const aux = document.createElement("textarea")
|
|
||||||
const copy = this.responseBodyText
|
|
||||||
aux.innerText = copy
|
|
||||||
document.body.appendChild(aux)
|
|
||||||
aux.select()
|
|
||||||
document.execCommand("copy")
|
|
||||||
document.body.removeChild(aux)
|
|
||||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
export default {
|
|
||||||
props: {
|
|
||||||
response: {},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
responseBodyText() {
|
|
||||||
if (typeof this.response.body === "string") return this.response.body
|
|
||||||
return new TextDecoder("utf-8").decode(this.response.body)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<label for="log">{{ title }}</label>
|
|
||||||
<div name="log" class="realtime-log" ref="log">
|
|
||||||
<span v-if="log">
|
|
||||||
<span v-for="(logEntry, index) in 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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
div.realtime-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;
|
|
||||||
user-select: text;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: block;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { getSourcePrefix } from "~/helpers/utils/string"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: ["log", "title"],
|
|
||||||
methods: {
|
|
||||||
getSourcePrefix,
|
|
||||||
},
|
|
||||||
updated: function () {
|
|
||||||
this.$nextTick(function () {
|
|
||||||
if (this.$refs.log) {
|
|
||||||
this.$refs.log.scrollBy(0, this.$refs.log.scrollHeight + 100)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<pw-section class="blue" :label="$t('request')">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="mqtt-url">{{ $t("url") }}</label>
|
|
||||||
<input id="mqtt-url" type="url" v-model="url" spellcheck="false" />
|
|
||||||
</li>
|
|
||||||
<div>
|
|
||||||
<li>
|
|
||||||
<label for="connect" class="hide-on-small-screen"> </label>
|
|
||||||
<button id="connect" :disabled="!validUrl" @click="toggleConnection">
|
|
||||||
{{ this.connectionState ? $t("disconnect") : $t("connect") }}
|
|
||||||
<span>
|
|
||||||
<i class="material-icons">{{ !connectionState ? "sync" : "sync_disabled" }}</i>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
</ul>
|
|
||||||
</pw-section>
|
|
||||||
|
|
||||||
<pw-section class="blue" :label="$t('communication')">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<realtime-log :title="$t('log')" :log="this.log" />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="pub_topic">{{ $t("mqtt_topic") }}</label>
|
|
||||||
<input id="pub_topic" type="text" v-model="pub_topic" spellcheck="false" />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label for="mqtt-message">{{ $t("message") }}</label>
|
|
||||||
<input id="mqtt-message" type="text" v-model="msg" spellcheck="false" />
|
|
||||||
</li>
|
|
||||||
<div>
|
|
||||||
<li>
|
|
||||||
<label for="publish" class="hide-on-small-screen"> </label>
|
|
||||||
<button id="publish" name="get" :disabled="!canpublish" @click="publish">
|
|
||||||
{{ $t("mqtt_publish") }}
|
|
||||||
<span>
|
|
||||||
<i class="material-icons">send</i>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="sub_topic">{{ $t("mqtt_topic") }}</label>
|
|
||||||
<input id="sub_topic" type="text" v-model="sub_topic" spellcheck="false" />
|
|
||||||
</li>
|
|
||||||
<div>
|
|
||||||
<li>
|
|
||||||
<label for="subscribe" class="hide-on-small-screen"> </label>
|
|
||||||
<button id="subscribe" name="get" :disabled="!cansubscribe" @click="toggleSubscription">
|
|
||||||
{{ subscriptionState ? $t("mqtt_unsubscribe") : $t("mqtt_subscribe") }}
|
|
||||||
<span>
|
|
||||||
<i class="material-icons">{{ subscriptionState ? "sync_disabled" : "sync" }}</i>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
</ul>
|
|
||||||
</pw-section>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Paho from "paho-mqtt"
|
|
||||||
import { wsValid } from "~/helpers/utils/valid"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
"pw-section": () => import("~/components/layout/section"),
|
|
||||||
realtimeLog: () => import("./log"),
|
|
||||||
},
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
url: "wss://test.mosquitto.org:8081",
|
|
||||||
client: null,
|
|
||||||
pub_topic: "",
|
|
||||||
sub_topic: "",
|
|
||||||
msg: "",
|
|
||||||
connectionState: false,
|
|
||||||
log: null,
|
|
||||||
manualDisconnect: false,
|
|
||||||
subscriptionState: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
validUrl() {
|
|
||||||
return wsValid(this.url)
|
|
||||||
},
|
|
||||||
canpublish() {
|
|
||||||
return this.pub_topic != "" && this.msg != "" && this.connectionState
|
|
||||||
},
|
|
||||||
cansubscribe() {
|
|
||||||
return this.sub_topic != "" && this.connectionState
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
connect() {
|
|
||||||
this.log = [
|
|
||||||
{
|
|
||||||
payload: this.$t("connecting_to", { name: this.url }),
|
|
||||||
source: "info",
|
|
||||||
color: "var(--ac-color)",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
let parseUrl = new URL(this.url)
|
|
||||||
this.client = new Paho.Client(
|
|
||||||
parseUrl.hostname,
|
|
||||||
parseUrl.port != "" ? Number(parseUrl.port) : 8081,
|
|
||||||
"postwoman"
|
|
||||||
)
|
|
||||||
this.client.connect({
|
|
||||||
onSuccess: this.onConnectionSuccess,
|
|
||||||
onFailure: this.onConnectionFailure,
|
|
||||||
useSSL: true,
|
|
||||||
})
|
|
||||||
this.client.onConnectionLost = this.onConnectionLost
|
|
||||||
this.client.onMessageArrived = this.onMessageArrived
|
|
||||||
},
|
|
||||||
onConnectionFailure() {
|
|
||||||
this.connectionState = false
|
|
||||||
this.log.push({
|
|
||||||
payload: this.$t("error_occurred"),
|
|
||||||
source: "info",
|
|
||||||
color: "#ff5555",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onConnectionSuccess() {
|
|
||||||
this.connectionState = true
|
|
||||||
this.log.push({
|
|
||||||
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",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onMessageArrived(message) {
|
|
||||||
this.log.push({
|
|
||||||
payload: `Message: ${message.payloadString} arrived on topic: ${message.destinationName}`,
|
|
||||||
source: "info",
|
|
||||||
color: "var(--ac-color)",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
toggleConnection() {
|
|
||||||
if (this.connectionState) {
|
|
||||||
this.disconnect()
|
|
||||||
} else {
|
|
||||||
this.connect()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
disconnect() {
|
|
||||||
this.manualDisconnect = true
|
|
||||||
this.client.disconnect()
|
|
||||||
this.log.push({
|
|
||||||
payload: this.$t("disconnected_from", { name: this.url }),
|
|
||||||
source: "info",
|
|
||||||
color: "#ff5555",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onConnectionLost() {
|
|
||||||
this.connectionState = false
|
|
||||||
if (this.manualDisconnect) {
|
|
||||||
this.$toast.error(this.$t("disconnected"), {
|
|
||||||
icon: "sync_disabled",
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.$toast.error(this.$t("something_went_wrong"), {
|
|
||||||
icon: "error",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.manualDisconnect = false
|
|
||||||
this.subscriptionState = false
|
|
||||||
},
|
|
||||||
publish() {
|
|
||||||
try {
|
|
||||||
this.client.publish(this.pub_topic, this.msg, 0, false)
|
|
||||||
this.log.push({
|
|
||||||
payload: `Published message: ${this.msg} to topic: ${this.pub_topic}`,
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
source: "info",
|
|
||||||
color: "var(--ac-color)",
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
this.log.push({
|
|
||||||
payload:
|
|
||||||
this.$t("error_occurred") +
|
|
||||||
`while publishing msg: ${this.msg} to topic: ${this.pub_topic}`,
|
|
||||||
source: "info",
|
|
||||||
color: "#ff5555",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleSubscription() {
|
|
||||||
if (this.subscriptionState) {
|
|
||||||
this.unsubscribe()
|
|
||||||
} else {
|
|
||||||
this.subscribe()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
subscribe() {
|
|
||||||
try {
|
|
||||||
this.client.subscribe(this.sub_topic, {
|
|
||||||
onSuccess: this.usubSuccess,
|
|
||||||
onFailure: this.usubFailure,
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
this.log.push({
|
|
||||||
payload: this.$t("error_occurred") + `while subscribing to topic: ${this.sub_topic}`,
|
|
||||||
source: "info",
|
|
||||||
color: "#ff5555",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
usubSuccess() {
|
|
||||||
this.subscriptionState = !this.subscriptionState
|
|
||||||
this.log.push({
|
|
||||||
payload:
|
|
||||||
`Successfully ` +
|
|
||||||
(this.subscriptionState ? "subscribed" : "unsubscribed") +
|
|
||||||
` to topic: ${this.sub_topic}`,
|
|
||||||
source: "info",
|
|
||||||
color: "var(--ac-color)",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
usubFailure() {
|
|
||||||
this.log.push({
|
|
||||||
payload:
|
|
||||||
`Failed to ` +
|
|
||||||
(this.subscriptionState ? "unsubscribe" : "subscribe") +
|
|
||||||
` to topic: ${this.sub_topic}`,
|
|
||||||
source: "info",
|
|
||||||
color: "#ff5555",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
unsubscribe() {
|
|
||||||
this.client.unsubscribe(this.sub_topic, {
|
|
||||||
onSuccess: this.usubSuccess,
|
|
||||||
onFailure: this.usubFailure,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<pw-section class="blue" :label="$t('request')" ref="request">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="socketio-url">{{ $t("url") }}</label>
|
|
||||||
<input
|
|
||||||
id="socketio-url"
|
|
||||||
type="url"
|
|
||||||
spellcheck="false"
|
|
||||||
: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>
|
|
||||||
<realtime-log :title="$t('log')" :log="communication.log" />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="event_name">{{ $t("event_name") }}</label>
|
|
||||||
<input
|
|
||||||
id="event_name"
|
|
||||||
name="event_name"
|
|
||||||
type="text"
|
|
||||||
v-model="communication.eventName"
|
|
||||||
:readonly="!connectionState"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="socketio-message">{{ $t("message") }}</label>
|
|
||||||
<input
|
|
||||||
id="socketio-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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { socketioValid } from "~/helpers/utils/valid"
|
|
||||||
import io from "socket.io-client"
|
|
||||||
import wildcard from "socketio-wildcard"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
"pw-section": () => import("~/components/layout/section"),
|
|
||||||
realtimeLog: () => import("./log"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
url: "ws://",
|
|
||||||
connectionState: false,
|
|
||||||
io: null,
|
|
||||||
communication: {
|
|
||||||
log: null,
|
|
||||||
eventName: "",
|
|
||||||
input: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
urlValid() {
|
|
||||||
return socketioValid(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: this.$t("connecting_to", { name: this.url }),
|
|
||||||
source: "info",
|
|
||||||
color: "var(--ac-color)",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.io = new io(this.url)
|
|
||||||
// Add ability to listen to all events
|
|
||||||
wildcard(io.Manager)(this.io)
|
|
||||||
this.io.on("connect", () => {
|
|
||||||
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.io.on("*", ({ data }) => {
|
|
||||||
const [eventName, message] = data
|
|
||||||
this.communication.log.push({
|
|
||||||
payload: `[${eventName}] ${message ? JSON.stringify(message) : ""}`,
|
|
||||||
source: "server",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.io.on("connect_error", (error) => {
|
|
||||||
this.handleError(error)
|
|
||||||
})
|
|
||||||
this.io.on("reconnect_error", (error) => {
|
|
||||||
this.handleError(error)
|
|
||||||
})
|
|
||||||
this.io.on("error", (data) => {
|
|
||||||
this.handleError()
|
|
||||||
})
|
|
||||||
this.io.on("disconnect", () => {
|
|
||||||
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",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} catch (ex) {
|
|
||||||
this.handleError(ex)
|
|
||||||
this.$toast.error(this.$t("something_went_wrong"), {
|
|
||||||
icon: "error",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
disconnect() {
|
|
||||||
this.io.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 eventName = this.communication.eventName
|
|
||||||
let message
|
|
||||||
|
|
||||||
try {
|
|
||||||
message = JSON.parse(this.communication.input)
|
|
||||||
} catch (err) {
|
|
||||||
message = this.communication.input
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.io) {
|
|
||||||
// TODO: support only one argument now
|
|
||||||
// maybe should support more argument
|
|
||||||
this.io.emit(eventName, message, (data) => {
|
|
||||||
// receive response from server
|
|
||||||
this.communication.log.push({
|
|
||||||
payload: `[${eventName}] ${JSON.stringify(data)}`,
|
|
||||||
source: "server",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
this.communication.log.push({
|
|
||||||
payload: `[${eventName}] ${JSON.stringify(message)}`,
|
|
||||||
source: "client",
|
|
||||||
ts: new Date().toLocaleTimeString(),
|
|
||||||
})
|
|
||||||
this.communication.input = ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="page">
|
|
||||||
<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>
|
|
||||||
<realtime-log :title="$t('events')" :log="events.log" />
|
|
||||||
<div id="result"></div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</pw-section>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { httpValid } from "~/helpers/utils/valid"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
"pw-section": () => import("../layout/section"),
|
|
||||||
realtimeLog: () => import("./log"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
connectionSSEState: false,
|
|
||||||
server: "https://express-eventsource.herokuapp.com/events",
|
|
||||||
sse: null,
|
|
||||||
events: {
|
|
||||||
log: null,
|
|
||||||
input: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
serverValid() {
|
|
||||||
return httpValid(this.server)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
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 = ({ data }) => {
|
|
||||||
this.events.log.push({
|
|
||||||
payload: 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()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="page">
|
|
||||||
<pw-section class="blue" :label="$t('request')" ref="request">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="websocket-url">{{ $t("url") }}</label>
|
|
||||||
<input
|
|
||||||
id="websocket-url"
|
|
||||||
type="url"
|
|
||||||
spellcheck="false"
|
|
||||||
: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>
|
|
||||||
<realtime-log :title="$t('log')" :log="communication.log" />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<label for="websocket-message">{{ $t("message") }}</label>
|
|
||||||
<input
|
|
||||||
id="websocket-message"
|
|
||||||
name="message"
|
|
||||||
type="text"
|
|
||||||
v-model="communication.input"
|
|
||||||
:readonly="!connectionState"
|
|
||||||
@keyup.enter="connectionState ? sendMessage() : null"
|
|
||||||
@keyup.up="connectionState ? walkHistory('up') : null"
|
|
||||||
@keyup.down="connectionState ? walkHistory('down') : 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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { wsValid } from "~/helpers/utils/valid"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
"pw-section": () => import("../layout/section"),
|
|
||||||
realtimeLog: () => import("./log"),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
connectionState: false,
|
|
||||||
url: "wss://echo.websocket.org",
|
|
||||||
socket: null,
|
|
||||||
communication: {
|
|
||||||
log: null,
|
|
||||||
input: "",
|
|
||||||
},
|
|
||||||
currentIndex: -1, //index of the message log array to put in input box
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
urlValid() {
|
|
||||||
return wsValid(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: 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 = ({ data }) => {
|
|
||||||
this.communication.log.push({
|
|
||||||
payload: 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 = ""
|
|
||||||
},
|
|
||||||
walkHistory(direction) {
|
|
||||||
const clientMessages = this.communication.log.filter(({ source }) => source === "client")
|
|
||||||
const length = clientMessages.length
|
|
||||||
switch (direction) {
|
|
||||||
case "up":
|
|
||||||
if (length > 0 && this.currentIndex !== 0) {
|
|
||||||
//does nothing if message log is empty or the currentIndex is 0 when up arrow is pressed
|
|
||||||
if (this.currentIndex === -1) {
|
|
||||||
this.currentIndex = length - 1
|
|
||||||
this.communication.input = clientMessages[this.currentIndex].payload
|
|
||||||
} else if (this.currentIndex === 0) {
|
|
||||||
this.communication.input = clientMessages[0].payload
|
|
||||||
} else if (this.currentIndex > 0) {
|
|
||||||
this.currentIndex = this.currentIndex - 1
|
|
||||||
this.communication.input = clientMessages[this.currentIndex].payload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case "down":
|
|
||||||
if (length > 0 && this.currentIndex > -1) {
|
|
||||||
if (this.currentIndex === length - 1) {
|
|
||||||
this.currentIndex = -1
|
|
||||||
this.communication.input = ""
|
|
||||||
} else if (this.currentIndex < length - 1) {
|
|
||||||
this.currentIndex = this.currentIndex + 1
|
|
||||||
this.communication.input = clientMessages[this.currentIndex].payload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<template>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.color {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 8px;
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 3px solid var(--bg-dark-color);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
|
|
||||||
&.fg {
|
|
||||||
color: var(--act-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
border: 3px solid var(--ac-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.fg.active {
|
|
||||||
border: 3px solid var(--fg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.activeTick {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="show-if-initialized" :class="{ initialized }">
|
|
||||||
<pre ref="editor"></pre>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.show-if-initialized {
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
&.initialized {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const DEFAULT_THEME = "twilight"
|
|
||||||
|
|
||||||
import ace from "ace-builds"
|
|
||||||
import "ace-builds/webpack-resolver"
|
|
||||||
import jsonParse from "~/helpers/jsonParse"
|
|
||||||
import debounce from "~/helpers/utils/debounce"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
lang: {
|
|
||||||
type: String,
|
|
||||||
default: "json",
|
|
||||||
},
|
|
||||||
lint: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Object,
|
|
||||||
default: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
initialized: false,
|
|
||||||
editor: null,
|
|
||||||
cacheValue: "",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value(value) {
|
|
||||||
if (value !== this.cacheValue) {
|
|
||||||
this.editor.session.setValue(value, 1)
|
|
||||||
this.cacheValue = value
|
|
||||||
if (this.lint) this.provideLinting(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
theme() {
|
|
||||||
this.initialized = false
|
|
||||||
this.editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
|
||||||
this.$nextTick().then(() => {
|
|
||||||
this.initialized = true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
lang(value) {
|
|
||||||
this.editor.getSession().setMode("ace/mode/" + value)
|
|
||||||
},
|
|
||||||
options(value) {
|
|
||||||
this.editor.setOptions(value)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
const editor = ace.edit(this.$refs.editor, {
|
|
||||||
mode: `ace/mode/${this.lang}`,
|
|
||||||
...this.options,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Set the theme and show the editor only after it's been set to prevent FOUC.
|
|
||||||
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
|
||||||
this.$nextTick().then(() => {
|
|
||||||
this.initialized = true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
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
|
|
||||||
if (this.lint) this.provideLinting(content)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Disable linting, if lint prop is false
|
|
||||||
if (this.lint) this.provideLinting(this.value)
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
defineTheme() {
|
|
||||||
if (this.theme) {
|
|
||||||
return this.theme
|
|
||||||
}
|
|
||||||
return this.$store.state.postwoman.settings.THEME_ACE_EDITOR || DEFAULT_THEME
|
|
||||||
},
|
|
||||||
|
|
||||||
provideLinting: debounce(function (code) {
|
|
||||||
if (this.lang === "json") {
|
|
||||||
try {
|
|
||||||
jsonParse(code)
|
|
||||||
this.editor.session.setAnnotations([])
|
|
||||||
} catch (e) {
|
|
||||||
const pos = this.editor.session.getDocument().indexToPosition(e.start, 0)
|
|
||||||
this.editor.session.setAnnotations([
|
|
||||||
{
|
|
||||||
row: pos.row,
|
|
||||||
column: pos.column,
|
|
||||||
text: e.message,
|
|
||||||
type: "error",
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 2000),
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyed() {
|
|
||||||
this.editor.destroy()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
<template>
|
|
||||||
<transition name="modal" appear>
|
|
||||||
<div class="modal-backdrop">
|
|
||||||
<div class="modal-wrapper">
|
|
||||||
<div class="modal-container">
|
|
||||||
<div class="modal-header">
|
|
||||||
<slot name="header"></slot>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<slot name="body"></slot>
|
|
||||||
<!-- <div class="fade top"></div>
|
|
||||||
<div class="fade bottom"></div> -->
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<slot name="footer"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</template>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.modal-backdrop {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 998;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.32);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 16px;
|
|
||||||
padding: 16px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
background-color: var(--bg-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0px 16px 70px rgba(0, 0, 0, 0.5);
|
|
||||||
max-height: calc(100vh - 128px);
|
|
||||||
max-width: 720px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-body {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following styles are auto-applied to elements with
|
|
||||||
* transition="modal" when their visibility is toggled
|
|
||||||
* by Vue.js.
|
|
||||||
*
|
|
||||||
* You can easily play with the modal transition by editing
|
|
||||||
* these styles.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.modal-enter,
|
|
||||||
.modal-leave-active {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-enter .modal-container,
|
|
||||||
.modal-leave-active .modal-container {
|
|
||||||
transform: scale(0.8);
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade {
|
|
||||||
position: absolute;
|
|
||||||
left: 16px;
|
|
||||||
right: 20px;
|
|
||||||
display: block;
|
|
||||||
height: 32px;
|
|
||||||
transition: all 0.2s;
|
|
||||||
|
|
||||||
&.top {
|
|
||||||
top: 68px;
|
|
||||||
background: linear-gradient(to bottom, var(--bg-color), transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom {
|
|
||||||
bottom: 16px;
|
|
||||||
background: linear-gradient(to top, var(--bg-color), transparent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div v-show="isActive">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
label: { type: String },
|
|
||||||
icon: { type: String },
|
|
||||||
id: { required: true },
|
|
||||||
selected: {
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isActive: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// computed: {
|
|
||||||
// href() {
|
|
||||||
// return `#${this.label.toLowerCase().replace(/ /g, "-")}`
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.isActive = this.selected
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="tabs-wrapper">
|
|
||||||
<div class="tabs">
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="(tab, index) in tabs"
|
|
||||||
:class="{ 'is-active': tab.isActive }"
|
|
||||||
:key="index"
|
|
||||||
:tabindex="0"
|
|
||||||
@keyup.enter="selectTab(tab)"
|
|
||||||
>
|
|
||||||
<a :href="tab.href" @click="selectTab(tab)">
|
|
||||||
<i v-if="tab.icon" class="material-icons">
|
|
||||||
{{ tab.icon }}
|
|
||||||
</i>
|
|
||||||
<span v-if="tab.label">{{ tab.label }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="tabs-details">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.tabs-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.tabs {
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
display: flex;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
width: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: inline-flex;
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 8px 16px;
|
|
||||||
color: var(--fg-light-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.material-icons {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--fg-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus a {
|
|
||||||
color: var(--fg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active a {
|
|
||||||
background-color: var(--brd-color);
|
|
||||||
color: var(--fg-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
tabs: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.tabs = this.$children
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
selectTab({ id }) {
|
|
||||||
this.tabs.forEach((tab) => {
|
|
||||||
tab.isActive = tab.id == id
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div @click="toggle()">
|
|
||||||
<label class="toggle" :class="{ on: on }" ref="toggle">
|
|
||||||
<span class="handle"></span>
|
|
||||||
</label>
|
|
||||||
<label class="caption">
|
|
||||||
<slot />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
$useBorder: false;
|
|
||||||
$borderColor: var(--fg-light-color);
|
|
||||||
$activeColor: var(--ac-color);
|
|
||||||
$inactiveColor: var(--fg-light-color);
|
|
||||||
|
|
||||||
$inactiveHandleColor: var(--bg-color);
|
|
||||||
$activeHandleColor: var(--act-color);
|
|
||||||
|
|
||||||
$width: 32px;
|
|
||||||
$height: 16px;
|
|
||||||
$handleSpacing: 4px;
|
|
||||||
|
|
||||||
$transition: all 0.2s ease-in-out;
|
|
||||||
|
|
||||||
div {
|
|
||||||
display: inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
label.caption {
|
|
||||||
vertical-align: middle;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
label.toggle {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: $width;
|
|
||||||
height: $height;
|
|
||||||
border: if($useBorder, 2px solid $borderColor, none);
|
|
||||||
background-color: if($useBorder, transparent, $inactiveColor);
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
border-radius: 32px;
|
|
||||||
transition: $transition;
|
|
||||||
box-sizing: initial;
|
|
||||||
padding: 0;
|
|
||||||
margin: 8px 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.handle {
|
|
||||||
position: absolute;
|
|
||||||
display: inline-block;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
margin: $handleSpacing;
|
|
||||||
background-color: $inactiveHandleColor;
|
|
||||||
|
|
||||||
width: #{$height - ($handleSpacing * 2)};
|
|
||||||
height: #{$height - ($handleSpacing * 2)};
|
|
||||||
border-radius: 100px;
|
|
||||||
|
|
||||||
pointer-events: none;
|
|
||||||
transition: $transition;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.on {
|
|
||||||
background-color: $activeColor;
|
|
||||||
border-color: $activeColor;
|
|
||||||
|
|
||||||
.handle {
|
|
||||||
background-color: $activeHandleColor;
|
|
||||||
left: #{$width - $height};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
on: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggle() {
|
|
||||||
const containsOnClass = this.$refs.toggle.classList.toggle("on")
|
|
||||||
this.$emit("change", containsOnClass)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"baseUrl": "http://localhost:3000",
|
|
||||||
"integrationFolder": "tests/e2e/integration",
|
|
||||||
"screenshotsFolder": "tests/e2e/screenshots",
|
|
||||||
"fixturesFolder": "tests/e2e/fixtures",
|
|
||||||
"supportFile": "tests/e2e/support",
|
|
||||||
"pluginsFile": false,
|
|
||||||
"video": false
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"rules": {
|
|
||||||
".read": false,
|
|
||||||
".write": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
volumes:
|
volumes:
|
||||||
- "./.postwoman:/app/.postwoman"
|
- "./.hoppscotch:/app/.hoppscotch"
|
||||||
- "./assets:/app/assets"
|
- "./assets:/app/assets"
|
||||||
- "./directives:/app/directives"
|
- "./directives:/app/directives"
|
||||||
- "./layouts:/app/layouts"
|
- "./layouts:/app/layouts"
|
||||||
@@ -15,6 +15,9 @@ services:
|
|||||||
- "./static:/app/static"
|
- "./static:/app/static"
|
||||||
- "./store:/app/store"
|
- "./store:/app/store"
|
||||||
- "./components:/app/components"
|
- "./components:/app/components"
|
||||||
|
- "./helpers:/app/helpers"
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
HOST: 0.0.0.0
|
||||||
command: "npm run dev"
|
command: "npm run dev"
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
<!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,18 +1,19 @@
|
|||||||
{
|
{
|
||||||
"database": {
|
|
||||||
"rules": "database.rules.json"
|
|
||||||
},
|
|
||||||
"firestore": {
|
"firestore": {
|
||||||
"rules": "firestore.rules",
|
"rules": "firestore.rules",
|
||||||
"indexes": "firestore.indexes.json"
|
"indexes": "firestore.indexes.json"
|
||||||
},
|
},
|
||||||
"hosting": {
|
"hosting": {
|
||||||
"target": "postwoman",
|
"predeploy": [
|
||||||
"public": "dist",
|
"cd packages/hoppscotch-app && mv .env.example .env && cd ../.. && npm install -g pnpm && pnpm i && pnpm run generate"
|
||||||
"cleanUrls": true,
|
],
|
||||||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
|
"public": "packages/hoppscotch-app/dist",
|
||||||
},
|
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
|
||||||
"storage": {
|
"rewrites": [
|
||||||
"rules": "storage.rules"
|
{
|
||||||
|
"source": "**",
|
||||||
|
"destination": "/index.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,4 @@
|
|||||||
{
|
{
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// "indexes": [
|
|
||||||
// {
|
|
||||||
// "collectionGroup": "widgets",
|
|
||||||
// "queryScope": "COLLECTION",
|
|
||||||
// "fields": [
|
|
||||||
// { "fieldPath": "foo", "arrayConfig": "CONTAINS" },
|
|
||||||
// { "fieldPath": "bar", "mode": "DESCENDING" }
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
//
|
|
||||||
// "fieldOverrides": [
|
|
||||||
// {
|
|
||||||
// "collectionGroup": "widgets",
|
|
||||||
// "fieldPath": "baz",
|
|
||||||
// "indexes": [
|
|
||||||
// { "order": "ASCENDING", "queryScope": "COLLECTION" }
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// ]
|
|
||||||
"indexes": [],
|
"indexes": [],
|
||||||
"fieldOverrides": []
|
"fieldOverrides": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ service cloud.firestore {
|
|||||||
match /{document=**} {
|
match /{document=**} {
|
||||||
allow read, write: if request.auth.uid != null;
|
allow read, write: if request.auth.uid != null;
|
||||||
}
|
}
|
||||||
// Make sure the uid of the requesting user matches name of the user
|
// Make sure the uid of the requesting user matches the name of the user
|
||||||
// document. The wildcard expression {userId} makes the userId variable
|
// document. The wildcard expression {userId} makes the userId variable
|
||||||
// available in rules.
|
// available in rules.
|
||||||
match /users/{userId} {
|
match /users/{userId} {
|
||||||
|
|||||||
1
functions/.gitignore
vendored
1
functions/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
// const functions = require('firebase-functions');
|
|
||||||
|
|
||||||
// // Create and Deploy Your First Cloud Functions
|
|
||||||
// // https://firebase.google.com/docs/functions/write-firebase-functions
|
|
||||||
//
|
|
||||||
// exports.helloWorld = functions.https.onRequest((request, response) => {
|
|
||||||
// response.send("Hello from Firebase!");
|
|
||||||
// });
|
|
||||||
1915
functions/package-lock.json
generated
1915
functions/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "functions",
|
|
||||||
"description": "Cloud Functions for Firebase",
|
|
||||||
"scripts": {
|
|
||||||
"serve": "firebase serve --only functions",
|
|
||||||
"shell": "firebase functions:shell",
|
|
||||||
"start": "npm run shell",
|
|
||||||
"deploy": "firebase deploy --only functions",
|
|
||||||
"logs": "firebase functions:log"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "8"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"firebase-admin": "^8.0.0",
|
|
||||||
"firebase-functions": "^3.1.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"firebase-functions-test": "^0.1.6"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
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"
|
|
||||||
}
|
|
||||||
225
helpers/fb.js
225
helpers/fb.js
@@ -1,225 +0,0 @@
|
|||||||
import firebase from "firebase/app"
|
|
||||||
import "firebase/firestore"
|
|
||||||
import "firebase/auth"
|
|
||||||
|
|
||||||
// Initialize Firebase, copied from cloud console
|
|
||||||
const firebaseConfig = {
|
|
||||||
apiKey: process.env.API_KEY || "AIzaSyCMsFreESs58-hRxTtiqQrIcimh4i1wbsM",
|
|
||||||
authDomain: process.env.AUTH_DOMAIN || "postwoman-api.firebaseapp.com",
|
|
||||||
databaseURL: process.env.DATABASE_URL || "https://postwoman-api.firebaseio.com",
|
|
||||||
projectId: process.env.PROJECT_ID || "postwoman-api",
|
|
||||||
storageBucket: process.env.STORAGE_BUCKET || "postwoman-api.appspot.com",
|
|
||||||
messagingSenderId: process.env.MESSAGING_SENDER_ID || "421993993223",
|
|
||||||
appId: process.env.APP_ID || "1:421993993223:web:ec0baa8ee8c02ffa1fc6a2",
|
|
||||||
measurementId: process.env.MEASUREMENT_ID || "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: null,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
if (collections.length > 0) {
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
if (environments.length > 0) {
|
|
||||||
fb.currentEnvironments = environments[0].environment
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
fb.currentUser = null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
const htmlLens = {
|
|
||||||
lensName: "HTML",
|
|
||||||
supportedContentTypes: ["text/html"],
|
|
||||||
renderer: "htmlres",
|
|
||||||
rendererImport: () => import("~/components/lenses/renderers/HTMLLensRenderer"),
|
|
||||||
}
|
|
||||||
|
|
||||||
export default htmlLens
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
const imageLens = {
|
|
||||||
lensName: "Image",
|
|
||||||
supportedContentTypes: [
|
|
||||||
"image/gif",
|
|
||||||
"image/jpeg",
|
|
||||||
"image/png",
|
|
||||||
"image/bmp",
|
|
||||||
"image/svg+xml",
|
|
||||||
"image/x-icon",
|
|
||||||
"image/vnd.microsoft.icon",
|
|
||||||
],
|
|
||||||
renderer: "imageres",
|
|
||||||
rendererImport: () => import("~/components/lenses/renderers/ImageLensRenderer"),
|
|
||||||
}
|
|
||||||
|
|
||||||
export default imageLens
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
const jsonLens = {
|
|
||||||
lensName: "JSON",
|
|
||||||
supportedContentTypes: ["application/json", "application/hal+json", "application/vnd.api+json"],
|
|
||||||
renderer: "json",
|
|
||||||
rendererImport: () => import("~/components/lenses/renderers/JSONLensRenderer"),
|
|
||||||
}
|
|
||||||
|
|
||||||
export default jsonLens
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import jsonLens from "./jsonLens"
|
|
||||||
import rawLens from "./rawLens"
|
|
||||||
import imageLens from "./imageLens"
|
|
||||||
import htmlLens from "./htmlLens"
|
|
||||||
import xmlLens from "./xmlLens"
|
|
||||||
|
|
||||||
const lenses = [jsonLens, imageLens, htmlLens, xmlLens, rawLens]
|
|
||||||
|
|
||||||
export function getSuitableLenses(response) {
|
|
||||||
const result = []
|
|
||||||
|
|
||||||
if (response && response.headers && response.headers["content-type"]) {
|
|
||||||
const properContentType = response.headers["content-type"].split(";")[0]
|
|
||||||
|
|
||||||
for (const lens of lenses) {
|
|
||||||
if (
|
|
||||||
lens.supportedContentTypes === null ||
|
|
||||||
lens.supportedContentTypes.includes(properContentType)
|
|
||||||
) {
|
|
||||||
result.push(lens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We don't know the content type, so lets just add rawLens
|
|
||||||
result.push(rawLens)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLensRenderers() {
|
|
||||||
const response = {}
|
|
||||||
for (const lens of lenses) {
|
|
||||||
response[lens.renderer] = lens.rendererImport
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user