Compare commits
1468 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
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_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
@@ -97,8 +97,8 @@ sw.*
|
||||
# Vim swap files
|
||||
*.swp
|
||||
|
||||
# Postwoman build data
|
||||
.postwoman
|
||||
# Build data
|
||||
.hoppscotch
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# https://editorconfig.org
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
charset = utf-8
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
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": {
|
||||
"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
|
||||
open_collective: postwoman
|
||||
patreon: liyasthomas
|
||||
custom: https://www.paypal.me/liyascthomas
|
||||
github: hoppscotch
|
||||
open_collective: hoppscotch
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/custom.md
vendored
2
.github/ISSUE_TEMPLATE/custom.md
vendored
@@ -6,5 +6,3 @@ labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -3,7 +3,7 @@ updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
interval: weekly
|
||||
time: '00:00'
|
||||
open-pull-requests-limit: 10
|
||||
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:
|
||||
push:
|
||||
branches: [master, ]
|
||||
branches: [main]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
branches: [main]
|
||||
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:
|
||||
analyse:
|
||||
name: Analyse
|
||||
CodeQL-Build:
|
||||
# CodeQL runs on ubuntu-latest, windows-latest, and macos-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:
|
||||
- name: Checkout repository
|
||||
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
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below).
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following
|
||||
# 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
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
- 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_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
@@ -93,8 +93,8 @@ sw.*
|
||||
# Vim swap files
|
||||
*.swp
|
||||
|
||||
# Postwoman build data
|
||||
.postwoman
|
||||
# Build data
|
||||
.hoppscotch
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
@@ -104,3 +104,9 @@ tests/*/screenshots
|
||||
|
||||
# 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
|
||||
.github
|
||||
.nuxt
|
||||
.postwoman
|
||||
.hoppscotch
|
||||
.vscode
|
||||
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
|
||||
|
||||
## [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)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.7...v1.9.9)
|
||||
|
||||
@@ -2,75 +2,131 @@
|
||||
|
||||
## 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, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community 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
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
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
|
||||
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
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
## Enforcement 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.
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
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.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## 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.
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at hello@postwoman.io. 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.
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[INSERT CONTACT METHOD].
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
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.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## 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
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][mozilla coc].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][faq]. Translations are available
|
||||
at [https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
|
||||
[mozilla coc]: https://github.com/mozilla/diversity
|
||||
[faq]: https://www.contributor-covenant.org/faq
|
||||
[translations]: 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
|
||||
variables, exposed ports, useful file locations and container parameters.
|
||||
3. Increase the version numbers in any examples files and the README.md to the new version that this
|
||||
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
|
||||
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
|
||||
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
|
||||
RUN apk add --update --no-cache \
|
||||
git
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install
|
||||
ADD . /app/
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
RUN npm install -g pnpm
|
||||
|
||||
RUN pnpm i --unsafe-perm=true
|
||||
|
||||
ENV HOST 0.0.0.0
|
||||
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
|
||||
|
||||
Copyright (c) 2019 Liyas Thomas
|
||||
Copyright (c) 2020
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
565
README.md
565
README.md
@@ -1,107 +1,105 @@
|
||||
<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>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://hoppscotch.io">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/56705483"
|
||||
alt="Hoppscotch Logo"
|
||||
height="64"
|
||||
/>
|
||||
</a>
|
||||
<br />
|
||||
<p>
|
||||
<b>A free, fast and beautiful API request builder</b>
|
||||
<h3>
|
||||
<b>
|
||||
Hoppscotch
|
||||
</b>
|
||||
</h3>
|
||||
</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>
|
||||
|
||||
[](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>
|
||||
<sub>Built with ❤︎ by
|
||||
<a href="https://github.com/liyasthomas">liyasthomas</a> and
|
||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors">contributors</a>
|
||||
<sub>
|
||||
Built with ❤︎ by
|
||||
<a href="https://github.com/hoppscotch/hoppscotch/graphs/contributors">
|
||||
contributors
|
||||
</a>
|
||||
</sub>
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
<a href="https://hoppscotch.io">
|
||||
<img
|
||||
src="https://tiny.cc/hoppscotch_screenshot_1"
|
||||
alt="Screenshot"
|
||||
width="100%"
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
</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)**
|
||||
|
||||
**Chat: [Telegram](https://t.me/postwoman_app), [Discord](https://discord.gg/GAMWxmR)**
|
||||
|
||||
**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)**
|
||||
|
||||
<div align="center">
|
||||
<a href="https://postwoman.io"><img src="https://raw.githubusercontent.com/liyasthomas/postwoman/master/static/images/screenshot1.png" alt="Screenshot1" width="100%"></a>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<summary>Table of contents</summary>
|
||||
|
||||
---
|
||||
|
||||
- [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-)
|
||||
- [Features](#features)
|
||||
- [Demo](#demo)
|
||||
- [Usage](#usage)
|
||||
- [Built with](#built-with)
|
||||
- [Developing](#developing)
|
||||
- [Docker](#docker)
|
||||
- [Releasing](#releasing)
|
||||
- [Contributing](#contributing)
|
||||
- [Continuous Integration](#continuous-integration)
|
||||
- [Changelog](#changelog)
|
||||
- [Authors](#authors)
|
||||
- [License](#license)
|
||||
|
||||
---
|
||||
|
||||
</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.
|
||||
- `POST` - Create a REST API resource
|
||||
- `PUT` - Update a REST API resource
|
||||
- `DELETE` - Delete a REST API resource or related component
|
||||
- `CONNECT` - Establishes a tunnel to the server identified by the target resource
|
||||
- `OPTIONS` - Describe the communication options for the target resource
|
||||
- `TRACE` - Performs a message loop-back test along the path to the target resource
|
||||
- `PATCH` - Apply partial modifications to a REST API resource
|
||||
- `<custom>` - Some APIs use custom request methods such as `LIST`. Type in your custom methods.
|
||||
|
||||
🌈 **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 accent color: Green (default), Yellow, Pink, Red, Purple, Orange, Cyan and Blue
|
||||
- Toggle multi-colored headings
|
||||
- Toggle auto-scroll to response
|
||||
- Choose theme: System (default), Light, Dark and Black
|
||||
- Choose accent color: Green (default), Teal, Blue, Indigo, Purple, Yellow, Orange, Red and Pink
|
||||
- Distraction free Zen mode
|
||||
|
||||
_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.
|
||||
|
||||
**Features:**
|
||||
🔥 **PWA:** Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
|
||||
|
||||
- Instant loading with Service Workers
|
||||
- Offline support
|
||||
@@ -109,41 +107,34 @@ _Customized themes are synced with local session storage_
|
||||
- Add to Home Screen
|
||||
- Desktop PWA
|
||||
|
||||
🚀 **Request**: Retrieve response from endpoint instantly.
|
||||
🚀 **Request:** Retrieve response from endpoint instantly.
|
||||
|
||||
- Choose `method`
|
||||
- Enter `URL`
|
||||
- Send
|
||||
|
||||
**Features:**
|
||||
1. Choose `method`
|
||||
2. Enter `URL`
|
||||
3. Send
|
||||
|
||||
- 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`
|
||||
- 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
|
||||
- Basic and Bearer Token authentication
|
||||
📡 **Server Sent Events:** Receive a stream of updates from a server over a HTTP connection without resorting to polling.
|
||||
|
||||
📡 **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 schemas
|
||||
- Set endpoint and get schema
|
||||
- Multi-column docs
|
||||
- Set custom request headers
|
||||
- Query schema
|
||||
- Get query response
|
||||
|
||||
🔐 **Authentication**: Allows to identify the end user.
|
||||
|
||||
**Types:**
|
||||
🔐 **Authorization:** Allows to identify the end user.
|
||||
|
||||
- None
|
||||
- Basic
|
||||
@@ -151,386 +142,222 @@ _Customized themes are synced with local session storage_
|
||||
- OAuth 2.0
|
||||
- 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.
|
||||
|
||||
**Options:**
|
||||
📃 **Request Body:** Used to send and receive data via the REST API.
|
||||
|
||||
- Set `Content Type`
|
||||
- Add or remove Parameter list
|
||||
- FormData, JSON and many more
|
||||
- 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
|
||||
- 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_
|
||||
|
||||
🌐 **Proxy**: Enable Proxy Mode from Settings to access blocked APIs.
|
||||
|
||||
**Features:**
|
||||
🌐 **Proxy:** Enable Proxy Mode from Settings to access blocked APIs.
|
||||
|
||||
- Hide your IP address
|
||||
- Fixes [`CORS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross Origin Resource Sharing) issues
|
||||
- Access APIs served in non-HTTPS (`http://`)
|
||||
- Use custom Proxy URL
|
||||
- Access APIs served in non-HTTPS (`http://`) endpoints
|
||||
- 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.
|
||||
|
||||
**Use-cases:**
|
||||
📜 **Pre-Request Scripts β:** Snippets of code associated with a request that are executed before the request is sent.
|
||||
|
||||
- Set environment variables
|
||||
- Include timestamp in the request headers
|
||||
- Send a random alphanumeric string in the URL parameters
|
||||
- Any JavaScript functions
|
||||
|
||||
📄 **API Documentation**: Create and share dynamic API documentation easily, quickly.
|
||||
|
||||
**Usage:**
|
||||
📄 **API Documentation:** Create and share dynamic API documentation easily, quickly.
|
||||
|
||||
1. Add your requests to Collections and Folders
|
||||
2. Export Collections and easily share your APIs with the rest of your team
|
||||
3. Import Collections and Generate Documentation on-the-go
|
||||
|
||||
⌨️ **Keyboard Shortcuts**: Optimized for efficiency.
|
||||
⌨️ **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>
|
||||
- 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.
|
||||
|
||||
🌎 **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
|
||||
2. Click "Choose Language" icon button
|
||||
3. Select your language from the menu
|
||||
📦 **Add-ons:** Official add-ons for hoppscotch.
|
||||
|
||||
_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.**
|
||||
|
||||
_**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)
|
||||
[ **Firefox**](https://addons.mozilla.org/en-US/firefox/addon/hoppscotch) | [ **Chrome**](https://chrome.google.com/webstore/detail/hoppscotch-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld)
|
||||
|
||||
> **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
|
||||
- Google
|
||||
- Email
|
||||
|
||||
**Sync:**
|
||||
**Synchronize your data**
|
||||
|
||||
- History
|
||||
- Collections
|
||||
- Environments
|
||||
- Notes
|
||||
- Settings
|
||||
|
||||
✅ **Post-Request Tests β**: Write tests associated with a request that are executed after the request response.
|
||||
|
||||
**Use-cases:**
|
||||
✅ **Post-Request Tests β:** Write tests associated with a request that are executed after the request response.
|
||||
|
||||
- Check the status code as an integer
|
||||
- Filter response headers
|
||||
- Parse the response data
|
||||
|
||||
📝 **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_
|
||||
- Any JavaScript functions
|
||||
|
||||
🌱 **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
|
||||
- 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
|
||||
|
||||
**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`
|
||||
2. Enter `URL`
|
||||
3. Send request
|
||||
4. Get response
|
||||
|
||||
## Built with 🔧
|
||||
## **Built with**
|
||||
|
||||
- HTML - For the web framework
|
||||
- CSS - For styling components
|
||||
- JavaScript - For magic!
|
||||
- [Vue](https://vuejs.org/)
|
||||
- [Nuxt](https://nuxtjs.org/)
|
||||
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)
|
||||
- [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS), [SCSS](https://sass-lang.com), [Windi CSS](https://windicss.org)
|
||||
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
||||
- [TypeScript](https://www.typescriptlang.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.
|
||||
2. Install dependencies by running `npm install` within the directory that you cloned (probably `postwoman`).
|
||||
3. Start the development server with `npm run dev`.
|
||||
4. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
||||
2. Install pnpm using npm by running `npm install -g pnpm`.
|
||||
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
||||
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.
|
||||
2. Run `docker-compose up`
|
||||
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
|
||||
#pull
|
||||
docker pull liyasthomas/postwoman
|
||||
|
||||
#run
|
||||
docker run -p 3000:3000 liyasthomas/postwoman:latest
|
||||
|
||||
#build
|
||||
docker build -t postwoman:latest .
|
||||
docker run --rm --name hoppscotch -p 3000:3000 hoppscotch/hoppscotch:latest
|
||||
```
|
||||
|
||||
## Releasing 🧞
|
||||
## **Releasing**
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
2. Install dependencies by running `npm install` within the directory that you cloned (probably `postwoman`).
|
||||
3. Build the release files with `npm run build`.
|
||||
4. Find the built project in `./dist`.
|
||||
2. Install pnpm using npm by running `npm install -g pnpm`.
|
||||
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
||||
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.
|
||||
|
||||
## 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)
|
||||
|
||||
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 📝
|
||||
## **Changelog**
|
||||
|
||||
See the [`CHANGELOG`](CHANGELOG.md) file for details.
|
||||
|
||||
## Authors 🧙
|
||||
## **Authors**
|
||||
|
||||
### Lead Developers
|
||||
|
||||
- **[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>
|
||||
This project exists thanks to all the people who contribute — [make a contribution](CONTRIBUTING.md).
|
||||
|
||||
<div align="center">
|
||||
<br>
|
||||
<a href="https://postwoman.io"><img src="https://raw.githubusercontent.com/liyasthomas/templates/master/assets/logo.gif" alt="Postwoman.io" width="200"></a>
|
||||
<br>
|
||||
<h3>Happy Coding ❤︎</h3>
|
||||
<a href="https://github.com/hoppscotch/hoppscotch/graphs/contributors">
|
||||
<img src="https://opencollective.com/hoppscotch/contributors.svg?width=840&button=false"
|
||||
alt="Contributors"
|
||||
width="100%" />
|
||||
</a>
|
||||
</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
|
||||
|
||||
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)
|
||||
2. Create a new branch for your translation work e.g. `es`.
|
||||
3. Copy `lang/en-US.json` to your target language file e.g. `lang/es-ES.json` and translate all the strings.
|
||||
4. Add your language entry to `nuxt.config.js`.
|
||||
1. **[Fork the repository](https://github.com/hoppscotch/hoppscotch/fork).**
|
||||
2. **Create a new branch for your translation.**
|
||||
3. **Create target language file in the [`locales`](https://github.com/hoppscotch/hoppscotch/tree/main/packages/hoppscotch-app/locales) directory.**
|
||||
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._
|
||||
|
||||
```
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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 |
26
babel.config.js
Normal file
26
babel.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
function isBabelLoader(caller) {
|
||||
return caller && caller.name === "babel-loader"
|
||||
}
|
||||
|
||||
module.exports = function (api) {
|
||||
if (api.env("test") && !api.caller(isBabelLoader)) {
|
||||
return {
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
],
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
targets: {
|
||||
node: "current",
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
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:
|
||||
context: .
|
||||
volumes:
|
||||
- "./.postwoman:/app/.postwoman"
|
||||
- "./.hoppscotch:/app/.hoppscotch"
|
||||
- "./assets:/app/assets"
|
||||
- "./directives:/app/directives"
|
||||
- "./layouts:/app/layouts"
|
||||
@@ -15,6 +15,9 @@ services:
|
||||
- "./static:/app/static"
|
||||
- "./store:/app/store"
|
||||
- "./components:/app/components"
|
||||
- "./helpers:/app/helpers"
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
HOST: 0.0.0.0
|
||||
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": {
|
||||
"rules": "firestore.rules",
|
||||
"indexes": "firestore.indexes.json"
|
||||
},
|
||||
"hosting": {
|
||||
"target": "postwoman",
|
||||
"public": "dist",
|
||||
"cleanUrls": true,
|
||||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
|
||||
},
|
||||
"storage": {
|
||||
"rules": "storage.rules"
|
||||
"predeploy": [
|
||||
"cd packages/hoppscotch-app && mv .env.example .env && cd ../.. && npm install -g pnpm && pnpm i && pnpm run generate"
|
||||
],
|
||||
"public": "packages/hoppscotch-app/dist",
|
||||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
|
||||
"rewrites": [
|
||||
{
|
||||
"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": [],
|
||||
"fieldOverrides": []
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ service cloud.firestore {
|
||||
match /{document=**} {
|
||||
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
|
||||
// available in rules.
|
||||
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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user