Compare commits
1935 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2b4aec4dd | ||
|
|
bad0604d0c | ||
|
|
a8ba2bf70f | ||
|
|
da85a7ca87 | ||
|
|
2a9cc0eebf | ||
|
|
e0ee4f5249 | ||
|
|
2ffb6d6a9d | ||
|
|
c285217ed2 | ||
|
|
c30a0b534c | ||
|
|
ff0b35fd01 | ||
|
|
c11161d294 | ||
|
|
26c05586e7 | ||
|
|
1a3ff5dbb7 | ||
|
|
942ef6a752 | ||
|
|
f88158e61f | ||
|
|
d447029cdc | ||
|
|
39a7d73430 | ||
|
|
5c31cf5c4d | ||
|
|
7c941bbc25 | ||
|
|
44ef2cf02c | ||
|
|
38949bf4b0 | ||
|
|
e0aede3fc4 | ||
|
|
fec3c309be | ||
|
|
764d001ef6 | ||
|
|
6f034ad878 | ||
|
|
b598d846bb | ||
|
|
e3fb11cce0 | ||
|
|
d4aa3699f2 | ||
|
|
80d0925ed7 | ||
|
|
77471a5f56 | ||
|
|
c655d55197 | ||
|
|
ab6de361ab | ||
|
|
113210b08e | ||
|
|
db43181733 | ||
|
|
d60939211a | ||
|
|
7c945a674d | ||
|
|
f0a70d945a | ||
|
|
fdca00b198 | ||
|
|
8b961fb8cd | ||
|
|
7e3c775f70 | ||
|
|
a25b0d0349 | ||
|
|
5c5fa5c4bc | ||
|
|
98cb9b3234 | ||
|
|
eb5f51249b | ||
|
|
d81c45fef1 | ||
|
|
22178a6c2e | ||
|
|
6644e3b606 | ||
|
|
d42f842b2f | ||
|
|
9163e27a5f | ||
|
|
f4d6958491 | ||
|
|
9cb6486c71 | ||
|
|
ad7bff102d | ||
|
|
3a8b31a5a7 | ||
|
|
5461392516 | ||
|
|
fc90ca3815 | ||
|
|
ec2f4ea547 | ||
|
|
5f06eb5116 | ||
|
|
ae10af2464 | ||
|
|
9092c06e71 | ||
|
|
54e0130216 | ||
|
|
0fc47e07d4 | ||
|
|
ae72c5a8fe | ||
|
|
a65a4796ca | ||
|
|
4789b76e66 | ||
|
|
7b52637c5f | ||
|
|
db2429d1cb | ||
|
|
25f014ef3b | ||
|
|
44cff354f2 | ||
|
|
6276c1df97 | ||
|
|
8c2420fd03 | ||
|
|
011b19b62d | ||
|
|
fbd41bc2d8 | ||
|
|
950d9b1a8f | ||
|
|
9c6b7b005f | ||
|
|
74ce8dcfce | ||
|
|
3c892eb1aa | ||
|
|
7907508240 | ||
|
|
602a13660a | ||
|
|
2adead91bf | ||
|
|
ca6e27b1f7 | ||
|
|
76082a2e61 | ||
|
|
83b1993cd2 | ||
|
|
18d569aad8 | ||
|
|
30987d5ad0 | ||
|
|
b71c95192e | ||
|
|
f6269ad0ce | ||
|
|
8f374e5244 | ||
|
|
c497faa07a | ||
|
|
2033eec0e8 | ||
|
|
76ffc0c509 | ||
|
|
538377034f | ||
|
|
33e0084e93 | ||
|
|
1fa58cab6b | ||
|
|
9747f19343 | ||
|
|
51004da441 | ||
|
|
266cb83ba9 | ||
|
|
1d7b3523ae | ||
|
|
3924077b97 | ||
|
|
868eab0344 | ||
|
|
9aec950593 | ||
|
|
dc5ab92a62 | ||
|
|
55d61e3282 | ||
|
|
60965c1b37 | ||
|
|
f5c595fa2e | ||
|
|
8a3e4b2041 | ||
|
|
de90a73a83 | ||
|
|
4721f5fda2 | ||
|
|
50f9364f7a | ||
|
|
ceb9eafe27 | ||
|
|
4e15117746 | ||
|
|
b87b43cfa8 | ||
|
|
caf056caf1 | ||
|
|
c5953b584f | ||
|
|
de053edeab | ||
|
|
392e675bd4 | ||
|
|
e53c8bd43f | ||
|
|
976d5e2bb3 | ||
|
|
d1a1c9308a | ||
|
|
1a8e162eb9 | ||
|
|
fbac03e9e1 | ||
|
|
b89a1cd066 | ||
|
|
780e80a4f9 | ||
|
|
d6bc1b8eb1 | ||
|
|
5783d10696 | ||
|
|
97e06b6284 | ||
|
|
09f7f1f5e0 | ||
|
|
943d116b69 | ||
|
|
55224aa221 | ||
|
|
05cd1fd0a8 | ||
|
|
e8762bfb25 | ||
|
|
242d212402 | ||
|
|
b14522f22d | ||
|
|
4f43756c0e | ||
|
|
4e9b724070 | ||
|
|
774c1678be | ||
|
|
bb18a7278e | ||
|
|
f2609dba56 | ||
|
|
071c57c5a2 | ||
|
|
caf76f9cd9 | ||
|
|
012b61ae8e | ||
|
|
936f91f204 | ||
|
|
de28bd4660 | ||
|
|
e60a333aff | ||
|
|
b9664906d1 | ||
|
|
910cf73515 | ||
|
|
c70bea5f62 | ||
|
|
cefb6463c5 | ||
|
|
7059b64948 | ||
|
|
76bf91bc0a | ||
|
|
3679eecfc2 | ||
|
|
ac7875011d | ||
|
|
83b708368f | ||
|
|
140d585122 | ||
|
|
848535a343 | ||
|
|
3fcf90edd7 | ||
|
|
0e7597edcc | ||
|
|
5f6c60656d | ||
|
|
b85220fc65 | ||
|
|
8c038ae662 | ||
|
|
84e3fdb4ac | ||
|
|
a0eb2d5a8d | ||
|
|
ed03bc372d | ||
|
|
ecc2d70c3c | ||
|
|
2995cf9fa6 | ||
|
|
2823a0f52a | ||
|
|
04fe61bf94 | ||
|
|
4b13c8db90 | ||
|
|
33af2f1e44 | ||
|
|
ac477c3d0d | ||
|
|
0fd7a1287b | ||
|
|
8fa09a41ff | ||
|
|
d932f9f1e2 | ||
|
|
16e54284fb | ||
|
|
61e1f87d18 | ||
|
|
65a460a9c3 | ||
|
|
3cd58548db | ||
|
|
7f5609a3eb | ||
|
|
23bab5a2b9 | ||
|
|
9919904c97 | ||
|
|
5df569d9d3 | ||
|
|
e8c9a5ecea | ||
|
|
87bc0b957c | ||
|
|
65d01303c5 | ||
|
|
887e00ca37 | ||
|
|
3443bf9941 | ||
|
|
28452a343b | ||
|
|
c8f94a4cae | ||
|
|
807491a8fd | ||
|
|
9dbe8d2a71 | ||
|
|
ab400af39e | ||
|
|
a8dc7c08e6 | ||
|
|
fc9f2a23fb | ||
|
|
0bbbc5f4cd | ||
|
|
e5f8259981 | ||
|
|
359b6de304 | ||
|
|
c321f138e9 | ||
|
|
5b10939269 | ||
|
|
b0184ca756 | ||
|
|
e092e256c5 | ||
|
|
7383043837 | ||
|
|
22e255f706 | ||
|
|
9d8d5d0cd5 | ||
|
|
f2c0c9acf8 | ||
|
|
33597d5232 | ||
|
|
228d22f9f0 | ||
|
|
754a1d0f06 | ||
|
|
ec7951bd93 | ||
|
|
ac0a89ae26 | ||
|
|
8568f4c2a9 | ||
|
|
6ef198e705 | ||
|
|
8fc379f30d | ||
|
|
46306db274 | ||
|
|
b13e130f7c | ||
|
|
76b784319d | ||
|
|
6342e3263b | ||
|
|
49e1d2107e | ||
|
|
36de22ef1c | ||
|
|
865fe1917f | ||
|
|
c00673ffcb | ||
|
|
7ae92c1c2f | ||
|
|
f0e55abab5 | ||
|
|
ce03c56030 | ||
|
|
5bb01f47ff | ||
|
|
a0f2803a80 | ||
|
|
5679500c76 | ||
|
|
7e1846dfda | ||
|
|
e5fb3bd9b1 | ||
|
|
da5f52db74 | ||
|
|
e7e960e5ec | ||
|
|
ed4f1fe6f8 | ||
|
|
2a8ba00024 | ||
|
|
3f9ae7fecc | ||
|
|
76786f7a01 | ||
|
|
99ae80cc12 | ||
|
|
b925f7e419 | ||
|
|
14d5461085 | ||
|
|
88cbbe69b2 | ||
|
|
90bd8d45b4 | ||
|
|
c370ee8018 | ||
|
|
9a4e64e13f | ||
|
|
dc771f51e8 | ||
|
|
86ee80f607 | ||
|
|
b14adc29f5 | ||
|
|
ec9954fc34 | ||
|
|
675d44c798 | ||
|
|
1f3f1a95ce | ||
|
|
83c2b4ff3c | ||
|
|
3b2934d4ae | ||
|
|
d95c53bbb6 | ||
|
|
131c58fdae | ||
|
|
3cb2c43003 | ||
|
|
222d145753 | ||
|
|
3225f283cb | ||
|
|
95b9c7c421 | ||
|
|
3a64907ce2 | ||
|
|
00921b2603 | ||
|
|
811ed57b14 | ||
|
|
6cfea0ce5b | ||
|
|
f2c3bd54d6 | ||
|
|
b0c327854c | ||
|
|
c7a9b5ccf6 | ||
|
|
8469d13a12 | ||
|
|
1f38c25e6a | ||
|
|
90a6315e2b | ||
|
|
651251b371 | ||
|
|
992fee5620 | ||
|
|
8f867540ba | ||
|
|
f7fc9ec32e | ||
|
|
b5aefd251e | ||
|
|
b7941187ff | ||
|
|
5b2fc12480 | ||
|
|
86830f91ef | ||
|
|
b5f66408fc | ||
|
|
9a318db6fb | ||
|
|
a95de9ff99 | ||
|
|
e83ca8425f | ||
|
|
b95e381a56 | ||
|
|
6a2c59700e | ||
|
|
49d91fb127 | ||
|
|
564f659778 | ||
|
|
fa31f8667f | ||
|
|
7d5c246a1f | ||
|
|
4900cfd402 | ||
|
|
5e5cdb181d | ||
|
|
1f65980f26 | ||
|
|
bd1d81c1b8 | ||
|
|
5de3dc7737 | ||
|
|
f7bf010cfa | ||
|
|
68375469c5 | ||
|
|
4c73ee7d82 | ||
|
|
32aeb97c34 | ||
|
|
b576f34893 | ||
|
|
39015d79d4 | ||
|
|
87c0702959 | ||
|
|
6fa8a46a8a | ||
|
|
7d465ca489 | ||
|
|
49bb77c098 | ||
|
|
7345cc9943 | ||
|
|
f7e71100f0 | ||
|
|
e2788b86a4 | ||
|
|
2cb350468b | ||
|
|
d962af1f18 | ||
|
|
292343660c | ||
|
|
84458fb660 | ||
|
|
94b0021453 | ||
|
|
05b45aa65a | ||
|
|
37b6dd19f0 | ||
|
|
995e0c6c51 | ||
|
|
799c650f32 | ||
|
|
33f5474033 | ||
|
|
bc3bdb835e | ||
|
|
1a1d4111fb | ||
|
|
53a4760034 | ||
|
|
ca03fc2fc3 | ||
|
|
35fe734ddf | ||
|
|
edf544ec93 | ||
|
|
e4abe4efd5 | ||
|
|
30ee570ef7 | ||
|
|
64091c1f29 | ||
|
|
cb0de46e7d | ||
|
|
a9ba2b50f9 | ||
|
|
7dd4db13a1 | ||
|
|
41c33e0626 | ||
|
|
94b2688073 | ||
|
|
08e578b4b4 | ||
|
|
e68ec2eb56 | ||
|
|
0f2686a102 | ||
|
|
da628ca26d | ||
|
|
31fde475a3 | ||
|
|
0533f3a070 | ||
|
|
1a93ce1d43 | ||
|
|
71764ad364 | ||
|
|
6a96729055 | ||
|
|
356d736f4e | ||
|
|
14701369bd | ||
|
|
b14e475db0 | ||
|
|
d6fd01d210 | ||
|
|
77edec5f76 | ||
|
|
e843203bb6 | ||
|
|
89d43ae52d | ||
|
|
10e13ce8f8 | ||
|
|
1f43a68612 | ||
|
|
3afdce151b | ||
|
|
378d9e7758 | ||
|
|
f564ca1eb5 | ||
|
|
09d2878e1d | ||
|
|
afdf4a268a | ||
|
|
0958376bb4 | ||
|
|
3c2307dca8 | ||
|
|
0d0eca89c2 | ||
|
|
a279085526 | ||
|
|
a9fdba83cc | ||
|
|
78ef6e56e0 | ||
|
|
54ce6aaffa | ||
|
|
7c5e82e31d | ||
|
|
a2b4a36414 | ||
|
|
4772b58454 | ||
|
|
fc615a04f6 | ||
|
|
1f03db1ec0 | ||
|
|
898d703a67 | ||
|
|
3248910174 | ||
|
|
88e7f35995 | ||
|
|
69a7f86559 | ||
|
|
57950e2637 | ||
|
|
e56fa732fa | ||
|
|
d2945913ce | ||
|
|
fc4d9260bf | ||
|
|
ff6ecb81d7 | ||
|
|
d1becb3b1e | ||
|
|
8df5a5ec4b | ||
|
|
37e63cc610 | ||
|
|
44de8438ef | ||
|
|
011f8c65f5 | ||
|
|
e7f1f019d6 | ||
|
|
be0f7b0067 | ||
|
|
0694f46fcd | ||
|
|
e5e66d8cc0 | ||
|
|
d4c6065e45 | ||
|
|
2208403c20 | ||
|
|
2f2580d2d3 | ||
|
|
5daf4a19b7 | ||
|
|
154c2b2da3 | ||
|
|
ab7ee92112 | ||
|
|
3a2bb63c98 | ||
|
|
241e16dd06 | ||
|
|
3bbe71f2c9 | ||
|
|
ba798287f9 | ||
|
|
e1c34a3689 | ||
|
|
257e6dfb1c | ||
|
|
05d1535823 | ||
|
|
21e8277dd9 | ||
|
|
1ed4179b70 | ||
|
|
448c239f9f | ||
|
|
b947c96858 | ||
|
|
b1c76c0ba6 | ||
|
|
4c3248065e | ||
|
|
f609d36818 | ||
|
|
e3eba8b39f | ||
|
|
679ef22438 | ||
|
|
7148738112 | ||
|
|
02c4d40f94 | ||
|
|
4239b1358e | ||
|
|
21e99598fc | ||
|
|
54207e3f33 | ||
|
|
5849d351c2 | ||
|
|
eac8381981 | ||
|
|
50339a2480 | ||
|
|
dddd8f32e8 | ||
|
|
caf07cd8ba | ||
|
|
2802e04688 | ||
|
|
492d1cfa8f | ||
|
|
f36b91c3cc | ||
|
|
60f2482082 | ||
|
|
8d7c6f46b7 | ||
|
|
590403650e | ||
|
|
f4b46763a4 | ||
|
|
68c749b378 | ||
|
|
2a1eca1539 | ||
|
|
8cc7b525d9 | ||
|
|
711cabcf9b | ||
|
|
b9b0745f30 | ||
|
|
f7f4f02d4a | ||
|
|
3d32377cb9 | ||
|
|
0cfcfa0026 | ||
|
|
2a0322541d | ||
|
|
fb0c6d42eb | ||
|
|
4867df5fe8 | ||
|
|
e88f98418a | ||
|
|
6769ca8905 | ||
|
|
8e9d6ae084 | ||
|
|
ef7f1334fe | ||
|
|
45b2b81fe4 | ||
|
|
ab4004fae0 | ||
|
|
cd91bc0628 | ||
|
|
6d534d312f | ||
|
|
ab49a125b2 | ||
|
|
2d5b70ab85 | ||
|
|
6b675d5def | ||
|
|
5779cddf22 | ||
|
|
77b50c0be3 | ||
|
|
aeebe5015e | ||
|
|
fbbe1a05a0 | ||
|
|
19bfa3a7c3 | ||
|
|
13c85b3a45 | ||
|
|
8e0adc9514 | ||
|
|
94ee60b832 | ||
|
|
00b623d823 | ||
|
|
d39b190788 | ||
|
|
b8ba6e39e2 | ||
|
|
90d347dc89 | ||
|
|
77ea9dfac9 | ||
|
|
1d616c7ec6 | ||
|
|
0eab54cbdc | ||
|
|
62a970e8c3 | ||
|
|
7c23cd7c4c | ||
|
|
e233e9be16 | ||
|
|
09d98cff7d | ||
|
|
a3d6573d93 | ||
|
|
38ec50bf0e | ||
|
|
a8c380c67a | ||
|
|
b8a8aa53ba | ||
|
|
3b1f232bc0 | ||
|
|
f846cf32fd | ||
|
|
48100ead55 | ||
|
|
3bd7c00038 | ||
|
|
6962d22142 | ||
|
|
a4014c3f39 | ||
|
|
4758acc413 | ||
|
|
939ffcd42a | ||
|
|
403254a983 | ||
|
|
21c6c07b39 | ||
|
|
070a830eaa | ||
|
|
c635d21180 | ||
|
|
19c9c5491b | ||
|
|
c2befaee46 | ||
|
|
7dc702c626 | ||
|
|
b8b2dfda76 | ||
|
|
97f5ffc8ae | ||
|
|
241abd3c50 | ||
|
|
777e629b3d | ||
|
|
1543c990ca | ||
|
|
b0f5ab9776 | ||
|
|
fdedaa073a | ||
|
|
f8d032d9fc | ||
|
|
64e20a1350 | ||
|
|
0c8eea65a5 | ||
|
|
08bb47da1d | ||
|
|
4fad3808a2 | ||
|
|
f05a875a05 | ||
|
|
4f7f2ba361 | ||
|
|
0cfe87e65c | ||
|
|
5ec9944f2a | ||
|
|
fa339a5183 | ||
|
|
5936a06ad1 | ||
|
|
c21bbf1022 | ||
|
|
00fa17b31f | ||
|
|
6e03c8d236 | ||
|
|
7022ef5f7e | ||
|
|
54d590765f | ||
|
|
d24c572d7f | ||
|
|
010be95ed5 | ||
|
|
59ca8cb2c6 | ||
|
|
15dc0ad9ac | ||
|
|
f690ea01c4 | ||
|
|
9c09a8128a | ||
|
|
16779d496e | ||
|
|
3ab7318b25 | ||
|
|
a2b7e039d5 | ||
|
|
a68e88826f | ||
|
|
7c8545c21b | ||
|
|
d17c3b4aa3 | ||
|
|
a0529b1cdd | ||
|
|
1f0c77c3bf | ||
|
|
56acfab2b2 | ||
|
|
256cef9047 | ||
|
|
7d3750e54c | ||
|
|
f021d080af | ||
|
|
293400fd76 | ||
|
|
c495ca5256 | ||
|
|
956c7bf93a | ||
|
|
219d88851e | ||
|
|
9b9fb62596 | ||
|
|
f636e7dbd4 | ||
|
|
33f2341f7f | ||
|
|
f5bbcb2777 | ||
|
|
5a56081a29 | ||
|
|
d2b73a8942 | ||
|
|
09a35cf10a | ||
|
|
eb7ac7bfc4 | ||
|
|
a98636ed39 | ||
|
|
73a2a0a127 | ||
|
|
494dc72eb9 | ||
|
|
4665dbc372 | ||
|
|
0b6671d538 | ||
|
|
392c97f35f | ||
|
|
8629eda6d4 | ||
|
|
8507f11175 | ||
|
|
ae6d33febd | ||
|
|
54e11170fb | ||
|
|
66eecf5e37 | ||
|
|
f7b1e876ad | ||
|
|
ccff68ad18 | ||
|
|
112c140ce7 | ||
|
|
c2cd9f0865 | ||
|
|
749e89e362 | ||
|
|
7241bbbb6d | ||
|
|
edb7bef5a3 | ||
|
|
436de528e9 | ||
|
|
7f0f932aca | ||
|
|
73d2d58da9 | ||
|
|
777b201c1f | ||
|
|
11ff85c56c | ||
|
|
a9ed1c0fe8 | ||
|
|
9a06b19288 | ||
|
|
516610a820 | ||
|
|
8b989924a4 | ||
|
|
bd3e1b7592 | ||
|
|
af18b95ffa | ||
|
|
9ecec8ce17 | ||
|
|
f2f097bb7e | ||
|
|
27da1c8e49 | ||
|
|
015fd24e03 | ||
|
|
1306b78f63 | ||
|
|
ad0b342d89 | ||
|
|
6d463dcfef | ||
|
|
965923116d | ||
|
|
3281adce65 | ||
|
|
74f6be6123 | ||
|
|
74b997ba49 | ||
|
|
b7c6f3e2bb | ||
|
|
8506275f90 | ||
|
|
64c3a82189 | ||
|
|
23b1d94808 | ||
|
|
0a9712d227 | ||
|
|
6575d03f45 | ||
|
|
6037daedcd | ||
|
|
a351abd5d9 | ||
|
|
8b00bb59de | ||
|
|
910c20bd00 | ||
|
|
885318623f | ||
|
|
e1df6105ed | ||
|
|
c4879d598b | ||
|
|
cc9cfa53fa | ||
|
|
996bb206c6 | ||
|
|
df0ac3b7c8 | ||
|
|
df68f9ac25 | ||
|
|
a639ee63dd | ||
|
|
04feb3cdbe | ||
|
|
f77f0a59ec | ||
|
|
fdc5695fb4 | ||
|
|
f24477e7b4 | ||
|
|
0e561e7bc2 | ||
|
|
257cb5141b | ||
|
|
89312347d1 | ||
|
|
f1e752892d | ||
|
|
6a13b8c3c5 | ||
|
|
c43a864480 | ||
|
|
06d58547c8 | ||
|
|
173e3a3fc0 | ||
|
|
d0cf780ea2 | ||
|
|
493538ae70 | ||
|
|
955bb5c99d | ||
|
|
88fc45975f | ||
|
|
09a88b4a97 | ||
|
|
67e98ef46b | ||
|
|
85c5f16e93 | ||
|
|
7cf5345c5a | ||
|
|
4c62baa577 | ||
|
|
0f55b8ee8a | ||
|
|
ad041a5cf1 | ||
|
|
3bd22f6b78 | ||
|
|
fa15457ce4 | ||
|
|
ef434ca804 | ||
|
|
2b165a065c | ||
|
|
12b7e22c31 | ||
|
|
d77e3745bb | ||
|
|
5429e493ea | ||
|
|
b05cd2e6e4 | ||
|
|
a9564086b0 | ||
|
|
c32c6e0363 | ||
|
|
f9008df1c6 | ||
|
|
9a2fa9e2c7 | ||
|
|
dd8f7d078b | ||
|
|
b40d5a75f2 | ||
|
|
01e41ba1b2 | ||
|
|
1ed28a717c | ||
|
|
1d6d8af748 | ||
|
|
f80c5d6a46 | ||
|
|
6c7643a4c3 | ||
|
|
563ac1d2fb | ||
|
|
91511c09f3 | ||
|
|
82180a54f1 | ||
|
|
aee2863caf | ||
|
|
ac705a6900 | ||
|
|
5b4f3f5b67 | ||
|
|
94c2440832 | ||
|
|
5d2ba22988 | ||
|
|
cf9fe0c02c | ||
|
|
4c57139cdc | ||
|
|
2c0b8b5f8e | ||
|
|
2634eb10be | ||
|
|
101d7807ff | ||
|
|
d707ba3bad | ||
|
|
d35d3061e5 | ||
|
|
f93fc4cf62 | ||
|
|
870484d06a | ||
|
|
f816ad0841 | ||
|
|
a55eb11f5b | ||
|
|
dacf479838 | ||
|
|
568fd873e4 | ||
|
|
d2bf0b7749 | ||
|
|
e68bfc0557 | ||
|
|
60c82fa144 | ||
|
|
aca53be909 | ||
|
|
aefe0abede | ||
|
|
e3221aedaa | ||
|
|
505268c037 | ||
|
|
363902a588 | ||
|
|
dc236c6bf1 | ||
|
|
f16e6148b4 | ||
|
|
9e615c91cb | ||
|
|
16491d36de | ||
|
|
3e126cdfa4 | ||
|
|
51fcec3369 | ||
|
|
2077dc2eee | ||
|
|
4487d90fec | ||
|
|
f088894ee1 | ||
|
|
8c9cf86d91 | ||
|
|
f41d8ca868 | ||
|
|
048776b53a | ||
|
|
ac634708cc | ||
|
|
54e4dbc4ac | ||
|
|
254b500f03 | ||
|
|
c057c7ea24 | ||
|
|
3e27157eaa | ||
|
|
0da1f3a406 | ||
|
|
113939c273 | ||
|
|
708043dd6b | ||
|
|
bc4c59694c | ||
|
|
63b7035e74 | ||
|
|
d8d5707f33 | ||
|
|
24584cdadd | ||
|
|
bbaa48c1ec | ||
|
|
92d8878c38 | ||
|
|
282f3c47ed | ||
|
|
65e9e7a73b | ||
|
|
ab336de732 | ||
|
|
2efc549aca | ||
|
|
7275e8075b | ||
|
|
118a7aa258 | ||
|
|
6bcd532767 | ||
|
|
6eb9ae8ad5 | ||
|
|
b450cd7ee4 | ||
|
|
c8568ae15a | ||
|
|
65e1221298 | ||
|
|
b4a7c45b61 | ||
|
|
211dfa24fa | ||
|
|
1fdde89d49 | ||
|
|
94d0bf59b2 | ||
|
|
05fe9dcccf | ||
|
|
f89f27698e | ||
|
|
4931804056 | ||
|
|
67bcf91073 | ||
|
|
87be0b3d3d | ||
|
|
27585d5c93 | ||
|
|
7d003cefd7 | ||
|
|
f132de8dbb | ||
|
|
b0c22a2b2d | ||
|
|
9b8936f53c | ||
|
|
c3ecf98b62 | ||
|
|
5481c28e4b | ||
|
|
4ad4bad295 | ||
|
|
5119505475 | ||
|
|
8b7fd238d0 | ||
|
|
df21deddba | ||
|
|
91b29b893b | ||
|
|
32c7ece2ef | ||
|
|
dbbf310e83 | ||
|
|
52fe6474e2 | ||
|
|
81654c35da | ||
|
|
32d57814a9 | ||
|
|
860993663f | ||
|
|
ea7feee840 | ||
|
|
a556e6d0c2 | ||
|
|
bf686736f8 | ||
|
|
56c7ec3750 | ||
|
|
a7cd6395b7 | ||
|
|
2d6d1b2d34 | ||
|
|
a235d38240 | ||
|
|
43d95a2271 | ||
|
|
40e05a0461 | ||
|
|
b60bc1ee39 | ||
|
|
bb973ee449 | ||
|
|
e16019dcbf | ||
|
|
01a890bce9 | ||
|
|
fcdf93c5e5 | ||
|
|
a34acfd0c5 | ||
|
|
2853a4bbef | ||
|
|
8bc1df9b8e | ||
|
|
29e96ddd12 | ||
|
|
a852432c9f | ||
|
|
c7e5fc47ba | ||
|
|
19a2323880 | ||
|
|
fb4554abe3 | ||
|
|
cd2680135d | ||
|
|
8ae5dbc375 | ||
|
|
744d647704 | ||
|
|
77e51f3301 | ||
|
|
f0cfee56f2 | ||
|
|
61bf735315 | ||
|
|
b2be9dce6f | ||
|
|
8b970ed9a0 | ||
|
|
5458debe35 | ||
|
|
dd970af6b1 | ||
|
|
d9feffa630 | ||
|
|
efe21d450e | ||
|
|
7728231cad | ||
|
|
3ecf62857c | ||
|
|
b8beac37c3 | ||
|
|
aa056ff1c1 | ||
|
|
23199979e6 | ||
|
|
b3dd2ebf31 | ||
|
|
0cec1b977a | ||
|
|
3b96f85c55 | ||
|
|
8e86e4c8e8 | ||
|
|
a1568a98d4 | ||
|
|
c61d825c37 | ||
|
|
730018b45f | ||
|
|
d8af767dc4 | ||
|
|
82756a9c55 | ||
|
|
fe81a7dba9 | ||
|
|
9d085e301d | ||
|
|
5206aeead0 | ||
|
|
6a93e56747 | ||
|
|
15e5edb1a3 | ||
|
|
2b17b9b218 | ||
|
|
808e4545ae | ||
|
|
2da4c385ba | ||
|
|
b6e5f7f337 | ||
|
|
8b39da6043 | ||
|
|
9e43bbcce2 | ||
|
|
a9224b94b1 | ||
|
|
951266e529 | ||
|
|
2a51ec6d9f | ||
|
|
cc5966e90c | ||
|
|
76d2ca3e37 | ||
|
|
c4c2f1cbee | ||
|
|
f7a67ec30f | ||
|
|
60676addef | ||
|
|
c4a48f1037 | ||
|
|
217cdad775 | ||
|
|
3dac917cc1 | ||
|
|
93acb03297 | ||
|
|
7c530550f9 | ||
|
|
aafb4dc901 | ||
|
|
497a4dfde6 | ||
|
|
5070d9fe95 | ||
|
|
4b48d8a8c8 | ||
|
|
ebb17489b7 | ||
|
|
1e2797a681 | ||
|
|
2faf675c0a | ||
|
|
c8878aba57 | ||
|
|
061f86fbfa | ||
|
|
1b783fdfa0 | ||
|
|
e7a15c9242 | ||
|
|
36c984d152 | ||
|
|
bd008f42dd | ||
|
|
8690d18695 | ||
|
|
3a8b4337f0 | ||
|
|
5ef1b5b75c | ||
|
|
3726f0a376 | ||
|
|
61d7dcf61b | ||
|
|
dce58bdb2f | ||
|
|
311d87b223 | ||
|
|
2214645a96 | ||
|
|
119eb321ec | ||
|
|
5fffc5d29e | ||
|
|
9838a68ff7 | ||
|
|
64222327c9 | ||
|
|
e1423033a6 | ||
|
|
dcd148e43b | ||
|
|
daf214275b | ||
|
|
a4b89a2152 | ||
|
|
9e5a162d74 | ||
|
|
3ba279c81b | ||
|
|
836bd9ab3c | ||
|
|
12ef2f4465 | ||
|
|
4ce673acfd | ||
|
|
80f7d3120a | ||
|
|
1a04d79fb9 | ||
|
|
ee49f8d26c | ||
|
|
bbb559ad2c | ||
|
|
5dab781fbe | ||
|
|
2f3c3aeba9 | ||
|
|
7f8b49dd1d | ||
|
|
14f03e61ef | ||
|
|
bf08883f15 | ||
|
|
f1b22e7122 | ||
|
|
caf33ba87e | ||
|
|
46d5ffc05b | ||
|
|
086b2de505 | ||
|
|
908d672cf2 | ||
|
|
d13e6f1897 | ||
|
|
de480c457a | ||
|
|
493b6cd576 | ||
|
|
7296873562 | ||
|
|
85fd5cf0dc | ||
|
|
bb9db3b1ca | ||
|
|
35a0d50918 | ||
|
|
57f7621567 | ||
|
|
bf1a143f03 | ||
|
|
355688abf8 | ||
|
|
f5efb425cd | ||
|
|
cc396aecd3 | ||
|
|
faa420753f | ||
|
|
dbba0cf847 | ||
|
|
725fa31e77 | ||
|
|
bb924dc250 | ||
|
|
b0346aa340 | ||
|
|
671fca0736 | ||
|
|
322945ec99 | ||
|
|
1c6f87784b | ||
|
|
9d56af4160 | ||
|
|
fdedb1f231 | ||
|
|
161c0b7b88 | ||
|
|
f847cbe122 | ||
|
|
df48e85de5 | ||
|
|
2a818dc81d | ||
|
|
83a20bd7de | ||
|
|
2e7e40c4cc | ||
|
|
b98d9074bb | ||
|
|
5377c69b40 | ||
|
|
d24b917c17 | ||
|
|
d99ede8c05 | ||
|
|
3f727d6f71 | ||
|
|
99acc4921c | ||
|
|
db7dcba1b9 | ||
|
|
8f76d3fa58 | ||
|
|
0930b1ada8 | ||
|
|
35df7c6429 | ||
|
|
e679e8c5f4 | ||
|
|
d43a655116 | ||
|
|
56a4ca0e21 | ||
|
|
f17b126dd4 | ||
|
|
1ac144e3a7 | ||
|
|
13402a5aa5 | ||
|
|
92a0cc245f | ||
|
|
8b9a2c5f7e | ||
|
|
bc10e4304f | ||
|
|
d40de785b7 | ||
|
|
9ff02eefb8 | ||
|
|
e8837e69a0 | ||
|
|
9d02f2687d | ||
|
|
9f2ecd619f | ||
|
|
6b899b2ce0 | ||
|
|
d5cb66079b | ||
|
|
5690599241 | ||
|
|
3ee2bae78d | ||
|
|
21041fc6da | ||
|
|
2b40db285e | ||
|
|
c3a6cc133f | ||
|
|
10f6bb9cc6 | ||
|
|
5589c61423 | ||
|
|
158c34d091 | ||
|
|
99f182599a | ||
|
|
216bd4e7b4 | ||
|
|
ecfc7c84c3 | ||
|
|
d4819bcd0a | ||
|
|
b067a4723b | ||
|
|
9217ebf75e | ||
|
|
305a8f74b0 | ||
|
|
a3666b3d44 | ||
|
|
182bca9361 | ||
|
|
f0868f383b | ||
|
|
001c4818a0 | ||
|
|
0d47a1a1c2 | ||
|
|
75f4d8e09c | ||
|
|
2e5d8b330b | ||
|
|
42e0200956 | ||
|
|
780bc55a96 | ||
|
|
bc6ea34f14 | ||
|
|
ae237f1ad3 | ||
|
|
7451a0bcc4 | ||
|
|
ffed1dbc90 | ||
|
|
3ffe5559e7 | ||
|
|
2c93e2f783 | ||
|
|
7ef5919ba1 | ||
|
|
4c19a7d598 | ||
|
|
feb782270b | ||
|
|
29c6109ea3 | ||
|
|
ffe4796f2c | ||
|
|
5762cea9bd | ||
|
|
7b0a1c4266 | ||
|
|
d2086b1661 | ||
|
|
c50cbc9750 | ||
|
|
24ee395b73 | ||
|
|
48bd772171 | ||
|
|
928ace5ba6 | ||
|
|
0791d196d4 | ||
|
|
9223044967 | ||
|
|
998a925fd1 | ||
|
|
f49bfb4d74 | ||
|
|
b20cb040b0 | ||
|
|
82096f3a1e | ||
|
|
fcc6f5aa76 | ||
|
|
4c82ef89fb | ||
|
|
9b7141b1f4 | ||
|
|
965133d6e2 | ||
|
|
498b3f80bf | ||
|
|
6cf9044db6 | ||
|
|
acc80cff74 | ||
|
|
9e2407b7f1 | ||
|
|
6da9beb273 | ||
|
|
1104f22f19 | ||
|
|
cd272ab0c8 | ||
|
|
34dbec4a5c | ||
|
|
3f0608fd9c | ||
|
|
17a6b03d00 | ||
|
|
5efb921a6b | ||
|
|
01d4443a4f | ||
|
|
1591205f46 | ||
|
|
ad005d99d2 | ||
|
|
27e1541a6d | ||
|
|
9fb966f705 | ||
|
|
096b981247 | ||
|
|
cea9ac3965 | ||
|
|
f446c9acc1 | ||
|
|
870521c004 | ||
|
|
05114b6dfc | ||
|
|
945a83625d | ||
|
|
e2d7c0225e | ||
|
|
55e5444a71 | ||
|
|
a3e8a4a41f | ||
|
|
b91798e8b8 | ||
|
|
d284002803 | ||
|
|
74e6bf50b1 | ||
|
|
60ba539104 | ||
|
|
979909ad57 | ||
|
|
c4bd471516 | ||
|
|
cd8b4d0dd1 | ||
|
|
d018ebda7e | ||
|
|
5b9aeb70d9 | ||
|
|
35d551f05e | ||
|
|
a32eb24f8c | ||
|
|
5c7f3c282b | ||
|
|
c19c399508 | ||
|
|
4d24d49a0b | ||
|
|
cbe214113c | ||
|
|
fdc2d2cb17 | ||
|
|
eae1d22a2f | ||
|
|
3d66a7deb9 | ||
|
|
a4022dc70c | ||
|
|
f82c0cdb4f | ||
|
|
6e208c3766 | ||
|
|
777fe9b42a | ||
|
|
afbf21c47f | ||
|
|
461c30580f | ||
|
|
82cdcc1cce | ||
|
|
3efcaf4921 | ||
|
|
2be4b672d5 | ||
|
|
2b4d743d79 | ||
|
|
c80634b026 | ||
|
|
0028d03e1b | ||
|
|
d687c9f36d | ||
|
|
5fbf4879ae | ||
|
|
a719dff1f7 | ||
|
|
d99170f0d9 | ||
|
|
7d5a8499c3 | ||
|
|
1cfaf0bd57 | ||
|
|
bc3c608277 | ||
|
|
2f09995306 | ||
|
|
72acdeaab9 | ||
|
|
0bfc0256a6 | ||
|
|
c1a733af53 | ||
|
|
c3544076ba | ||
|
|
ae528f6302 | ||
|
|
6a63325f10 | ||
|
|
395d244e04 | ||
|
|
d9f0e61375 | ||
|
|
b3751ec4bf | ||
|
|
a11a24bfe2 | ||
|
|
b31de72ccc | ||
|
|
fa4c08dcc5 | ||
|
|
d0aa75c792 | ||
|
|
492a5bd5e9 | ||
|
|
772fdd5e87 | ||
|
|
c4116d6819 | ||
|
|
050a53af0d | ||
|
|
a42136c419 | ||
|
|
c5d5c15b11 | ||
|
|
2360803e44 | ||
|
|
3f5752247b | ||
|
|
1e85a649db | ||
|
|
84eed2aab2 | ||
|
|
32f5e784e8 | ||
|
|
53e391982f | ||
|
|
0f8adf13ae | ||
|
|
a91e265097 | ||
|
|
b7fc72004f | ||
|
|
9927239ec9 | ||
|
|
cff94a1672 | ||
|
|
a0191bafe7 | ||
|
|
f2262c0e19 | ||
|
|
068d251b64 | ||
|
|
1872dacb5e | ||
|
|
ef162dd963 | ||
|
|
2a19285d3c | ||
|
|
38a9a75b2d | ||
|
|
6959a4510e | ||
|
|
fe383d32a7 | ||
|
|
560bdb139a | ||
|
|
fe14286bc8 | ||
|
|
7584b489ac | ||
|
|
84461ffa35 | ||
|
|
d89c652622 | ||
|
|
9d19d26d8e | ||
|
|
e307d51f24 | ||
|
|
4632e281a6 | ||
|
|
ab4f961795 | ||
|
|
9859a34e90 | ||
|
|
b10a209daf | ||
|
|
2a81c2611b | ||
|
|
8d98c8ad16 | ||
|
|
1c12c66b31 | ||
|
|
cb79567942 | ||
|
|
c35a9c64f6 | ||
|
|
95832eb657 | ||
|
|
ee53c12c35 | ||
|
|
c3d2d928b3 | ||
|
|
87e598b5f0 | ||
|
|
f61cbba8cb | ||
|
|
a4089efe85 | ||
|
|
781d8e3b68 | ||
|
|
cf81a970cc | ||
|
|
367d73ef23 | ||
|
|
314084d092 | ||
|
|
e4441d129c | ||
|
|
292550f6b5 | ||
|
|
93364afc98 | ||
|
|
558b961afa | ||
|
|
d99b5cf525 | ||
|
|
c75e4a52eb | ||
|
|
0ac01fe8e5 | ||
|
|
13615e06e9 | ||
|
|
623b177eb0 | ||
|
|
a74522c465 | ||
|
|
4f5788fe31 | ||
|
|
d739af3d4b | ||
|
|
3a2284cf99 | ||
|
|
9019babf6d | ||
|
|
5382cd8d5f | ||
|
|
37c41086d2 | ||
|
|
2697cce6dd | ||
|
|
294b26787b | ||
|
|
b67de81616 | ||
|
|
36568d5720 | ||
|
|
dcf1966719 | ||
|
|
33cbc9b525 | ||
|
|
d2115ab004 | ||
|
|
9cbd6ea7b7 | ||
|
|
ded69f979e | ||
|
|
7fab6f4732 | ||
|
|
9ad822577b | ||
|
|
bdaac40435 | ||
|
|
19b43e152a | ||
|
|
c4c320da83 | ||
|
|
43cd6504b6 | ||
|
|
cb8734bba7 | ||
|
|
57860c17d9 | ||
|
|
c56c162045 | ||
|
|
249407403d | ||
|
|
1aa5ec6aa4 | ||
|
|
f129ead9a0 | ||
|
|
321a45615a | ||
|
|
dd280732d1 | ||
|
|
8966e6fd55 | ||
|
|
3523b5f2c7 | ||
|
|
2f727b1a1e | ||
|
|
6e537eed58 | ||
|
|
ffb8fd0172 | ||
|
|
ba1410c7f4 | ||
|
|
61f8e36383 | ||
|
|
20a7094d33 | ||
|
|
06d7534462 | ||
|
|
b3680224cc | ||
|
|
113bf14718 | ||
|
|
937df4486e | ||
|
|
5f79ca2872 | ||
|
|
e999d7428a | ||
|
|
146df237f2 | ||
|
|
d802945ec7 | ||
|
|
122782f244 | ||
|
|
ff006a254a | ||
|
|
c1d11e7489 | ||
|
|
7d78d34de0 | ||
|
|
5f97f49f28 | ||
|
|
f63bb11cc3 | ||
|
|
b23f0a9c16 | ||
|
|
e609300533 | ||
|
|
f623b31220 | ||
|
|
a17239ca31 | ||
|
|
f2a35a7716 | ||
|
|
0383403cce | ||
|
|
57b71ce4ea | ||
|
|
f6752e9743 | ||
|
|
45fa84d5ae | ||
|
|
873b97b052 | ||
|
|
7a25f4c13c | ||
|
|
48e9171153 | ||
|
|
1dbea4d39a | ||
|
|
edba562a99 | ||
|
|
60368341ed | ||
|
|
ab24faaa55 | ||
|
|
e79b113907 | ||
|
|
fb69fcee3c | ||
|
|
41ff83821b | ||
|
|
463ef82255 | ||
|
|
5d011b09ae | ||
|
|
638f3f1a05 | ||
|
|
879fc58d9c | ||
|
|
2c139c2a65 | ||
|
|
4c9c9a2240 | ||
|
|
354ad3983f | ||
|
|
8d99d4aa99 | ||
|
|
1ab2de9c69 | ||
|
|
6a938cdcca | ||
|
|
0c705bfa6d | ||
|
|
6169f1c150 | ||
|
|
3639ed8f70 | ||
|
|
2083e02698 | ||
|
|
a7d483ea1b | ||
|
|
a0dd02ec07 | ||
|
|
929d955237 | ||
|
|
f61eeebc8f | ||
|
|
f8c3d1e6db | ||
|
|
38513d3eed | ||
|
|
051259419c | ||
|
|
fad28ee40b | ||
|
|
1d9778226c | ||
|
|
f1dab84571 | ||
|
|
c413317970 | ||
|
|
631c8b625b | ||
|
|
2dd9683eb1 | ||
|
|
f7faac1afc | ||
|
|
91d0422f53 | ||
|
|
a22090e3df | ||
|
|
62f52a0be1 | ||
|
|
eca1dc8e66 | ||
|
|
8e3542863a | ||
|
|
34be6ce795 | ||
|
|
2d995b87b1 | ||
|
|
edbb81d089 | ||
|
|
6c4fbb501c | ||
|
|
5f3ca632cb | ||
|
|
3db28e4f22 | ||
|
|
79a7b3c985 | ||
|
|
109d57b4b4 | ||
|
|
71779d560a | ||
|
|
967bf49db0 | ||
|
|
3cedd48503 | ||
|
|
0c303b63bd | ||
|
|
ef070dbad7 | ||
|
|
d88e777f80 | ||
|
|
cffdd56522 | ||
|
|
d44b821a04 | ||
|
|
0c6a59282e | ||
|
|
3ba2aa922f | ||
|
|
ce8e2c6684 | ||
|
|
d3aa8e03a2 | ||
|
|
3ae9c49029 | ||
|
|
bac0db10a6 | ||
|
|
a40d67138b | ||
|
|
a8f6df16a8 | ||
|
|
63f23118b6 | ||
|
|
71de9cfec5 | ||
|
|
5f8729536c | ||
|
|
461dd21806 | ||
|
|
d5a44fdec4 | ||
|
|
0fd96aa92e | ||
|
|
3ba6de6f34 | ||
|
|
d214be027d | ||
|
|
af7273e7eb | ||
|
|
69971198af | ||
|
|
23a681f214 | ||
|
|
7516524d38 | ||
|
|
474711431e | ||
|
|
3cea9de8fe | ||
|
|
9235b0ffb6 | ||
|
|
a159275dba | ||
|
|
105005aa07 | ||
|
|
40a8d45ab0 | ||
|
|
009eae83a6 | ||
|
|
c146879fd3 | ||
|
|
00c6e22861 | ||
|
|
939a74b7be | ||
|
|
a53db858e3 | ||
|
|
38da938ec1 | ||
|
|
8cf1a37633 | ||
|
|
9de8c4cf04 | ||
|
|
40be148c9b | ||
|
|
dfbb563b6f | ||
|
|
ab76afb322 | ||
|
|
4b3593d081 | ||
|
|
210e182d7e | ||
|
|
6f1154a1f8 | ||
|
|
d935ff4b3e | ||
|
|
cd92d87c5d | ||
|
|
e8ac8ac0ab | ||
|
|
7ae7bdd670 | ||
|
|
1dbb89df34 | ||
|
|
b9908f68f9 | ||
|
|
b839e8183f | ||
|
|
51b370efa6 | ||
|
|
f65e67d86a | ||
|
|
653e42c2b9 | ||
|
|
3b6c8247c5 | ||
|
|
f457cc5107 | ||
|
|
f9e62f3237 | ||
|
|
4ca78a2fe5 | ||
|
|
179010ddbf | ||
|
|
4cca931d9d | ||
|
|
3c2743510b | ||
|
|
716434d59e | ||
|
|
4accbda497 | ||
|
|
9f0512e81e | ||
|
|
10b699a6f7 | ||
|
|
1a9b4cdbf5 | ||
|
|
ee8f133ebb | ||
|
|
b193625fb6 | ||
|
|
ca3abed605 | ||
|
|
3b2fd26bd9 | ||
|
|
1b21187397 | ||
|
|
c2519bdb7d | ||
|
|
797edc50f4 | ||
|
|
6ffbd88d92 | ||
|
|
377cd2ba1f | ||
|
|
14a67fa698 | ||
|
|
efe1c7ba45 | ||
|
|
96cf5f43d2 | ||
|
|
4ed461bc69 | ||
|
|
78fd2d5399 | ||
|
|
cf1d07e7a6 | ||
|
|
f71b940197 | ||
|
|
aae182a9f8 | ||
|
|
2162f52e00 | ||
|
|
7a88d2d08c | ||
|
|
896c4e7561 | ||
|
|
c537477d3a | ||
|
|
2a9adfd180 | ||
|
|
7a60cc25e9 | ||
|
|
73255c9c75 | ||
|
|
840af00d6d | ||
|
|
c3a58eec8d | ||
|
|
e18b8f6bb6 | ||
|
|
48d4e69fc8 | ||
|
|
c388eddc67 | ||
|
|
dcf5c2a0d6 | ||
|
|
7d070810d5 | ||
|
|
5923c88a94 | ||
|
|
a45770119c | ||
|
|
d3d3cda758 | ||
|
|
8ae157a272 | ||
|
|
8398db90ef | ||
|
|
177585c998 | ||
|
|
7ac9c34820 | ||
|
|
5e424bb64d | ||
|
|
d290b25f8a | ||
|
|
90c3ca47b9 | ||
|
|
924a931568 | ||
|
|
caed1a2f3c | ||
|
|
57f932851a | ||
|
|
cc956edf2d | ||
|
|
10f3a7193a | ||
|
|
f08865eedc | ||
|
|
cf67df3bf9 | ||
|
|
86c14a1d78 | ||
|
|
6411901200 | ||
|
|
af110337ce | ||
|
|
789b934996 | ||
|
|
9ec5c91a8d | ||
|
|
f89a901f6b | ||
|
|
063496e738 | ||
|
|
847fb4d52c | ||
|
|
3157de7e8b | ||
|
|
3cd0eaaf58 | ||
|
|
184aa59799 | ||
|
|
b6e88e713d | ||
|
|
09eabbf848 | ||
|
|
84d0e5c39e | ||
|
|
c63caa6015 | ||
|
|
b9cf079a85 | ||
|
|
ce4059a68e | ||
|
|
65a4ca8925 | ||
|
|
2a049433eb | ||
|
|
89ca42dabd | ||
|
|
12f6a324b9 | ||
|
|
f3d2ad55f6 | ||
|
|
62a911161d | ||
|
|
f4adfc4757 | ||
|
|
2de0dfb81c | ||
|
|
abe95a0876 | ||
|
|
0f0baf904f | ||
|
|
ef0eb6b047 | ||
|
|
323dd6b6f4 | ||
|
|
7edf8e4f3f | ||
|
|
b76980de14 | ||
|
|
a13b4bc514 | ||
|
|
cf58ee1d1f | ||
|
|
f7774f7ed0 | ||
|
|
8417e925a9 | ||
|
|
068d6dff6e | ||
|
|
f96dac147f | ||
|
|
9b212fa9e7 | ||
|
|
185e6093ed | ||
|
|
999499b840 | ||
|
|
0279af9f57 | ||
|
|
8305f1a8cb | ||
|
|
39019f03ed | ||
|
|
e0d4312cbe | ||
|
|
c0a364ef57 | ||
|
|
b1e5c3b361 | ||
|
|
fa9e955fc2 | ||
|
|
0504c35f2b | ||
|
|
4b8882f222 | ||
|
|
35e1a8e821 | ||
|
|
35be891910 | ||
|
|
2400556da3 | ||
|
|
09d3d26b45 | ||
|
|
a51fd1d35e | ||
|
|
dd4b388452 | ||
|
|
f6eb1e9652 | ||
|
|
6ab7a92184 | ||
|
|
5523cece62 | ||
|
|
3198f6172b | ||
|
|
ae2d07838c | ||
|
|
c81394fe66 | ||
|
|
389f9366e0 | ||
|
|
9921133d14 | ||
|
|
f751457313 | ||
|
|
33fe4d51e7 | ||
|
|
0d0b20e60e | ||
|
|
7e99be469d | ||
|
|
acdb26d476 | ||
|
|
7cc7e56f73 | ||
|
|
18bbe98cc6 | ||
|
|
3f042d71f2 | ||
|
|
5db9ca9786 | ||
|
|
b95eaf1aed | ||
|
|
9023e62fb4 | ||
|
|
45bab0cc00 | ||
|
|
70fb0c1c6b | ||
|
|
95ee8d101f | ||
|
|
fa91c4b4d5 | ||
|
|
2108cb8e1c | ||
|
|
e5d9a25cff | ||
|
|
c06cb8d9a7 | ||
|
|
da0f5f2bd1 | ||
|
|
8e9af90289 | ||
|
|
3f66d87a8a | ||
|
|
c019ef0a24 | ||
|
|
eac2e1548a | ||
|
|
2934a5517d | ||
|
|
4e1d3e9fe9 | ||
|
|
47e88ff055 | ||
|
|
9ee398af19 | ||
|
|
e369791f37 | ||
|
|
986abd5549 | ||
|
|
e61ec3e576 | ||
|
|
430fde9ea9 | ||
|
|
ad1c7450ab | ||
|
|
02f28eb87c | ||
|
|
b2645c50e7 | ||
|
|
1bf17d5514 | ||
|
|
36fb51705b | ||
|
|
668f99c37f | ||
|
|
a53c7ce08c | ||
|
|
1068f8a664 | ||
|
|
8c98e7fcd7 | ||
|
|
282749166a | ||
|
|
1f3d1fd344 | ||
|
|
1250a46274 | ||
|
|
e7a7c7b5ac | ||
|
|
be00a6fd60 | ||
|
|
0647fc3297 | ||
|
|
6c28ebe057 | ||
|
|
db0826d43a | ||
|
|
b1157a0615 | ||
|
|
e9830479af | ||
|
|
55cacc2a81 | ||
|
|
201fe59983 | ||
|
|
22a50571e6 | ||
|
|
1245ecf8fb | ||
|
|
312d4c1721 | ||
|
|
236a38f6c9 | ||
|
|
df8da24b5a | ||
|
|
9c28042fdd | ||
|
|
91310a803c | ||
|
|
a4aeb83a01 | ||
|
|
fc82976cf0 | ||
|
|
17adfeb553 | ||
|
|
41c82e1ea9 | ||
|
|
f51bcc2406 | ||
|
|
84eda99dc0 | ||
|
|
fa268fd092 | ||
|
|
9103ceeb1e | ||
|
|
224079674b | ||
|
|
2f13546b8b | ||
|
|
2ac8a45446 | ||
|
|
0673cf0bbb | ||
|
|
55dfb12358 | ||
|
|
27d3e9e6b7 | ||
|
|
a09d7d76d3 | ||
|
|
96adfa0b5a | ||
|
|
4ee7ff45e3 | ||
|
|
8334441f2d | ||
|
|
36ec17a06c | ||
|
|
2f47fc534d | ||
|
|
bd64e4a45b | ||
|
|
3743ff96ff | ||
|
|
932b92e67d | ||
|
|
770556aa74 | ||
|
|
26bc275f0f | ||
|
|
ae8dccf292 | ||
|
|
9e9a73efc6 | ||
|
|
94364f0dad | ||
|
|
635c82c316 | ||
|
|
e2bae8a61f | ||
|
|
93300a4932 | ||
|
|
613f3c5cbc | ||
|
|
04f55cd9c4 | ||
|
|
7f7d509212 | ||
|
|
cbbc0e5efa | ||
|
|
3208cec882 | ||
|
|
4b3cee7f43 | ||
|
|
705fd9971f | ||
|
|
ee58075d3f | ||
|
|
7ad6cd83db | ||
|
|
3f5307ef79 | ||
|
|
b244ee8580 | ||
|
|
16bcb65d94 | ||
|
|
1b9db42114 | ||
|
|
de63ff558d | ||
|
|
307daf2a7a | ||
|
|
5e551b4ffe | ||
|
|
f6316ca956 | ||
|
|
4d764b6bac | ||
|
|
1070260476 | ||
|
|
f839189121 | ||
|
|
a73d8053b1 | ||
|
|
395029489a | ||
|
|
1c99ffa3cc | ||
|
|
18178741c8 | ||
|
|
6bf9fc29e7 | ||
|
|
299d8bbc90 | ||
|
|
7c37123216 | ||
|
|
ab06ee6bd4 | ||
|
|
719928f5e5 | ||
|
|
0d55dc2b38 | ||
|
|
b229bf6197 | ||
|
|
35b47b83d7 | ||
|
|
85d6c3ac34 | ||
|
|
5847f0b16e | ||
|
|
cad907125b | ||
|
|
d989da2811 | ||
|
|
ed53b433b5 | ||
|
|
18a0c391f6 | ||
|
|
d78c4041a3 | ||
|
|
c62b65b012 | ||
|
|
6a9d026052 | ||
|
|
f562dc9e2a | ||
|
|
0fdb4197a1 | ||
|
|
39cbe8a858 | ||
|
|
ac29f7eeb0 | ||
|
|
6de37b5ed9 | ||
|
|
35163086f9 | ||
|
|
14ce62ed50 | ||
|
|
81736b77a3 | ||
|
|
26bb639b67 | ||
|
|
d56401c347 | ||
|
|
1213cf3140 | ||
|
|
e70e448b77 | ||
|
|
f0bff83fa0 | ||
|
|
249af016a0 | ||
|
|
c8a480b0e3 | ||
|
|
ccd96e67b1 | ||
|
|
9317b5b983 | ||
|
|
11e6a8838c | ||
|
|
82ad9cbf5e | ||
|
|
21386b924e | ||
|
|
87087c7eac | ||
|
|
8ccbe56282 | ||
|
|
6e5e974767 | ||
|
|
7c0987dc88 | ||
|
|
2f16882c1b | ||
|
|
0377232e1e | ||
|
|
51dd5c5900 | ||
|
|
4f75e5d631 | ||
|
|
3d116decbe | ||
|
|
12317d9649 | ||
|
|
ade7733d47 | ||
|
|
3ad814ebb7 | ||
|
|
f5798438ac | ||
|
|
cf21a20f05 | ||
|
|
13784db34d | ||
|
|
2fced1dd60 | ||
|
|
c54b019d55 | ||
|
|
fe1034ce1a | ||
|
|
2068556ca5 | ||
|
|
55d1283320 | ||
|
|
65aeddd717 | ||
|
|
6e24ee30e1 | ||
|
|
42394c9c56 | ||
|
|
e5b72c7072 | ||
|
|
befb151ea4 | ||
|
|
efbe5f946e | ||
|
|
c994b7232a | ||
|
|
6008e3da03 | ||
|
|
2d321bc27d | ||
|
|
0afd0205ed | ||
|
|
78aae759e4 | ||
|
|
b6b3216f55 | ||
|
|
bb671547d6 | ||
|
|
b561f6c8ae | ||
|
|
bc2a723e80 | ||
|
|
b506e45bee | ||
|
|
7df140d226 | ||
|
|
271008d748 | ||
|
|
d5759286fa | ||
|
|
7fc58666ad | ||
|
|
a95666ab1d | ||
|
|
be1d94909b | ||
|
|
a7442e9623 | ||
|
|
abfaf24627 | ||
|
|
a36b4207c8 | ||
|
|
13f6cfd601 | ||
|
|
75b69cad21 | ||
|
|
26326d8878 | ||
|
|
612b518198 | ||
|
|
6f04d9e8c0 | ||
|
|
940d2a8b45 | ||
|
|
4c3a6c0952 | ||
|
|
e4381c974f | ||
|
|
393a02f98b | ||
|
|
855a3925fa | ||
|
|
14274b20fd | ||
|
|
351e694448 | ||
|
|
dc3b069c23 | ||
|
|
60bae8253f | ||
|
|
30ad96322d | ||
|
|
079c84f2df | ||
|
|
50badac708 | ||
|
|
fdb9a351f4 | ||
|
|
b106126565 | ||
|
|
51823b1a96 | ||
|
|
6d0c18fce3 | ||
|
|
51c500fd83 | ||
|
|
9481587151 | ||
|
|
e6387c694e | ||
|
|
53f3af6944 | ||
|
|
901162c8b6 | ||
|
|
13be809cfe | ||
|
|
8857821716 | ||
|
|
0c873a1c02 | ||
|
|
74d2119f31 | ||
|
|
20a8296cd5 | ||
|
|
f42e0329da | ||
|
|
918ef57c78 | ||
|
|
1b93ece0de | ||
|
|
4bb6981cbb | ||
|
|
2878207170 | ||
|
|
ad41d265b3 | ||
|
|
df8d176a34 | ||
|
|
e764df0687 | ||
|
|
182590ef92 | ||
|
|
fdf0d63665 | ||
|
|
3ecc170fd2 | ||
|
|
d1a2786c7c | ||
|
|
e4db91e35b | ||
|
|
b861c4a7b4 | ||
|
|
ddb86da459 | ||
|
|
ac36618de8 | ||
|
|
8dadec6220 | ||
|
|
dd4402fca5 | ||
|
|
00e0e797b0 | ||
|
|
5038551165 | ||
|
|
1d677a7ea5 | ||
|
|
3781445e5d | ||
|
|
e1e060859b | ||
|
|
0cc59898b1 | ||
|
|
1660373153 | ||
|
|
a5b0626792 | ||
|
|
a0fa933430 | ||
|
|
43e8f31f6a | ||
|
|
42411038e4 | ||
|
|
164624786d | ||
|
|
945da8bf92 | ||
|
|
95eb7b86c9 | ||
|
|
e34662baec | ||
|
|
d93c76373c | ||
|
|
3fa9c22a04 | ||
|
|
2cde1a6e02 | ||
|
|
61b38b4405 | ||
|
|
5ed3650f7b | ||
|
|
b7ac58b31a | ||
|
|
c86d4e8060 | ||
|
|
cb2240c71d | ||
|
|
d4bffba010 | ||
|
|
aef118880a | ||
|
|
2f39f7f68b | ||
|
|
d67e22e57b | ||
|
|
96750cd04f | ||
|
|
5960aea37a | ||
|
|
a7b70cc947 | ||
|
|
54d9897048 | ||
|
|
296f31e7a1 | ||
|
|
0ad76be012 | ||
|
|
d1b4d135c8 | ||
|
|
268748f85f | ||
|
|
28068fb5d5 | ||
|
|
c0c7036ada | ||
|
|
68ce66e881 | ||
|
|
9b010ee633 | ||
|
|
8cf80c7bbf | ||
|
|
1038b4b349 | ||
|
|
a993e21387 | ||
|
|
46ee372c0f | ||
|
|
afd466b516 | ||
|
|
b6051d6fc2 | ||
|
|
a23c3424ba | ||
|
|
5ae371db18 | ||
|
|
0c255bae22 | ||
|
|
464d550f3b | ||
|
|
c86dd978b5 | ||
|
|
8a4f9ed33e | ||
|
|
039fbcd809 | ||
|
|
7a8d629a81 | ||
|
|
0816fc6682 | ||
|
|
163be1f1b5 | ||
|
|
f246df572b | ||
|
|
a3463a6ac1 | ||
|
|
265ec57efe | ||
|
|
c2b284175f | ||
|
|
c33d7e9ada | ||
|
|
755e758f75 | ||
|
|
5d1c13ce2b | ||
|
|
25bc37294b | ||
|
|
ec7a45db92 | ||
|
|
f2837b1b2d | ||
|
|
e95a4a9782 | ||
|
|
0a91fb66e3 | ||
|
|
5e71831019 | ||
|
|
e0d66a2346 | ||
|
|
5602d2fd62 | ||
|
|
6545a9343d | ||
|
|
0350355626 | ||
|
|
0aec1e1607 | ||
|
|
077cb583b6 | ||
|
|
c62e368cee | ||
|
|
cb1f366d35 | ||
|
|
acba29add9 | ||
|
|
29b0f3b1bf | ||
|
|
15c564fc61 | ||
|
|
17639e94da | ||
|
|
928545e7eb | ||
|
|
80a0669c69 | ||
|
|
7aee2f4211 | ||
|
|
a43ec422c0 | ||
|
|
4147bac094 | ||
|
|
16d9e1e34a | ||
|
|
7645d0d2c9 | ||
|
|
db31dbeb0e | ||
|
|
a84eac06d3 | ||
|
|
41e6f7d61e | ||
|
|
aa1d3c6943 | ||
|
|
f3fa8d4e73 | ||
|
|
55816acc99 | ||
|
|
8226d0a1ae | ||
|
|
4d4a55cd3a | ||
|
|
dd5dfdbabd | ||
|
|
7c49f58565 | ||
|
|
32218cff9f | ||
|
|
0b5b342542 | ||
|
|
19a57e8ecf | ||
|
|
a696c33bca | ||
|
|
b6a759ab29 | ||
|
|
a3acf34b39 | ||
|
|
3d1b4f0ee2 | ||
|
|
aa1cb313b8 | ||
|
|
1c5ee17d00 | ||
|
|
de3a409a1c | ||
|
|
19a4156b1c | ||
|
|
aad76bc9d9 | ||
|
|
25f94341ad | ||
|
|
cafead69f6 | ||
|
|
a359ebae02 | ||
|
|
c1abf5f07c | ||
|
|
fc5d98c05c | ||
|
|
9eb9365c89 | ||
|
|
be4cb4efc8 | ||
|
|
0c543908d3 | ||
|
|
3786ef90fe | ||
|
|
6ca847bdde | ||
|
|
bc43291a0c | ||
|
|
3b285f088f | ||
|
|
9e26882b5f | ||
|
|
a038b59ef4 | ||
|
|
ad073427b3 | ||
|
|
6bceab7488 | ||
|
|
3d617811b5 | ||
|
|
daa491cd17 | ||
|
|
4b4e6ca31a | ||
|
|
0c9977a92a | ||
|
|
8d90875e18 | ||
|
|
09872f6b26 | ||
|
|
6f0698bae2 | ||
|
|
414d24262b | ||
|
|
e424128a60 | ||
|
|
23ca857154 | ||
|
|
542b912090 | ||
|
|
7750a7bd58 | ||
|
|
2514a60fb3 | ||
|
|
8c8d4a129e | ||
|
|
a3f8abba1a | ||
|
|
e0dc8ea46d | ||
|
|
bc91487388 | ||
|
|
cc971e7e6d | ||
|
|
f485af3e21 | ||
|
|
edc8259b6f | ||
|
|
ca568cd3eb | ||
|
|
bac9dd1eec | ||
|
|
bb76c6c3da | ||
|
|
d71a4ba0bb | ||
|
|
4237dfc9d1 | ||
|
|
dc6ac391d7 | ||
|
|
d96b734639 | ||
|
|
4796fe4024 | ||
|
|
f37aedc3b2 | ||
|
|
5ec993f9f4 | ||
|
|
11791deec5 | ||
|
|
0861a7f303 | ||
|
|
dac6862b31 | ||
|
|
51bde261e5 | ||
|
|
7c9188ed03 | ||
|
|
090f72eedb | ||
|
|
780c35e804 | ||
|
|
f037a0eb17 | ||
|
|
834850fd98 | ||
|
|
e632b04b16 | ||
|
|
a7058a9e05 | ||
|
|
6bf8a6cbb3 | ||
|
|
acedaf27d9 | ||
|
|
9868a7d31e | ||
|
|
af51c48ede | ||
|
|
b5f812512f | ||
|
|
df2ceb3fe9 | ||
|
|
b1588d4553 | ||
|
|
f004a9c322 | ||
|
|
1652381444 | ||
|
|
ae2d996754 | ||
|
|
0a4839eaf7 | ||
|
|
85d51b2362 | ||
|
|
1a38f519f4 | ||
|
|
7bd87c4113 | ||
|
|
38f6f4ef2e | ||
|
|
8912004dcc | ||
|
|
be044dbcce | ||
|
|
2d89b6fc27 | ||
|
|
f265046676 | ||
|
|
515ea7e83d | ||
|
|
36a07290f2 | ||
|
|
0d0ec37edf | ||
|
|
66268b194c | ||
|
|
0377013d47 | ||
|
|
30a42907b5 | ||
|
|
52431e67a4 | ||
|
|
dabb7c370a | ||
|
|
71393a3cf7 | ||
|
|
c31a758435 | ||
|
|
0411b205ff | ||
|
|
8a754a5c89 | ||
|
|
accc439d75 | ||
|
|
271bd06176 | ||
|
|
872d2c09cd | ||
|
|
b4bc7cb709 | ||
|
|
e1889c1db0 | ||
|
|
997c095f35 | ||
|
|
6f7fd2fc7b | ||
|
|
2b1a73e04d | ||
|
|
8403b9c409 | ||
|
|
40c27f5139 | ||
|
|
6bed41b8c1 | ||
|
|
3cd8d94ceb | ||
|
|
c23fb7bfb2 | ||
|
|
54876e6db3 | ||
|
|
b46b2c1e8e | ||
|
|
bfffdbc4d8 | ||
|
|
fddd41f6b4 | ||
|
|
8759b245ea | ||
|
|
1a89a1b5f5 | ||
|
|
f80a0cbd07 | ||
|
|
9da753b915 | ||
|
|
5a8c9f5533 | ||
|
|
8a6ec093d3 | ||
|
|
bd434ff285 | ||
|
|
0571ec723d | ||
|
|
7545c83547 | ||
|
|
d66fadecda | ||
|
|
4d238b8ac6 | ||
|
|
02ef69f0f7 | ||
|
|
5584928489 | ||
|
|
67f0600702 | ||
|
|
3d9b3099f2 | ||
|
|
c11d56279f | ||
|
|
37ed800d6a | ||
|
|
35d7e1b32e | ||
|
|
5a6a14d1f3 | ||
|
|
8d35e7d3bd | ||
|
|
b678d848db | ||
|
|
d86b2c410f | ||
|
|
45bf7a234f | ||
|
|
b027eeb846 | ||
|
|
424df8c1ae | ||
|
|
d095b355cc | ||
|
|
70126748fd | ||
|
|
835e030c65 | ||
|
|
dbcd7442fe | ||
|
|
746361d495 | ||
|
|
8fd43e8cff | ||
|
|
8198872972 | ||
|
|
aae579c650 | ||
|
|
43c2bdf777 | ||
|
|
b7172da209 | ||
|
|
575f8335d6 | ||
|
|
a27be71b13 | ||
|
|
677bb041e9 | ||
|
|
190ae5a0bc | ||
|
|
bec12cdd55 | ||
|
|
1c8406ce3c | ||
|
|
eb69ddfef9 | ||
|
|
06db7c0641 | ||
|
|
542cfdf47d | ||
|
|
07b8e8fcf7 | ||
|
|
f3640446d2 | ||
|
|
0e74687910 | ||
|
|
417aeff21d | ||
|
|
46bc7d206d | ||
|
|
3c4cb60cda | ||
|
|
f05a61e8a8 | ||
|
|
181bc0c189 | ||
|
|
452c4ac89e | ||
|
|
8164e9712b | ||
|
|
a2c9fdb715 | ||
|
|
5c9b8addf3 | ||
|
|
e6f6c17c80 | ||
|
|
b8f9f23684 | ||
|
|
8f03f709fb | ||
|
|
6616f835af | ||
|
|
a7c29e4016 | ||
|
|
5adbd9519b | ||
|
|
d785f169c6 | ||
|
|
81252fd8bd | ||
|
|
02b80c2c6a | ||
|
|
7080ce72f6 | ||
|
|
133fdf03db | ||
|
|
8511821160 | ||
|
|
0c78f29907 | ||
|
|
cd10b56aa6 | ||
|
|
0531d0699a | ||
|
|
07dce5d474 | ||
|
|
bce85b6e45 | ||
|
|
f551b2c810 | ||
|
|
d2a1d7df44 | ||
|
|
df090d7970 | ||
|
|
dc278cc0e5 | ||
|
|
cdea0ff01b | ||
|
|
1a02837ada | ||
|
|
a59aba453f | ||
|
|
29f3ac9645 | ||
|
|
7c677f0fa1 | ||
|
|
362ab73026 | ||
|
|
fcdaf64268 | ||
|
|
fd4dcad80c | ||
|
|
3e3fd327d1 | ||
|
|
2e194361b3 | ||
|
|
92a852d28f | ||
|
|
f548f410b9 | ||
|
|
07f5b57f4c | ||
|
|
2c792975e0 | ||
|
|
0c771ca506 | ||
|
|
1ab691108e | ||
|
|
f0962e112a | ||
|
|
819e8bfecf | ||
|
|
d403bebfa0 | ||
|
|
a3ed07b3dc | ||
|
|
99e57ba888 |
143
.all-contributorsrc
Normal file
143
.all-contributorsrc
Normal file
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
104
.dockerignore
Normal file
104
.dockerignore
Normal file
@@ -0,0 +1,104 @@
|
||||
Dockerfile
|
||||
.vscode
|
||||
.github
|
||||
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
|
||||
# Firebase
|
||||
.firebase
|
||||
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# Nuxt generate
|
||||
dist
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# IDE / Editor
|
||||
.idea
|
||||
|
||||
# Service worker
|
||||
sw.*
|
||||
|
||||
# Mac OSX
|
||||
.DS_Store
|
||||
|
||||
# Vim swap files
|
||||
*.swp
|
||||
|
||||
# Postwoman build data
|
||||
.postwoman
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
# https://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
15
.env.example
Normal file
15
.env.example
Normal file
@@ -0,0 +1,15 @@
|
||||
# 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
|
||||
14
.firebaserc
Normal file
14
.firebaserc
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"projects": {
|
||||
"default": "postwoman-api"
|
||||
},
|
||||
"targets": {
|
||||
"postwoman-api": {
|
||||
"hosting": {
|
||||
"postwoman": [
|
||||
"postwoman"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
github: postwoman-io
|
||||
open_collective: postwoman
|
||||
patreon: liyasthomas
|
||||
custom: https://www.paypal.me/liyascthomas
|
||||
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
106
.gitignore
vendored
Normal file
106
.gitignore
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
|
||||
# Firebase
|
||||
.firebase
|
||||
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# Nuxt generate
|
||||
dist
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# IDE / Editor
|
||||
.idea
|
||||
|
||||
# Service worker
|
||||
sw.*
|
||||
|
||||
# Mac OSX
|
||||
.DS_Store
|
||||
|
||||
# Vim swap files
|
||||
*.swp
|
||||
|
||||
# Postwoman build data
|
||||
.postwoman
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
|
||||
# Tests screenshots
|
||||
tests/*/screenshots
|
||||
|
||||
# Tests videos
|
||||
tests/*/videos
|
||||
7
.prettierignore
Normal file
7
.prettierignore
Normal file
@@ -0,0 +1,7 @@
|
||||
.dependabot
|
||||
.github
|
||||
.nuxt
|
||||
.postwoman
|
||||
.vscode
|
||||
package-lock.json
|
||||
node_modules
|
||||
39
.travis.yml
39
.travis.yml
@@ -1,8 +1,39 @@
|
||||
# == 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:
|
||||
- "node"
|
||||
- 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
|
||||
env:
|
||||
- MY_VAR=EverythignIsAwesome
|
||||
- NODE_ENV=TEST
|
||||
80
404.html
80
404.html
@@ -1,80 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<script src="head.js"></script>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no">
|
||||
<title>mnmlurl - Minimal URL is a modern URL shortener with support for custom alias</title>
|
||||
<meta name="description" content="Minimal URL is a modern URL shortener with support for custom alias">
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<meta itemprop="name" content="mnmlurl">
|
||||
<meta itemprop="description" content="Minimal URL is a modern URL shortener with support for custom alias">
|
||||
<meta itemprop="image" content="icons/icon-192x192.png">
|
||||
<!-- See https://goo.gl/OOhYW5 -->
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<!-- See https://goo.gl/qRE0vM -->
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<!-- Add to homescreen for Chrome on Android. Fallback for manifest.json -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="application-name" content="mnmlurl">
|
||||
<!-- Add to homescreen for Safari on iOS -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="white-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="mnmlurl">
|
||||
<!-- Homescreen icons -->
|
||||
<link rel="apple-touch-icon" href="icons/icon-48x48.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="icons/icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="96x96" href="icons/icon-96x96.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="icons/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="icons/icon-192x192.png">
|
||||
<!-- Tile icon for Windows 8 (144x144 + tile color) -->
|
||||
<meta name="msapplication-TileImage" content="icons/icon-144x144.png">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<!-- OpenGraph -->
|
||||
<meta property="og:site_name" content="mnmlurl">
|
||||
<meta property="og:url" content="https://mnmlurl.ml">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="mnmlurl">
|
||||
<meta property="og:description" content="Minimal URL is a modern URL shortener with support for custom alias">
|
||||
<meta property="og:image" content="icons/icon-144x144.png">
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@liyasthomas">
|
||||
<meta name="twitter:creator" content="@liyasthomas">
|
||||
<meta name="twitter:url" content="https://mnmlurl.ml">
|
||||
<meta name="twitter:title" content="mnmlurl">
|
||||
<meta name="twitter:description" content="Minimal URL is a modern URL shortener with support for custom alias">
|
||||
<meta name="twitter:image" content="icons/icon-144x144.png">
|
||||
<!-- Web Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Poppins:700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="main">
|
||||
<div id="erbox">
|
||||
<h1>404</h1>
|
||||
page not found
|
||||
</div>
|
||||
<div class="flex"></div>
|
||||
<footer>
|
||||
<a href="https://mnmlurl.ml">
|
||||
<img src="icons/logo.svg" alt="logo" style="height: 24px; margin-right: 8px;">Home
|
||||
</a>
|
||||
<a href="https://github.com/liyasthomas/mnmlurl" target="_blank" rel="noopener">
|
||||
<img src="icons/github.svg" alt="GitHub" style="margin-right: 8px;">GitHub
|
||||
</a>
|
||||
<a href="https://github.com/liyasthomas/mnmlurl-extension" target="_blank" rel="noopener">
|
||||
Get browser extension
|
||||
</a>
|
||||
<button id="installPWA" onclick="installPWA()" style="padding: 16px;">
|
||||
<img src="icons/pwalogo.svg" alt="PWA" style="height: 16px;">
|
||||
</button>
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
726
CHANGELOG.md
726
CHANGELOG.md
@@ -1,38 +1,712 @@
|
||||
# Changelog
|
||||
# Title
|
||||
### Description by [Liyas Thomas](https://github.com/liyasthomas)
|
||||
|
||||
---
|
||||
## [v1.9.7](https://github.com/liyasthomas/postwoman/tree/v1.9.7) (2020-05-12)
|
||||
|
||||
# 1.0.0
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.5...v1.9.7)
|
||||
|
||||
## This is it, Title 1.0.0!
|
||||
We are finally out of beta, therefore many bugs were fixed and camera received a brand new look.
|
||||
**Implemented enhancements:**
|
||||
|
||||
* **NEW**: Camera redesign
|
||||
* **NEW**: Camera redesign
|
||||
* **NEW**: macOS and iOS support
|
||||
* **IMPROVEMENT**: Major improvements
|
||||
* **IMPROVEMENT**: Updated libraries
|
||||
* **FIX**: Fixed many bugs and crashes
|
||||
* **FIX**: Graphic glitches
|
||||
* **FIX**: Statusbar too dark
|
||||
* **TRANSLATION**: Updated translations
|
||||
* **REVERT**: Brought back the "Help" button
|
||||
* **OTHER**: Removed all analytics
|
||||
- Unable to use postwoman with latest docker image from docker hub [\#866](https://github.com/liyasthomas/postwoman/issues/866)
|
||||
- support response json array [\#805](https://github.com/liyasthomas/postwoman/issues/805)
|
||||
- Add Format Body option [\#767](https://github.com/liyasthomas/postwoman/issues/767)
|
||||
- Allow importing environment variables via Postman environment json files [\#759](https://github.com/liyasthomas/postwoman/issues/759)
|
||||
- Request headers kept same after changing request type [\#752](https://github.com/liyasthomas/postwoman/issues/752)
|
||||
- Add compatibility for postman collections & environments [\#746](https://github.com/liyasthomas/postwoman/issues/746)
|
||||
- Add GraphQL syntax highlighting [\#741](https://github.com/liyasthomas/postwoman/issues/741)
|
||||
- Add docker Images for all Tags [\#722](https://github.com/liyasthomas/postwoman/issues/722)
|
||||
- Provide post-build resources and default setting files [\#714](https://github.com/liyasthomas/postwoman/issues/714)
|
||||
- Theme lacks of contrast [\#709](https://github.com/liyasthomas/postwoman/issues/709)
|
||||
- Save collections on account sync turn on [\#679](https://github.com/liyasthomas/postwoman/issues/679)
|
||||
- Tests should be saved together with requests [\#643](https://github.com/liyasthomas/postwoman/issues/643)
|
||||
- npm modules of postwoman's vue components [\#384](https://github.com/liyasthomas/postwoman/issues/384)
|
||||
- \[request\] VS code extension [\#313](https://github.com/liyasthomas/postwoman/issues/313)
|
||||
- remove prerequest \<\< \>\> bindings when pre-request script is toggled off [\#234](https://github.com/liyasthomas/postwoman/issues/234)
|
||||
- Dynamic Headers [\#91](https://github.com/liyasthomas/postwoman/issues/91)
|
||||
- Allow importing environment variables via Postman environment json files [\#862](https://github.com/liyasthomas/postwoman/pull/862) ([sboulema](https://github.com/sboulema))
|
||||
- Add format body option [\#847](https://github.com/liyasthomas/postwoman/pull/847) ([sboulema](https://github.com/sboulema))
|
||||
- Save GraphQL Docs [\#846](https://github.com/liyasthomas/postwoman/pull/846) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- GraphQL Query Editor Syntax Highlighting [\#838](https://github.com/liyasthomas/postwoman/pull/838) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
---
|
||||
**Fixed bugs:**
|
||||
|
||||
# 0.9.0
|
||||
- Empty header in headers list results in SyntaxError: Failed to execute 'setRequestHeader' [\#765](https://github.com/liyasthomas/postwoman/issues/765)
|
||||
- Getting cannot read value of undefined [\#731](https://github.com/liyasthomas/postwoman/issues/731)
|
||||
- Environment variables in collections [\#642](https://github.com/liyasthomas/postwoman/issues/642)
|
||||
|
||||
## I worked a lot on Web apps, WebAR, WebGL & PWAs
|
||||
So I think Lvr is now ready to be released :)
|
||||
**Closed issues:**
|
||||
|
||||
I will keep the usual branch model.
|
||||
- Import/Export collections from private github repos to share among teams. [\#867](https://github.com/liyasthomas/postwoman/issues/867)
|
||||
- Access to nonexistent routes will not be redirect to page 404 [\#849](https://github.com/liyasthomas/postwoman/issues/849)
|
||||
- Error: Network Error. Check console for details. [\#827](https://github.com/liyasthomas/postwoman/issues/827)
|
||||
- 'Documentation Generated' response stacking past top of page if submit clicked enough times/fast enough [\#826](https://github.com/liyasthomas/postwoman/issues/826)
|
||||
- The UI could use some improvements [\#825](https://github.com/liyasthomas/postwoman/issues/825)
|
||||
- Postwoman won't build, produces 'FATAL Nuxt build error' [\#824](https://github.com/liyasthomas/postwoman/issues/824)
|
||||
- Improve contrast of UI components in all themes [\#819](https://github.com/liyasthomas/postwoman/issues/819)
|
||||
- Add an option to hide and/or collapse the right panel [\#818](https://github.com/liyasthomas/postwoman/issues/818)
|
||||
- Docker Cannot log in normally in the container [\#817](https://github.com/liyasthomas/postwoman/issues/817)
|
||||
- Body in Request [\#815](https://github.com/liyasthomas/postwoman/issues/815)
|
||||
- How to run postwoman under reverse proxy [\#812](https://github.com/liyasthomas/postwoman/issues/812)
|
||||
- Call local support [\#811](https://github.com/liyasthomas/postwoman/issues/811)
|
||||
- feature [\#810](https://github.com/liyasthomas/postwoman/issues/810)
|
||||
- socket binnery support [\#801](https://github.com/liyasthomas/postwoman/issues/801)
|
||||
- Ability to join and leave rooms in Socket.IO connection [\#796](https://github.com/liyasthomas/postwoman/issues/796)
|
||||
- How can I synchronize my data on local? [\#794](https://github.com/liyasthomas/postwoman/issues/794)
|
||||
- I cant login [\#792](https://github.com/liyasthomas/postwoman/issues/792)
|
||||
- Unresolved merge conflict in index.vue.orig [\#786](https://github.com/liyasthomas/postwoman/issues/786)
|
||||
- You send data my request to Google [\#780](https://github.com/liyasthomas/postwoman/issues/780)
|
||||
- I have question by \#750, it's closed,but my problem is still.... [\#770](https://github.com/liyasthomas/postwoman/issues/770)
|
||||
- Body scroll after modal is open [\#766](https://github.com/liyasthomas/postwoman/issues/766)
|
||||
- text.match is not a function [\#764](https://github.com/liyasthomas/postwoman/issues/764)
|
||||
- Request : Copy response headers [\#763](https://github.com/liyasthomas/postwoman/issues/763)
|
||||
- The accordion \(expand\) labels are out of place on mobile [\#762](https://github.com/liyasthomas/postwoman/issues/762)
|
||||
- why does the graphql case can't be saved? [\#761](https://github.com/liyasthomas/postwoman/issues/761)
|
||||
- Mobile responsiveness issues [\#760](https://github.com/liyasthomas/postwoman/issues/760)
|
||||
- Report abuse: liyasthomas/postwoman \(Contact Links\) [\#754](https://github.com/liyasthomas/postwoman/issues/754)
|
||||
- Report abuse: liyasthomas/postwoman \(Contact Links\) [\#753](https://github.com/liyasthomas/postwoman/issues/753)
|
||||
- I used it to post test,but response network error [\#750](https://github.com/liyasthomas/postwoman/issues/750)
|
||||
- Improve documentation on how to use environments [\#742](https://github.com/liyasthomas/postwoman/issues/742)
|
||||
- Broken link to translations branch [\#737](https://github.com/liyasthomas/postwoman/issues/737)
|
||||
- Postwoman raiase a connection error when communicate with localhost. [\#708](https://github.com/liyasthomas/postwoman/issues/708)
|
||||
- CORS issue when hosting [\#707](https://github.com/liyasthomas/postwoman/issues/707)
|
||||
- Add a description to the request or collection when saving it [\#706](https://github.com/liyasthomas/postwoman/issues/706)
|
||||
- Error: Network Error. Check console for details [\#704](https://github.com/liyasthomas/postwoman/issues/704)
|
||||
|
||||
* Stable release on `master` branch
|
||||
**Merged pull requests:**
|
||||
|
||||
---
|
||||
- chore\(deps\): bump @nuxtjs/sitemap from 2.2.1 to 2.3.0 [\#864](https://github.com/liyasthomas/postwoman/pull/864) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- docs: add sboulema as a contributor [\#863](https://github.com/liyasthomas/postwoman/pull/863) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.11.0 to 6.11.1 [\#861](https://github.com/liyasthomas/postwoman/pull/861) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Produce valid output when showing/copying as code [\#857](https://github.com/liyasthomas/postwoman/pull/857) ([Hydrophobefireman](https://github.com/Hydrophobefireman))
|
||||
- dotenv [\#856](https://github.com/liyasthomas/postwoman/pull/856) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Save Collections/Environments on enabling sync [\#854](https://github.com/liyasthomas/postwoman/pull/854) ([sboulema](https://github.com/sboulema))
|
||||
- chore\(deps\): bump firebase from 7.14.2 to 7.14.3 [\#853](https://github.com/liyasthomas/postwoman/pull/853) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Environment variables in collections [\#851](https://github.com/liyasthomas/postwoman/pull/851) ([sboulema](https://github.com/sboulema))
|
||||
- chore\(deps-dev\): bump node-sass from 4.14.0 to 4.14.1 [\#844](https://github.com/liyasthomas/postwoman/pull/844) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Remove not-deleted index.vue merge file [\#842](https://github.com/liyasthomas/postwoman/pull/842) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- chore\(deps\): remove stale dependency [\#839](https://github.com/liyasthomas/postwoman/pull/839) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.2.1 to 10.2.2 [\#837](https://github.com/liyasthomas/postwoman/pull/837) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(store\): better code structure [\#835](https://github.com/liyasthomas/postwoman/pull/835) ([jameslahm](https://github.com/jameslahm))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.10.2 to 5.10.3 [\#832](https://github.com/liyasthomas/postwoman/pull/832) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.10.1 to 6.11.0 [\#831](https://github.com/liyasthomas/postwoman/pull/831) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.2.0 to 10.2.1 [\#830](https://github.com/liyasthomas/postwoman/pull/830) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Add ability to navigate through message history [\#828](https://github.com/liyasthomas/postwoman/pull/828) ([jinyus](https://github.com/jinyus))
|
||||
- chore\(config\): delete render option [\#823](https://github.com/liyasthomas/postwoman/pull/823) ([jameslahm](https://github.com/jameslahm))
|
||||
- chore\(deps-dev\): bump cypress from 4.4.1 to 4.5.0 [\#822](https://github.com/liyasthomas/postwoman/pull/822) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.1.7 to 10.2.0 [\#821](https://github.com/liyasthomas/postwoman/pull/821) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Realtime SocketIO support for json user input [\#820](https://github.com/liyasthomas/postwoman/pull/820) ([feydan](https://github.com/feydan))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.10.1 to 5.10.2 [\#816](https://github.com/liyasthomas/postwoman/pull/816) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Modify responseType by Object\(json\) or Array\(json5\) [\#813](https://github.com/liyasthomas/postwoman/pull/813) ([shtakai](https://github.com/shtakai))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.9.2 to 6.10.1 [\#809](https://github.com/liyasthomas/postwoman/pull/809) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.14.1 to 7.14.2 [\#808](https://github.com/liyasthomas/postwoman/pull/808) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump node-sass from 4.13.1 to 4.14.0 [\#807](https://github.com/liyasthomas/postwoman/pull/807) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/sitemap from 2.2.0 to 2.2.1 [\#806](https://github.com/liyasthomas/postwoman/pull/806) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.10.0 to 5.10.1 [\#803](https://github.com/liyasthomas/postwoman/pull/803) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.9.1 to 6.9.2 [\#802](https://github.com/liyasthomas/postwoman/pull/802) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.1.6 to 10.1.7 [\#800](https://github.com/liyasthomas/postwoman/pull/800) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump prettier from 2.0.4 to 2.0.5 [\#799](https://github.com/liyasthomas/postwoman/pull/799) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.7 to 5.10.0 [\#798](https://github.com/liyasthomas/postwoman/pull/798) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 4.4.0 to 4.4.1 [\#797](https://github.com/liyasthomas/postwoman/pull/797) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Listen to all events in socket.io connection [\#795](https://github.com/liyasthomas/postwoman/pull/795) ([konradkalemba](https://github.com/konradkalemba))
|
||||
- Fix postman import with empty url [\#791](https://github.com/liyasthomas/postwoman/pull/791) ([Nikita240](https://github.com/Nikita240))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.1.5 to 10.1.6 [\#789](https://github.com/liyasthomas/postwoman/pull/789) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.1.3 to 10.1.5 [\#787](https://github.com/liyasthomas/postwoman/pull/787) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.14.0 to 7.14.1 [\#782](https://github.com/liyasthomas/postwoman/pull/782) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump yargs-parser from 18.1.2 to 18.1.3 [\#781](https://github.com/liyasthomas/postwoman/pull/781) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump start-server-and-test from 1.10.11 to 1.11.0 [\#778](https://github.com/liyasthomas/postwoman/pull/778) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.8.1 to 6.9.1 [\#776](https://github.com/liyasthomas/postwoman/pull/776) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump ace-builds from 1.4.9 to 1.4.11 [\#775](https://github.com/liyasthomas/postwoman/pull/775) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 4.3.0 to 4.4.0 [\#774](https://github.com/liyasthomas/postwoman/pull/774) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.13.2 to 7.14.0 [\#758](https://github.com/liyasthomas/postwoman/pull/758) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump husky from 4.2.3 to 4.2.5 [\#757](https://github.com/liyasthomas/postwoman/pull/757) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.1.2 to 10.1.3 [\#756](https://github.com/liyasthomas/postwoman/pull/756) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Fix indicator if extension is installed [\#748](https://github.com/liyasthomas/postwoman/pull/748) ([levrik](https://github.com/levrik))
|
||||
- Remove support for legacy extensions [\#747](https://github.com/liyasthomas/postwoman/pull/747) ([levrik](https://github.com/levrik))
|
||||
- Fix GQL introspection query not sent through extension [\#745](https://github.com/liyasthomas/postwoman/pull/745) ([levrik](https://github.com/levrik))
|
||||
- chore\(deps-dev\): bump prettier from 2.0.2 to 2.0.4 [\#744](https://github.com/liyasthomas/postwoman/pull/744) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/sitemap from 2.1.0 to 2.2.0 [\#743](https://github.com/liyasthomas/postwoman/pull/743) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.1.1 to 10.1.2 [\#740](https://github.com/liyasthomas/postwoman/pull/740) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.8.0 to 6.8.1 [\#736](https://github.com/liyasthomas/postwoman/pull/736) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump ace-builds from 1.4.8 to 1.4.9 [\#735](https://github.com/liyasthomas/postwoman/pull/735) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.13.1 to 7.13.2 [\#734](https://github.com/liyasthomas/postwoman/pull/734) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump vue-virtual-scroll-list from 1.4.6 to 1.4.7 [\#733](https://github.com/liyasthomas/postwoman/pull/733) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.7.2 to 6.8.0 [\#732](https://github.com/liyasthomas/postwoman/pull/732) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt from 2.12.1 to 2.12.2 [\#729](https://github.com/liyasthomas/postwoman/pull/729) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.7.1 to 6.7.2 [\#728](https://github.com/liyasthomas/postwoman/pull/728) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.1.0 to 10.1.1 [\#727](https://github.com/liyasthomas/postwoman/pull/727) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 4.2.0 to 4.3.0 [\#726](https://github.com/liyasthomas/postwoman/pull/726) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.0.10 to 10.1.0 [\#725](https://github.com/liyasthomas/postwoman/pull/725) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.7.0 to 6.7.1 [\#724](https://github.com/liyasthomas/postwoman/pull/724) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.6 to 5.9.7 [\#723](https://github.com/liyasthomas/postwoman/pull/723) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.0.9 to 10.0.10 [\#721](https://github.com/liyasthomas/postwoman/pull/721) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.5 to 5.9.6 [\#719](https://github.com/liyasthomas/postwoman/pull/719) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/sitemap from 2.0.1 to 2.1.0 [\#718](https://github.com/liyasthomas/postwoman/pull/718) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.13.0 to 7.13.1 [\#717](https://github.com/liyasthomas/postwoman/pull/717) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump yargs-parser from 18.1.1 to 18.1.2 [\#713](https://github.com/liyasthomas/postwoman/pull/713) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.6.1 to 6.7.0 [\#712](https://github.com/liyasthomas/postwoman/pull/712) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt from 2.12.0 to 2.12.1 [\#711](https://github.com/liyasthomas/postwoman/pull/711) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.12.0 to 7.13.0 [\#710](https://github.com/liyasthomas/postwoman/pull/710) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Updating the UI and style files [\#705](https://github.com/liyasthomas/postwoman/pull/705) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.0.8 to 10.0.9 [\#703](https://github.com/liyasthomas/postwoman/pull/703) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Improving performance [\#702](https://github.com/liyasthomas/postwoman/pull/702) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- :package: Updating packages [\#701](https://github.com/liyasthomas/postwoman/pull/701) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps-dev\): bump prettier from 1.19.1 to 2.0.1 [\#697](https://github.com/liyasthomas/postwoman/pull/697) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
|
||||
## Thanks
|
||||
* [Google](https://www.google.com) - for [Polymer](https://polymer-project.org)
|
||||
## [v1.9.5](https://github.com/liyasthomas/postwoman/tree/v1.9.5) (2020-03-22)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.0...v1.9.5)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Support preview of JSON:API's "application/vnd.api+json" Content-Type [\#694](https://github.com/liyasthomas/postwoman/issues/694)
|
||||
- Support SocketIO connections on Realtime page [\#611](https://github.com/liyasthomas/postwoman/issues/611)
|
||||
- Parameter list not showing JSON object fields \(force raw?\) [\#597](https://github.com/liyasthomas/postwoman/issues/597)
|
||||
- Add setting to disable scroll animations [\#592](https://github.com/liyasthomas/postwoman/issues/592)
|
||||
- Bigger URL and/or Path input field [\#581](https://github.com/liyasthomas/postwoman/issues/581)
|
||||
- \[request\] Offline cross-platform native build [\#267](https://github.com/liyasthomas/postwoman/issues/267)
|
||||
- On Save Update existing API [\#204](https://github.com/liyasthomas/postwoman/issues/204)
|
||||
- Import and export environments from JSON [\#190](https://github.com/liyasthomas/postwoman/issues/190)
|
||||
- Fast URL entry [\#62](https://github.com/liyasthomas/postwoman/issues/62)
|
||||
- Added the ability to prettify GraphQL queries [\#650](https://github.com/liyasthomas/postwoman/pull/650) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Updated messages for when GraphQL Get Schema fails [\#633](https://github.com/liyasthomas/postwoman/pull/633) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Ignore empty GQL Variable Strings [\#629](https://github.com/liyasthomas/postwoman/pull/629) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- test: purge travis [\#623](https://github.com/liyasthomas/postwoman/pull/623) ([yubathom](https://github.com/yubathom))
|
||||
- Added shortcut to quickly run the GraphQL query [\#620](https://github.com/liyasthomas/postwoman/pull/620) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- JSON linting in the code editor [\#605](https://github.com/liyasthomas/postwoman/pull/605) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- GraphQL page improvements, and more [\#602](https://github.com/liyasthomas/postwoman/pull/602) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Test script is not run after failing request [\#644](https://github.com/liyasthomas/postwoman/issues/644)
|
||||
- \[HELP\] Auth permission denied [\#621](https://github.com/liyasthomas/postwoman/issues/621)
|
||||
- Can't login on Brave Browser [\#607](https://github.com/liyasthomas/postwoman/issues/607)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- \[UI/UX\] - Change place of Send button [\#696](https://github.com/liyasthomas/postwoman/issues/696)
|
||||
- Report Portal integration [\#691](https://github.com/liyasthomas/postwoman/issues/691)
|
||||
- Docs request: how to prevent secrets from leaving local storage wrt. sync. [\#686](https://github.com/liyasthomas/postwoman/issues/686)
|
||||
- \[bug\] - Can't make a request to HTTP [\#676](https://github.com/liyasthomas/postwoman/issues/676)
|
||||
- Looking forward to that the postwoman Compatible 'swagger ' at next version [\#675](https://github.com/liyasthomas/postwoman/issues/675)
|
||||
- Error: Network Error. Check console for details. [\#673](https://github.com/liyasthomas/postwoman/issues/673)
|
||||
- \[Bug\] - Can't login to Github and Google [\#661](https://github.com/liyasthomas/postwoman/issues/661)
|
||||
- A question that has been raised but not resolved [\#658](https://github.com/liyasthomas/postwoman/issues/658)
|
||||
- An unknown error occurred whilst the proxy was processing your request. [\#656](https://github.com/liyasthomas/postwoman/issues/656)
|
||||
- Running app from downloaded zip fails to compile [\#651](https://github.com/liyasthomas/postwoman/issues/651)
|
||||
- Environment variable in path won't update [\#641](https://github.com/liyasthomas/postwoman/issues/641)
|
||||
- Info: The current domain is not authorized for OAuth operations Error [\#637](https://github.com/liyasthomas/postwoman/issues/637)
|
||||
- A suggestion for UI [\#635](https://github.com/liyasthomas/postwoman/issues/635)
|
||||
- How to use postwoman for local development and testing [\#634](https://github.com/liyasthomas/postwoman/issues/634)
|
||||
- How to debug localhost \(cors\) [\#630](https://github.com/liyasthomas/postwoman/issues/630)
|
||||
- Requests to local API returning error response [\#608](https://github.com/liyasthomas/postwoman/issues/608)
|
||||
- Why does the URL input field display only one line [\#604](https://github.com/liyasthomas/postwoman/issues/604)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add application/vnd.api+json [\#695](https://github.com/liyasthomas/postwoman/pull/695) ([allthesignals](https://github.com/allthesignals))
|
||||
- Fix raw input \(JSON\) [\#693](https://github.com/liyasthomas/postwoman/pull/693) ([leomp12](https://github.com/leomp12))
|
||||
- chore\(deps\): bump firebase from 7.11.0 to 7.12.0 [\#689](https://github.com/liyasthomas/postwoman/pull/689) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump vuefire from 2.2.1 to 2.2.2 [\#688](https://github.com/liyasthomas/postwoman/pull/688) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 4.1.0 to 4.2.0 [\#685](https://github.com/liyasthomas/postwoman/pull/685) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump start-server-and-test from 1.10.10 to 1.10.11 [\#684](https://github.com/liyasthomas/postwoman/pull/684) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.6.0 to 6.6.1 [\#683](https://github.com/liyasthomas/postwoman/pull/683) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt from 2.11.0 to 2.12.0 [\#682](https://github.com/liyasthomas/postwoman/pull/682) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Fix setting default raw params [\#681](https://github.com/liyasthomas/postwoman/pull/681) ([leomp12](https://github.com/leomp12))
|
||||
- Fix handling content type and raw input [\#678](https://github.com/liyasthomas/postwoman/pull/678) ([leomp12](https://github.com/leomp12))
|
||||
- \[Snyk\] Security upgrade yargs-parser from 18.1.0 to 18.1.1 [\#674](https://github.com/liyasthomas/postwoman/pull/674) ([snyk-bot](https://github.com/snyk-bot))
|
||||
- chore\(deps-dev\): bump start-server-and-test from 1.10.9 to 1.10.10 [\#672](https://github.com/liyasthomas/postwoman/pull/672) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.10.0 to 7.11.0 [\#671](https://github.com/liyasthomas/postwoman/pull/671) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ✅ Updating tests [\#669](https://github.com/liyasthomas/postwoman/pull/669) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Updating tests [\#668](https://github.com/liyasthomas/postwoman/pull/668) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- APIs [\#667](https://github.com/liyasthomas/postwoman/pull/667) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Insecure Websocket connection issue while connecting to MQTT broker. [\#666](https://github.com/liyasthomas/postwoman/pull/666) ([rahulnpadalkar](https://github.com/rahulnpadalkar))
|
||||
- Improving performance [\#664](https://github.com/liyasthomas/postwoman/pull/664) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Feature/mqtt [\#663](https://github.com/liyasthomas/postwoman/pull/663) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Added Support for MQTT [\#662](https://github.com/liyasthomas/postwoman/pull/662) ([rahulnpadalkar](https://github.com/rahulnpadalkar))
|
||||
- chore\(deps\): bump yargs-parser from 18.0.0 to 18.1.0 [\#660](https://github.com/liyasthomas/postwoman/pull/660) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump start-server-and-test from 1.10.8 to 1.10.9 [\#659](https://github.com/liyasthomas/postwoman/pull/659) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Added icon slot to tabs [\#657](https://github.com/liyasthomas/postwoman/pull/657) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Refactor/ui [\#655](https://github.com/liyasthomas/postwoman/pull/655) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- even [\#654](https://github.com/liyasthomas/postwoman/pull/654) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump yargs-parser from 17.0.0 to 18.0.0 [\#653](https://github.com/liyasthomas/postwoman/pull/653) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.9.3 to 7.10.0 [\#652](https://github.com/liyasthomas/postwoman/pull/652) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Add http/https support to socketio url valid [\#648](https://github.com/liyasthomas/postwoman/pull/648) ([moonrailgun](https://github.com/moonrailgun))
|
||||
- Refactor/ui [\#647](https://github.com/liyasthomas/postwoman/pull/647) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Even [\#646](https://github.com/liyasthomas/postwoman/pull/646) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Run tests even after failed request [\#645](https://github.com/liyasthomas/postwoman/pull/645) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Feature: add socket io support [\#640](https://github.com/liyasthomas/postwoman/pull/640) ([moonrailgun](https://github.com/moonrailgun))
|
||||
- Removed linting for the collection docs import editor [\#639](https://github.com/liyasthomas/postwoman/pull/639) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Moving or renaming files [\#638](https://github.com/liyasthomas/postwoman/pull/638) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Refactor/ui [\#636](https://github.com/liyasthomas/postwoman/pull/636) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Minor GraphQL page improvements [\#631](https://github.com/liyasthomas/postwoman/pull/631) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||
- chore\(deps-dev\): bump cypress from 4.0.2 to 4.1.0 [\#628](https://github.com/liyasthomas/postwoman/pull/628) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.5.0 to 6.6.0 [\#627](https://github.com/liyasthomas/postwoman/pull/627) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.9.1 to 7.9.3 [\#626](https://github.com/liyasthomas/postwoman/pull/626) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/google-tag-manager from 2.3.1 to 2.3.2 [\#625](https://github.com/liyasthomas/postwoman/pull/625) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- docs: add dmitryyankowski as a contributor [\#619](https://github.com/liyasthomas/postwoman/pull/619) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- Link multiple auth providers [\#618](https://github.com/liyasthomas/postwoman/pull/618) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Add --staged parameter to pretty-quick pre-commit [\#617](https://github.com/liyasthomas/postwoman/pull/617) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||
- :bug: FIxed URI not updating on Clear content, minor formData improve… [\#612](https://github.com/liyasthomas/postwoman/pull/612) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Update proxy information. [\#610](https://github.com/liyasthomas/postwoman/pull/610) ([NBTX](https://github.com/NBTX))
|
||||
- Fixed install extension toast appearing even when extension is installed [\#609](https://github.com/liyasthomas/postwoman/pull/609) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- chore\(deps-dev\): bump lint-staged from 10.0.7 to 10.0.8 [\#606](https://github.com/liyasthomas/postwoman/pull/606) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Added regex to handle url parts [\#603](https://github.com/liyasthomas/postwoman/pull/603) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||
- I18n [\#601](https://github.com/liyasthomas/postwoman/pull/601) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- feat\(i18n\): add Korean [\#600](https://github.com/liyasthomas/postwoman/pull/600) ([9j](https://github.com/9j))
|
||||
- Improve page load/unload experience \(remove FOUCs\) [\#599](https://github.com/liyasthomas/postwoman/pull/599) ([NBTX](https://github.com/NBTX))
|
||||
|
||||
## [v1.9.0](https://github.com/liyasthomas/postwoman/tree/v1.9.0) (2020-02-24)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.8.0...v1.9.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Disable SSL cert for websockets [\#557](https://github.com/liyasthomas/postwoman/issues/557)
|
||||
- Feature request: Keyboard shortcuts for folder creation [\#539](https://github.com/liyasthomas/postwoman/issues/539)
|
||||
- Friendly minded GraphQL [\#468](https://github.com/liyasthomas/postwoman/issues/468)
|
||||
- Environment management and configuration [\#147](https://github.com/liyasthomas/postwoman/issues/147)
|
||||
- POST request body editor reacts to the content type [\#594](https://github.com/liyasthomas/postwoman/pull/594) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- GraphQL Query Autocompletion [\#590](https://github.com/liyasthomas/postwoman/pull/590) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Postman collection parsing [\#574](https://github.com/liyasthomas/postwoman/pull/574) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||
- Added toggle to decide whether extensions should be used [\#551](https://github.com/liyasthomas/postwoman/pull/551) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Show Ctrl instead of Command for shortcuts non-Apple platforms [\#549](https://github.com/liyasthomas/postwoman/pull/549) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Updated GraphQL Query Variable Editor [\#534](https://github.com/liyasthomas/postwoman/pull/534) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Auto Theme Selection is kinda difficult to see [\#563](https://github.com/liyasthomas/postwoman/issues/563)
|
||||
- Can't send request to localhost via Chrome extention [\#560](https://github.com/liyasthomas/postwoman/issues/560)
|
||||
- Validation for duplicate collection ignores letter case [\#547](https://github.com/liyasthomas/postwoman/issues/547)
|
||||
- Multiple collections with the same name shouldn't exist [\#509](https://github.com/liyasthomas/postwoman/issues/509)
|
||||
- Build failed [\#327](https://github.com/liyasthomas/postwoman/issues/327)
|
||||
- Fixed typo in translation file for Auto theme [\#556](https://github.com/liyasthomas/postwoman/pull/556) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- don't run [\#577](https://github.com/liyasthomas/postwoman/issues/577)
|
||||
- Get correct response data but occurs with error "Cannot read property 'value' of undefined" [\#575](https://github.com/liyasthomas/postwoman/issues/575)
|
||||
- firebase\_app\_\_WEBPACK\_IMPORTED\_MODULE\_2\_\_\_default.a.firestore is not a function [\#558](https://github.com/liyasthomas/postwoman/issues/558)
|
||||
- relative module not found during start [\#552](https://github.com/liyasthomas/postwoman/issues/552)
|
||||
- Feature Request: Subfolders [\#540](https://github.com/liyasthomas/postwoman/issues/540)
|
||||
- Add max-height and overflow: auto to "parameter list" textarea [\#532](https://github.com/liyasthomas/postwoman/issues/532)
|
||||
- IE Support [\#386](https://github.com/liyasthomas/postwoman/issues/386)
|
||||
- ⏬ Import a Postman's Collection [\#333](https://github.com/liyasthomas/postwoman/issues/333)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Feature: Add prettier/pretty-quick formatting w/ Husky pre-commit [\#596](https://github.com/liyasthomas/postwoman/pull/596) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||
- Fix variablesJSONString store default for GraphQL page [\#593](https://github.com/liyasthomas/postwoman/pull/593) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||
- Refactor/lint [\#589](https://github.com/liyasthomas/postwoman/pull/589) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Even [\#588](https://github.com/liyasthomas/postwoman/pull/588) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump firebase from 7.9.0 to 7.9.1 [\#587](https://github.com/liyasthomas/postwoman/pull/587) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Even [\#586](https://github.com/liyasthomas/postwoman/pull/586) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump firebase from 7.8.2 to 7.9.0 [\#585](https://github.com/liyasthomas/postwoman/pull/585) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump vue-virtual-scroll-list from 1.4.5 to 1.4.6 [\#584](https://github.com/liyasthomas/postwoman/pull/584) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Adapt extension check to new extensions [\#583](https://github.com/liyasthomas/postwoman/pull/583) ([levrik](https://github.com/levrik))
|
||||
- Update link to extension repo in README [\#582](https://github.com/liyasthomas/postwoman/pull/582) ([levrik](https://github.com/levrik))
|
||||
- Even [\#579](https://github.com/liyasthomas/postwoman/pull/579) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Refactor/lint [\#578](https://github.com/liyasthomas/postwoman/pull/578) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump vue-virtual-scroll-list from 1.4.4 to 1.4.5 [\#576](https://github.com/liyasthomas/postwoman/pull/576) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Unify Chrome and Firefox extensions [\#573](https://github.com/liyasthomas/postwoman/pull/573) ([levrik](https://github.com/levrik))
|
||||
- fix: drop the toast which doesn't show up [\#572](https://github.com/liyasthomas/postwoman/pull/572) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- :sparkles: Native share + updated meta description [\#571](https://github.com/liyasthomas/postwoman/pull/571) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump firebase from 7.8.1 to 7.8.2 [\#570](https://github.com/liyasthomas/postwoman/pull/570) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 4.0.1 to 4.0.2 [\#569](https://github.com/liyasthomas/postwoman/pull/569) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Added create collection and save request syncs [\#568](https://github.com/liyasthomas/postwoman/pull/568) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||
- Moved common headers to a separate file [\#566](https://github.com/liyasthomas/postwoman/pull/566) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- chore\(deps-dev\): bump cypress from 4.0.0 to 4.0.1 [\#565](https://github.com/liyasthomas/postwoman/pull/565) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump yargs-parser from 16.1.0 to 17.0.0 [\#564](https://github.com/liyasthomas/postwoman/pull/564) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 3.8.3 to 4.0.0 [\#562](https://github.com/liyasthomas/postwoman/pull/562) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.8.0 to 7.8.1 [\#561](https://github.com/liyasthomas/postwoman/pull/561) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore: use typeof as an operator and make use of localizable strings [\#559](https://github.com/liyasthomas/postwoman/pull/559) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Support for Formdata [\#555](https://github.com/liyasthomas/postwoman/pull/555) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump @nuxtjs/pwa from 3.0.0-beta.19 to 3.0.0-beta.20 [\#554](https://github.com/liyasthomas/postwoman/pull/554) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.4 to 5.9.5 [\#553](https://github.com/liyasthomas/postwoman/pull/553) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- fix\(chore\): Take letter casing into account while checking for duplicate collection [\#548](https://github.com/liyasthomas/postwoman/pull/548) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- update e2e tests [\#546](https://github.com/liyasthomas/postwoman/pull/546) ([yubathom](https://github.com/yubathom))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.3 to 5.9.4 [\#545](https://github.com/liyasthomas/postwoman/pull/545) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump firebase from 7.7.0 to 7.8.0 [\#542](https://github.com/liyasthomas/postwoman/pull/542) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump graphql from 14.5.8 to 14.6.0 [\#541](https://github.com/liyasthomas/postwoman/pull/541) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- i18n [\#538](https://github.com/liyasthomas/postwoman/pull/538) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Modification of French translations [\#537](https://github.com/liyasthomas/postwoman/pull/537) ([thomasbnt](https://github.com/thomasbnt))
|
||||
- Even [\#535](https://github.com/liyasthomas/postwoman/pull/535) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Updating spanish translation [\#529](https://github.com/liyasthomas/postwoman/pull/529) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- even merge [\#528](https://github.com/liyasthomas/postwoman/pull/528) ([liyasthomas](https://github.com/liyasthomas))
|
||||
|
||||
## [v1.8.0](https://github.com/liyasthomas/postwoman/tree/v1.8.0) (2020-01-28)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.5.0...v1.8.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Sync collection with a cloud storage \(e.g: Google drive\) [\#507](https://github.com/liyasthomas/postwoman/issues/507)
|
||||
- Application contains many hard-coded strings that aren't translatable [\#488](https://github.com/liyasthomas/postwoman/issues/488)
|
||||
- ULR parsing and var auto creation [\#469](https://github.com/liyasthomas/postwoman/issues/469)
|
||||
- What about additional loaders: + Pug, TypeScript, SASS, material-vue ? [\#467](https://github.com/liyasthomas/postwoman/issues/467)
|
||||
- \[suggestion\] - Tests tab [\#465](https://github.com/liyasthomas/postwoman/issues/465)
|
||||
- cookie not found support [\#443](https://github.com/liyasthomas/postwoman/issues/443)
|
||||
- Feature Request: Consumer Driven Contract Testing [\#420](https://github.com/liyasthomas/postwoman/issues/420)
|
||||
- Feature Request: Support OAuth2/OIDC [\#337](https://github.com/liyasthomas/postwoman/issues/337)
|
||||
- Add DB cache [\#26](https://github.com/liyasthomas/postwoman/issues/26)
|
||||
- Auth [\#513](https://github.com/liyasthomas/postwoman/pull/513) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Support for Google Chrome Extension [\#512](https://github.com/liyasthomas/postwoman/pull/512) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- GraphQL query validation based on schema [\#508](https://github.com/liyasthomas/postwoman/pull/508) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Syntax Error marking in GraphQL query editor [\#505](https://github.com/liyasthomas/postwoman/pull/505) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Refactoring proxy handling to be done in strategies [\#500](https://github.com/liyasthomas/postwoman/pull/500) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Firefox Extension compatibility [\#494](https://github.com/liyasthomas/postwoman/pull/494) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Network Strategies [\#487](https://github.com/liyasthomas/postwoman/pull/487) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- chore\(oauth\): Added method signatures as per JSDoc conventions [\#486](https://github.com/liyasthomas/postwoman/pull/486) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- GraphQL Type Highlight and Links [\#479](https://github.com/liyasthomas/postwoman/pull/479) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Warn the user if name field was left blank while creating a new collection [\#515](https://github.com/liyasthomas/postwoman/issues/515)
|
||||
- GraphQL String variables are null [\#497](https://github.com/liyasthomas/postwoman/issues/497)
|
||||
- Post request body is empty [\#473](https://github.com/liyasthomas/postwoman/issues/473)
|
||||
- WebSocket page freezes when pasting long URL [\#471](https://github.com/liyasthomas/postwoman/issues/471)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Allow importing Postman collections [\#524](https://github.com/liyasthomas/postwoman/issues/524)
|
||||
- Request descriptions [\#511](https://github.com/liyasthomas/postwoman/issues/511)
|
||||
- Ability to run all requests of a folder/collection [\#498](https://github.com/liyasthomas/postwoman/issues/498)
|
||||
- Change import/export collection on requests page icon [\#495](https://github.com/liyasthomas/postwoman/issues/495)
|
||||
- import cURL error [\#477](https://github.com/liyasthomas/postwoman/issues/477)
|
||||
- move to postwoman org [\#475](https://github.com/liyasthomas/postwoman/issues/475)
|
||||
- Create standalone vue components of the request builder. [\#474](https://github.com/liyasthomas/postwoman/issues/474)
|
||||
- Enable running proxy as a backend for Request Capture [\#325](https://github.com/liyasthomas/postwoman/issues/325)
|
||||
- Label doesn't change when switching between collection requests [\#291](https://github.com/liyasthomas/postwoman/issues/291)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Refactor [\#543](https://github.com/liyasthomas/postwoman/pull/543) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Enhancements [\#531](https://github.com/liyasthomas/postwoman/pull/531) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Merge pull request \#530 from liyasthomas/feature/post-request-tests [\#530](https://github.com/liyasthomas/postwoman/pull/530) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Refactor [\#523](https://github.com/liyasthomas/postwoman/pull/523) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore\(deps\): bump nuxt-i18n from 6.4.1 to 6.5.0 [\#522](https://github.com/liyasthomas/postwoman/pull/522) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump v-tooltip from 2.0.2 to 2.0.3 [\#521](https://github.com/liyasthomas/postwoman/pull/521) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump cypress from 3.8.2 to 3.8.3 [\#520](https://github.com/liyasthomas/postwoman/pull/520) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Feature/post request tests [\#518](https://github.com/liyasthomas/postwoman/pull/518) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||
- Validations for edit and create collections activity [\#516](https://github.com/liyasthomas/postwoman/pull/516) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Validate duplicate collections [\#510](https://github.com/liyasthomas/postwoman/pull/510) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Lint and refactor [\#506](https://github.com/liyasthomas/postwoman/pull/506) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Merge pull request \#504 from liyasthomas/dependabot/npm\_and\_yarn/node-sass-4.13.1 [\#504](https://github.com/liyasthomas/postwoman/pull/504) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump @nuxtjs/axios from 5.9.2 to 5.9.3 [\#503](https://github.com/liyasthomas/postwoman/pull/503) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps-dev\): bump sass-loader from 8.0.1 to 8.0.2 [\#502](https://github.com/liyasthomas/postwoman/pull/502) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- chore\(deps\): bump ace-builds from 1.4.7 to 1.4.8 [\#501](https://github.com/liyasthomas/postwoman/pull/501) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- 💚 Fixed \#497 [\#499](https://github.com/liyasthomas/postwoman/pull/499) ([pushrbx](https://github.com/pushrbx))
|
||||
- Feat/firefox strategy [\#496](https://github.com/liyasthomas/postwoman/pull/496) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- i18n Japanese: Added new translations [\#492](https://github.com/liyasthomas/postwoman/pull/492) ([reefqi037](https://github.com/reefqi037))
|
||||
- Merge pull request \#491 from liyasthomas/i18n [\#491](https://github.com/liyasthomas/postwoman/pull/491) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Replaced hard-coded strings with localizable strings [\#490](https://github.com/liyasthomas/postwoman/pull/490) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- chore: Minor tweaks [\#485](https://github.com/liyasthomas/postwoman/pull/485) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Porting \(most of\) code to typescript [\#484](https://github.com/liyasthomas/postwoman/pull/484) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- ⬆️ Bump sass-loader from 8.0.0 to 8.0.1 [\#482](https://github.com/liyasthomas/postwoman/pull/482) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.2 to 2.2.3 [\#481](https://github.com/liyasthomas/postwoman/pull/481) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- OAuth 2.0/OIDC Access Token Retrieval Support [\#476](https://github.com/liyasthomas/postwoman/pull/476) ([reefqi037](https://github.com/reefqi037))
|
||||
|
||||
## [v1.5.0](https://github.com/liyasthomas/postwoman/tree/v1.5.0) (2020-01-04)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.0.0...v1.5.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Can WSDL be implemented, similar to SoapUI? [\#461](https://github.com/liyasthomas/postwoman/issues/461)
|
||||
- Raw Request Body should be supported to format the JSON string [\#446](https://github.com/liyasthomas/postwoman/issues/446)
|
||||
- multipart/form-data support [\#434](https://github.com/liyasthomas/postwoman/issues/434)
|
||||
- Ability to send Binary data using Postwoman [\#415](https://github.com/liyasthomas/postwoman/issues/415)
|
||||
- Custom request method [\#398](https://github.com/liyasthomas/postwoman/issues/398)
|
||||
- \[request\]: CLI possibilities [\#363](https://github.com/liyasthomas/postwoman/issues/363)
|
||||
- Feature request: OAuth header support/integration [\#358](https://github.com/liyasthomas/postwoman/issues/358)
|
||||
- i18n support [\#348](https://github.com/liyasthomas/postwoman/issues/348)
|
||||
- Ability to connect to a MQTT broker [\#342](https://github.com/liyasthomas/postwoman/issues/342)
|
||||
- Separate layers in dockerfile to improve image build [\#339](https://github.com/liyasthomas/postwoman/issues/339)
|
||||
- Internal server environment usage requirements [\#336](https://github.com/liyasthomas/postwoman/issues/336)
|
||||
- Server Sent Events [\#329](https://github.com/liyasthomas/postwoman/issues/329)
|
||||
- Generate API documentation [\#326](https://github.com/liyasthomas/postwoman/issues/326)
|
||||
- Auth info on WebSocket connections [\#321](https://github.com/liyasthomas/postwoman/issues/321)
|
||||
- Set response panel to fullscreen [\#320](https://github.com/liyasthomas/postwoman/issues/320)
|
||||
- Graphql support [\#312](https://github.com/liyasthomas/postwoman/issues/312)
|
||||
- Keyboard shortcuts [\#302](https://github.com/liyasthomas/postwoman/issues/302)
|
||||
- File/binary request body support [\#298](https://github.com/liyasthomas/postwoman/issues/298)
|
||||
- It's possible to tab into read only and non-form elements [\#287](https://github.com/liyasthomas/postwoman/issues/287)
|
||||
- Change cursor to disabled on disabled inputs [\#286](https://github.com/liyasthomas/postwoman/issues/286)
|
||||
- Hover Styling on Inputs [\#285](https://github.com/liyasthomas/postwoman/issues/285)
|
||||
- Focus Styles on Buttons [\#284](https://github.com/liyasthomas/postwoman/issues/284)
|
||||
- Missing Focus on Inputs [\#279](https://github.com/liyasthomas/postwoman/issues/279)
|
||||
- Download the request result into a file. [\#278](https://github.com/liyasthomas/postwoman/issues/278)
|
||||
- Improve UI Contrast [\#277](https://github.com/liyasthomas/postwoman/issues/277)
|
||||
- \[UI\] \[UX\] Allow app to take width of browser [\#236](https://github.com/liyasthomas/postwoman/issues/236)
|
||||
- Extend syntax highlighting with ACE for pre-request script textarea [\#235](https://github.com/liyasthomas/postwoman/issues/235)
|
||||
- Store pre-request scripts in history [\#233](https://github.com/liyasthomas/postwoman/issues/233)
|
||||
- Store the time spent on fetching a response [\#225](https://github.com/liyasthomas/postwoman/issues/225)
|
||||
- Cache view [\#188](https://github.com/liyasthomas/postwoman/issues/188)
|
||||
- chore: stick to Vue.js best practices [\#432](https://github.com/liyasthomas/postwoman/pull/432) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Header key autocompletion [\#421](https://github.com/liyasthomas/postwoman/pull/421) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Custom methods support [\#400](https://github.com/liyasthomas/postwoman/pull/400) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Two Way Data Binding \(v-model\) to Ace Editor component [\#379](https://github.com/liyasthomas/postwoman/pull/379) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Basic i18n support [\#351](https://github.com/liyasthomas/postwoman/pull/351) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Undo header/param/body param deletion [\#350](https://github.com/liyasthomas/postwoman/pull/350) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Added ability to run GraphQL queries [\#346](https://github.com/liyasthomas/postwoman/pull/346) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- API Documentation won't be generated [\#456](https://github.com/liyasthomas/postwoman/issues/456)
|
||||
- Sharing Requests via link is not working [\#435](https://github.com/liyasthomas/postwoman/issues/435)
|
||||
- URL input text is so stutters [\#412](https://github.com/liyasthomas/postwoman/issues/412)
|
||||
- Save to collections after deleting all the collections causes an error page [\#390](https://github.com/liyasthomas/postwoman/issues/390)
|
||||
- Bearer token doesn’t work with CORS when only authorization header is allowed [\#353](https://github.com/liyasthomas/postwoman/issues/353)
|
||||
- Make the UI more compact [\#314](https://github.com/liyasthomas/postwoman/issues/314)
|
||||
- Allow reserved characters on websocket URI [\#289](https://github.com/liyasthomas/postwoman/issues/289)
|
||||
- Unable to edit or delete row in history table [\#281](https://github.com/liyasthomas/postwoman/issues/281)
|
||||
- Allow url request with `/` at eol [\#275](https://github.com/liyasthomas/postwoman/issues/275)
|
||||
- \[request\] localhost support [\#274](https://github.com/liyasthomas/postwoman/issues/274)
|
||||
- Code generation for Fetch request type of some methods \(POST, PUT, PATCH\) won't be shown [\#268](https://github.com/liyasthomas/postwoman/issues/268)
|
||||
- \[BUG\] \[UI\] \[Mobile\] Get results not scrollable. [\#266](https://github.com/liyasthomas/postwoman/issues/266)
|
||||
- POSTing large raw JSON packets [\#265](https://github.com/liyasthomas/postwoman/issues/265)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Module not found: Error: Can't resolve '../.postwoman/version.json' [\#457](https://github.com/liyasthomas/postwoman/issues/457)
|
||||
- \* ../.postwoman/version.json in ./node\_modules/babel-loader/lib??ref--2-0!./node\_modules/vue-loader/lib??vue-loader-options!./layouts/default.vue?vue&type=script&lang=js& friendly-errors 11:12:37 [\#448](https://github.com/liyasthomas/postwoman/issues/448)
|
||||
- npm run dev module was not found: ../.postwoman/version.json [\#442](https://github.com/liyasthomas/postwoman/issues/442)
|
||||
- graphql and websocket work, but http and https do not [\#441](https://github.com/liyasthomas/postwoman/issues/441)
|
||||
- Can I test localhost? [\#433](https://github.com/liyasthomas/postwoman/issues/433)
|
||||
- No 'Access-Control-Allow-Origin' [\#426](https://github.com/liyasthomas/postwoman/issues/426)
|
||||
- When uninstall the PWA the "install PWA" link in postwoman.io isn't appear anymore [\#419](https://github.com/liyasthomas/postwoman/issues/419)
|
||||
- Toggling options will reset the UI to English [\#417](https://github.com/liyasthomas/postwoman/issues/417)
|
||||
- Oh my dear god why don't we just wrap it in electron [\#413](https://github.com/liyasthomas/postwoman/issues/413)
|
||||
- UI improvement suggestion for request method drop down [\#409](https://github.com/liyasthomas/postwoman/issues/409)
|
||||
- Can I share a request with my team? [\#408](https://github.com/liyasthomas/postwoman/issues/408)
|
||||
- Does it not support the post method? [\#403](https://github.com/liyasthomas/postwoman/issues/403)
|
||||
- Post can't send request [\#401](https://github.com/liyasthomas/postwoman/issues/401)
|
||||
- \[Bug\] fix header icons overlap [\#399](https://github.com/liyasthomas/postwoman/issues/399)
|
||||
- Improve translate for pt-BR \(i18n\) [\#395](https://github.com/liyasthomas/postwoman/issues/395)
|
||||
- Raw input disabled is not working properly [\#394](https://github.com/liyasthomas/postwoman/issues/394)
|
||||
- Input area is not clearly to identify for users because the dark mode [\#393](https://github.com/liyasthomas/postwoman/issues/393)
|
||||
- \[UX\] Setting to make sidebar buttons small [\#389](https://github.com/liyasthomas/postwoman/issues/389)
|
||||
- \[UX\] Improve responsive breaking points [\#388](https://github.com/liyasthomas/postwoman/issues/388)
|
||||
- \[UX\] Hide history/collections [\#387](https://github.com/liyasthomas/postwoman/issues/387)
|
||||
- Clearing shortcut overrides browser default [\#374](https://github.com/liyasthomas/postwoman/issues/374)
|
||||
- Proxy server default configuration [\#373](https://github.com/liyasthomas/postwoman/issues/373)
|
||||
- Intent to translate [\#367](https://github.com/liyasthomas/postwoman/issues/367)
|
||||
- Static builds on releases [\#352](https://github.com/liyasthomas/postwoman/issues/352)
|
||||
- fix:SSE onclose handle [\#349](https://github.com/liyasthomas/postwoman/issues/349)
|
||||
- \[Request\] Use responses for next request? [\#324](https://github.com/liyasthomas/postwoman/issues/324)
|
||||
- Make response body area expandable [\#294](https://github.com/liyasthomas/postwoman/issues/294)
|
||||
- Mobile can't see console for request errors [\#283](https://github.com/liyasthomas/postwoman/issues/283)
|
||||
- Duplicated query string in generated code [\#272](https://github.com/liyasthomas/postwoman/issues/272)
|
||||
- Query parameters are duplicated [\#271](https://github.com/liyasthomas/postwoman/issues/271)
|
||||
- Generated code is incorrect [\#269](https://github.com/liyasthomas/postwoman/issues/269)
|
||||
- Lacking documentation and wiki [\#232](https://github.com/liyasthomas/postwoman/issues/232)
|
||||
- I can't send POST method [\#210](https://github.com/liyasthomas/postwoman/issues/210)
|
||||
- Handling request failures when build number is obtained from GitHub [\#122](https://github.com/liyasthomas/postwoman/issues/122)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- ⬆️ Bump cypress from 3.8.1 to 3.8.2 [\#483](https://github.com/liyasthomas/postwoman/pull/483) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/axios from 5.9.0 to 5.9.2 [\#472](https://github.com/liyasthomas/postwoman/pull/472) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Added variables to graphql page. [\#464](https://github.com/liyasthomas/postwoman/pull/464) ([pushrbx](https://github.com/pushrbx))
|
||||
- i18n [\#463](https://github.com/liyasthomas/postwoman/pull/463) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- ⬆️ Bump cypress from 3.8.0 to 3.8.1 [\#460](https://github.com/liyasthomas/postwoman/pull/460) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- i18n\(de-DE\): improve some translations [\#459](https://github.com/liyasthomas/postwoman/pull/459) ([gabschne](https://github.com/gabschne))
|
||||
- bn-BD i18n [\#455](https://github.com/liyasthomas/postwoman/pull/455) ([hmtanbir](https://github.com/hmtanbir))
|
||||
- API documentation page [\#451](https://github.com/liyasthomas/postwoman/pull/451) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- ⬆️ Bump @nuxtjs/axios from 5.8.0 to 5.9.0 [\#450](https://github.com/liyasthomas/postwoman/pull/450) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump nuxt from 2.10.2 to 2.11.0 [\#449](https://github.com/liyasthomas/postwoman/pull/449) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Various UI tweaks [\#439](https://github.com/liyasthomas/postwoman/pull/439) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- i18n [\#438](https://github.com/liyasthomas/postwoman/pull/438) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Burmese translation added [\#437](https://github.com/liyasthomas/postwoman/pull/437) ([ZattWine](https://github.com/ZattWine))
|
||||
- Styled select input [\#431](https://github.com/liyasthomas/postwoman/pull/431) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Bumped dependencies and Improved UI contrast [\#430](https://github.com/liyasthomas/postwoman/pull/430) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- ⬆️ Bump cypress from 3.7.0 to 3.8.0 [\#429](https://github.com/liyasthomas/postwoman/pull/429) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- I18n [\#428](https://github.com/liyasthomas/postwoman/pull/428) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- I18n Japanese translation added [\#427](https://github.com/liyasthomas/postwoman/pull/427) ([reefqi037](https://github.com/reefqi037))
|
||||
- Even [\#424](https://github.com/liyasthomas/postwoman/pull/424) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- I18n [\#423](https://github.com/liyasthomas/postwoman/pull/423) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- I18n German translation added [\#422](https://github.com/liyasthomas/postwoman/pull/422) ([NJannasch](https://github.com/NJannasch))
|
||||
- Update id-ID.js [\#416](https://github.com/liyasthomas/postwoman/pull/416) ([williamsp](https://github.com/williamsp))
|
||||
- Improving translation for id-ID [\#414](https://github.com/liyasthomas/postwoman/pull/414) ([williamsp](https://github.com/williamsp))
|
||||
- Fixing bug on request saving [\#410](https://github.com/liyasthomas/postwoman/pull/410) ([adevr](https://github.com/adevr))
|
||||
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.1 to 2.2.2 [\#407](https://github.com/liyasthomas/postwoman/pull/407) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump vue-virtual-scroll-list from 1.4.3 to 1.4.4 [\#406](https://github.com/liyasthomas/postwoman/pull/406) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump nuxt-i18n from 6.4.0 to 6.4.1 [\#405](https://github.com/liyasthomas/postwoman/pull/405) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- I18n [\#404](https://github.com/liyasthomas/postwoman/pull/404) ([yubathom](https://github.com/yubathom))
|
||||
- App UI [\#391](https://github.com/liyasthomas/postwoman/pull/391) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- i18n [\#383](https://github.com/liyasthomas/postwoman/pull/383) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Added Turkish Language Support [\#382](https://github.com/liyasthomas/postwoman/pull/382) ([AliAnilKocak](https://github.com/AliAnilKocak))
|
||||
- Translated new words to Farsi lang [\#380](https://github.com/liyasthomas/postwoman/pull/380) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||
- fix: twitter summary card image url [\#378](https://github.com/liyasthomas/postwoman/pull/378) ([peterpeterparker](https://github.com/peterpeterparker))
|
||||
- Added nav shortcuts to GraphQL query and response, updated GraphQL shortcut icons [\#377](https://github.com/liyasthomas/postwoman/pull/377) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Bump cypress from 3.6.1 to 3.7.0 [\#376](https://github.com/liyasthomas/postwoman/pull/376) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Bump vuex-persist from 2.1.1 to 2.2.0 [\#375](https://github.com/liyasthomas/postwoman/pull/375) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- I18n [\#372](https://github.com/liyasthomas/postwoman/pull/372) ([EdikWang](https://github.com/EdikWang))
|
||||
- Use GraphQL logo for GraphQL tab [\#371](https://github.com/liyasthomas/postwoman/pull/371) ([NBTX](https://github.com/NBTX))
|
||||
- Update i18n keywords for Bahasa Indonesia [\#369](https://github.com/liyasthomas/postwoman/pull/369) ([wahwahid](https://github.com/wahwahid))
|
||||
- Intent to translate to Spanish on I18n [\#368](https://github.com/liyasthomas/postwoman/pull/368) ([adlpaf](https://github.com/adlpaf))
|
||||
- I18n [\#366](https://github.com/liyasthomas/postwoman/pull/366) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Add translations for FR/EN catalogues [\#364](https://github.com/liyasthomas/postwoman/pull/364) ([LaurentBrieu](https://github.com/LaurentBrieu))
|
||||
- Added Bahasa Indonesia language support [\#362](https://github.com/liyasthomas/postwoman/pull/362) ([wahwahid](https://github.com/wahwahid))
|
||||
- i18n [\#361](https://github.com/liyasthomas/postwoman/pull/361) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- Add Simplified Chinese language [\#360](https://github.com/liyasthomas/postwoman/pull/360) ([EdikWang](https://github.com/EdikWang))
|
||||
- Added Brazilian Portuguese language support [\#359](https://github.com/liyasthomas/postwoman/pull/359) ([tetri](https://github.com/tetri))
|
||||
- Added Farsi language support [\#357](https://github.com/liyasthomas/postwoman/pull/357) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||
- Adding french language basic [\#355](https://github.com/liyasthomas/postwoman/pull/355) ([thomasbnt](https://github.com/thomasbnt))
|
||||
- Add Proxy URL option [\#345](https://github.com/liyasthomas/postwoman/pull/345) ([NBTX](https://github.com/NBTX))
|
||||
- ♻️ Refactor Functions [\#344](https://github.com/liyasthomas/postwoman/pull/344) ([athul](https://github.com/athul))
|
||||
|
||||
## [v1.0.0](https://github.com/liyasthomas/postwoman/tree/v1.0.0) (2019-11-04)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v0.1.0...v1.0.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Implement pre-request and post-request scripts \(and request chaining\) [\#218](https://github.com/liyasthomas/postwoman/issues/218)
|
||||
- Chain requests. Execute a bunch of requests one by one and produce results [\#196](https://github.com/liyasthomas/postwoman/issues/196)
|
||||
- Allow User to Choose Whether to Include Authentication in Permalink [\#178](https://github.com/liyasthomas/postwoman/issues/178)
|
||||
- Allow HTTP \(not HTTPS\) on postwoman.io [\#175](https://github.com/liyasthomas/postwoman/issues/175)
|
||||
- Docker-compose in development [\#168](https://github.com/liyasthomas/postwoman/issues/168)
|
||||
- Add Docker [\#164](https://github.com/liyasthomas/postwoman/issues/164)
|
||||
- Clear Input [\#155](https://github.com/liyasthomas/postwoman/issues/155)
|
||||
- introduce some script language to parse the response and pass environment variable as request parameter [\#139](https://github.com/liyasthomas/postwoman/issues/139)
|
||||
- Add links to the footer version and commit sha [\#134](https://github.com/liyasthomas/postwoman/issues/134)
|
||||
- Please add a label for each request. It will be helpful. [\#133](https://github.com/liyasthomas/postwoman/issues/133)
|
||||
- Use 'icon buttons' instead of 'text buttons' [\#130](https://github.com/liyasthomas/postwoman/issues/130)
|
||||
- Change .editorconfig [\#115](https://github.com/liyasthomas/postwoman/issues/115)
|
||||
- \[UX\] Provide Focus State for Buttons, etc. [\#112](https://github.com/liyasthomas/postwoman/issues/112)
|
||||
- Add linter semistandard [\#98](https://github.com/liyasthomas/postwoman/issues/98)
|
||||
- Show "Send" button all over the page or enable hotkeys [\#94](https://github.com/liyasthomas/postwoman/issues/94)
|
||||
- Import request from cURL [\#93](https://github.com/liyasthomas/postwoman/issues/93)
|
||||
- Search on History [\#92](https://github.com/liyasthomas/postwoman/issues/92)
|
||||
- Add support for "application/hal+json" Content-Type [\#88](https://github.com/liyasthomas/postwoman/issues/88)
|
||||
- The query string is built incorrectly when the path contains a parameter [\#87](https://github.com/liyasthomas/postwoman/issues/87)
|
||||
- Option to Copy request as Fetch or XHR Or CURL [\#76](https://github.com/liyasthomas/postwoman/issues/76)
|
||||
- Add Tests [\#65](https://github.com/liyasthomas/postwoman/issues/65)
|
||||
- Request Headers [\#57](https://github.com/liyasthomas/postwoman/issues/57)
|
||||
- Colored response codes based on status code [\#46](https://github.com/liyasthomas/postwoman/issues/46)
|
||||
- Improve SEO [\#45](https://github.com/liyasthomas/postwoman/issues/45)
|
||||
- Add html preview to response section [\#41](https://github.com/liyasthomas/postwoman/issues/41)
|
||||
- websocket support [\#40](https://github.com/liyasthomas/postwoman/issues/40)
|
||||
- Raw request body for POST requests and Authorization key/value in Header [\#36](https://github.com/liyasthomas/postwoman/issues/36)
|
||||
- Code highlight on response body [\#33](https://github.com/liyasthomas/postwoman/issues/33)
|
||||
- Template selector [\#32](https://github.com/liyasthomas/postwoman/issues/32)
|
||||
- Vue template [\#31](https://github.com/liyasthomas/postwoman/issues/31)
|
||||
- Add copy response to clipboard button [\#30](https://github.com/liyasthomas/postwoman/issues/30)
|
||||
- Ability to store/share/create collections [\#29](https://github.com/liyasthomas/postwoman/issues/29)
|
||||
- Send request on Enter Key press [\#17](https://github.com/liyasthomas/postwoman/issues/17)
|
||||
- Readable [\#5](https://github.com/liyasthomas/postwoman/issues/5)
|
||||
- Serialize a request into JSON? [\#4](https://github.com/liyasthomas/postwoman/issues/4)
|
||||
- Add brand new logo to the project [\#244](https://github.com/liyasthomas/postwoman/pull/244) ([caneco](https://github.com/caneco))
|
||||
- Feature/pre request script [\#231](https://github.com/liyasthomas/postwoman/pull/231) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||
- Add the ApolloTV proxy server [\#217](https://github.com/liyasthomas/postwoman/pull/217) ([NBTX](https://github.com/NBTX))
|
||||
- bug: keeping information on page change [\#211](https://github.com/liyasthomas/postwoman/pull/211) ([breno-pereira](https://github.com/breno-pereira))
|
||||
- Work in Progress: feature/allow-collections-importing [\#209](https://github.com/liyasthomas/postwoman/pull/209) ([vlad0337187](https://github.com/vlad0337187))
|
||||
- Feature/log errors [\#207](https://github.com/liyasthomas/postwoman/pull/207) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||
- Use returned value from toggle component on change event [\#205](https://github.com/liyasthomas/postwoman/pull/205) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||
- Fix CORS and mixed content issue [\#199](https://github.com/liyasthomas/postwoman/pull/199) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||
- Added Tooltips [\#197](https://github.com/liyasthomas/postwoman/pull/197) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Added auto theme [\#185](https://github.com/liyasthomas/postwoman/pull/185) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
- Add Request name label for every requests [\#184](https://github.com/liyasthomas/postwoman/pull/184) ([sharath2106](https://github.com/sharath2106))
|
||||
- Collections [\#176](https://github.com/liyasthomas/postwoman/pull/176) ([TheHollidayInn](https://github.com/TheHollidayInn))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Bearer Token value still left even after being cleared [\#212](https://github.com/liyasthomas/postwoman/issues/212)
|
||||
- All changes in input fields lost when you switch to another page [\#203](https://github.com/liyasthomas/postwoman/issues/203)
|
||||
- POST request json bodies aren't sent [\#180](https://github.com/liyasthomas/postwoman/issues/180)
|
||||
- Headers turn into 0 : \[Object object\] [\#166](https://github.com/liyasthomas/postwoman/issues/166)
|
||||
- Send Again Button Constantly Flickering [\#157](https://github.com/liyasthomas/postwoman/issues/157)
|
||||
- There are cross-domain problems [\#128](https://github.com/liyasthomas/postwoman/issues/128)
|
||||
- Raw requests are not being sent [\#124](https://github.com/liyasthomas/postwoman/issues/124)
|
||||
- Request Body Is Not Sent [\#113](https://github.com/liyasthomas/postwoman/issues/113)
|
||||
- default menu option - 'Http' is not highlighted when launched from installed pwa app \(UI bug\) [\#100](https://github.com/liyasthomas/postwoman/issues/100)
|
||||
- App is broken with old history in localStorage [\#74](https://github.com/liyasthomas/postwoman/issues/74)
|
||||
- Last added history entry is removed automatically after refresh [\#66](https://github.com/liyasthomas/postwoman/issues/66)
|
||||
- Cannot use localhost as base url [\#56](https://github.com/liyasthomas/postwoman/issues/56)
|
||||
- \[CORS\] No 'Access-Control-Allow-Origin' header is present on the requested resource [\#2](https://github.com/liyasthomas/postwoman/issues/2)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Section labels don't display properly in Firefox [\#237](https://github.com/liyasthomas/postwoman/issues/237)
|
||||
- Unsupported URLs \[BUG\]? [\#229](https://github.com/liyasthomas/postwoman/issues/229)
|
||||
- Credentials are still being included in Permalink even when "Include in URL" is turned off [\#227](https://github.com/liyasthomas/postwoman/issues/227)
|
||||
- Display sendRequest runtime errors in the console [\#206](https://github.com/liyasthomas/postwoman/issues/206)
|
||||
- Missing "Landing/start page" [\#162](https://github.com/liyasthomas/postwoman/issues/162)
|
||||
- Response with content-type "application/hal+json" shows as \[Object object\] [\#158](https://github.com/liyasthomas/postwoman/issues/158)
|
||||
- A place to discuss [\#149](https://github.com/liyasthomas/postwoman/issues/149)
|
||||
- Inconsistent version name [\#141](https://github.com/liyasthomas/postwoman/issues/141)
|
||||
- Autoresize the textarea [\#102](https://github.com/liyasthomas/postwoman/issues/102)
|
||||
- Content-Type revamping [\#99](https://github.com/liyasthomas/postwoman/issues/99)
|
||||
- Add version number in footer [\#97](https://github.com/liyasthomas/postwoman/issues/97)
|
||||
- The history doesn't show a date with the timestamp. [\#81](https://github.com/liyasthomas/postwoman/issues/81)
|
||||
- Not working on Brave Browser anymore [\#71](https://github.com/liyasthomas/postwoman/issues/71)
|
||||
- Why da fuq is your name plastered all over the README? [\#70](https://github.com/liyasthomas/postwoman/issues/70)
|
||||
- Comparison with Postman is missing [\#69](https://github.com/liyasthomas/postwoman/issues/69)
|
||||
- HTTP request with different library [\#61](https://github.com/liyasthomas/postwoman/issues/61)
|
||||
- Editorconfig file [\#60](https://github.com/liyasthomas/postwoman/issues/60)
|
||||
- 500 this.isValidURL is not a function [\#58](https://github.com/liyasthomas/postwoman/issues/58)
|
||||
- Styling with Tailwindcss [\#38](https://github.com/liyasthomas/postwoman/issues/38)
|
||||
- Not Working in IE 11 [\#37](https://github.com/liyasthomas/postwoman/issues/37)
|
||||
- PWA not installable [\#19](https://github.com/liyasthomas/postwoman/issues/19)
|
||||
- Simple Misspelling [\#8](https://github.com/liyasthomas/postwoman/issues/8)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- docs: add liyasthomas as a contributor [\#264](https://github.com/liyasthomas/postwoman/pull/264) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add jamesgeorge007 as a contributor [\#263](https://github.com/liyasthomas/postwoman/pull/263) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add NBTX as a contributor [\#262](https://github.com/liyasthomas/postwoman/pull/262) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- Fix .all-contributorsrc badge template. [\#260](https://github.com/liyasthomas/postwoman/pull/260) ([NBTX](https://github.com/NBTX))
|
||||
- docs: add hosseinnedaee as a contributor [\#259](https://github.com/liyasthomas/postwoman/pull/259) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add nityanandagohain as a contributor [\#257](https://github.com/liyasthomas/postwoman/pull/257) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add JacobAnavisca as a contributor [\#256](https://github.com/liyasthomas/postwoman/pull/256) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add izerozlu as a contributor [\#255](https://github.com/liyasthomas/postwoman/pull/255) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add vlad0337187 as a contributor [\#254](https://github.com/liyasthomas/postwoman/pull/254) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add AndrewBastin as a contributor [\#253](https://github.com/liyasthomas/postwoman/pull/253) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add terranblake as a contributor [\#252](https://github.com/liyasthomas/postwoman/pull/252) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add nickpalenchar as a contributor [\#251](https://github.com/liyasthomas/postwoman/pull/251) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add yubathom as a contributor [\#250](https://github.com/liyasthomas/postwoman/pull/250) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add larouxn as a contributor [\#249](https://github.com/liyasthomas/postwoman/pull/249) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add NBTX as a contributor [\#248](https://github.com/liyasthomas/postwoman/pull/248) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- docs: add liyasthomas as a contributor [\#247](https://github.com/liyasthomas/postwoman/pull/247) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||
- Make page changes more fluid [\#246](https://github.com/liyasthomas/postwoman/pull/246) ([NBTX](https://github.com/NBTX))
|
||||
- Minor tweaks [\#245](https://github.com/liyasthomas/postwoman/pull/245) ([liyasthomas](https://github.com/liyasthomas))
|
||||
- ⬆️ Bump @nuxtjs/google-tag-manager from 2.3.0 to 2.3.1 [\#243](https://github.com/liyasthomas/postwoman/pull/243) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump yargs-parser from 15.0.0 to 16.1.0 [\#242](https://github.com/liyasthomas/postwoman/pull/242) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/toast from 3.2.1 to 3.3.0 [\#241](https://github.com/liyasthomas/postwoman/pull/241) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump highlight.js from 9.15.10 to 9.16.2 [\#240](https://github.com/liyasthomas/postwoman/pull/240) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump cypress from 3.5.0 to 3.6.0 [\#239](https://github.com/liyasthomas/postwoman/pull/239) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Fix legend labels in Firefox, fix colored labels slider [\#238](https://github.com/liyasthomas/postwoman/pull/238) ([NBTX](https://github.com/NBTX))
|
||||
- Documentation Cleanup [\#230](https://github.com/liyasthomas/postwoman/pull/230) ([amitdash291](https://github.com/amitdash291))
|
||||
- Fix \#227 Exclude credentials from permalink [\#228](https://github.com/liyasthomas/postwoman/pull/228) ([reefqi037](https://github.com/reefqi037))
|
||||
- ⬆️ Bump cypress from 3.4.1 to 3.5.0 [\#224](https://github.com/liyasthomas/postwoman/pull/224) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/axios from 5.6.0 to 5.8.0 [\#223](https://github.com/liyasthomas/postwoman/pull/223) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump node-sass from 4.12.0 to 4.13.0 [\#222](https://github.com/liyasthomas/postwoman/pull/222) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump nuxt from 2.10.1 to 2.10.2 [\#221](https://github.com/liyasthomas/postwoman/pull/221) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.0 to 2.2.1 [\#220](https://github.com/liyasthomas/postwoman/pull/220) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump vuex-persist from 2.1.0 to 2.1.1 [\#219](https://github.com/liyasthomas/postwoman/pull/219) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Fixed frame colors toggle [\#216](https://github.com/liyasthomas/postwoman/pull/216) ([mateusppereira](https://github.com/mateusppereira))
|
||||
- Re-order sections and add toggle for including authentication in URL [\#215](https://github.com/liyasthomas/postwoman/pull/215) ([NBTX](https://github.com/NBTX))
|
||||
- chore: minor code refactor [\#214](https://github.com/liyasthomas/postwoman/pull/214) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||
- Fix \#212 Clear bearer token value [\#213](https://github.com/liyasthomas/postwoman/pull/213) ([reefqi037](https://github.com/reefqi037))
|
||||
- fix: don't display 'Collection is empty' label if collection has any … [\#208](https://github.com/liyasthomas/postwoman/pull/208) ([vlad0337187](https://github.com/vlad0337187))
|
||||
- Fix proxy URL [\#201](https://github.com/liyasthomas/postwoman/pull/201) ([NBTX](https://github.com/NBTX))
|
||||
- Fix CORS and Mixed-Content issue & Bug Fixes [\#200](https://github.com/liyasthomas/postwoman/pull/200) ([NBTX](https://github.com/NBTX))
|
||||
- ⬆️ Bump start-server-and-test from 1.10.5 to 1.10.6 [\#198](https://github.com/liyasthomas/postwoman/pull/198) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump start-server-and-test from 1.10.3 to 1.10.5 [\#194](https://github.com/liyasthomas/postwoman/pull/194) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump @nuxtjs/google-tag-manager from 2.2.1 to 2.3.0 [\#193](https://github.com/liyasthomas/postwoman/pull/193) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump nuxt from 2.10.0 to 2.10.1 [\#192](https://github.com/liyasthomas/postwoman/pull/192) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- ⬆️ Bump yargs-parser from 14.0.0 to 15.0.0 [\#191](https://github.com/liyasthomas/postwoman/pull/191) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- Add quotation marks for generated code [\#187](https://github.com/liyasthomas/postwoman/pull/187) ([johnhenry](https://github.com/johnhenry))
|
||||
- updated threshold and rootMargin for IntersectionObserver [\#182](https://github.com/liyasthomas/postwoman/pull/182) ([edisonaugusthy](https://github.com/edisonaugusthy))
|
||||
- Add basic e2e tests [\#181](https://github.com/liyasthomas/postwoman/pull/181) ([yubathom](https://github.com/yubathom))
|
||||
- ⬆️ Bump nuxt from 2.9.2 to 2.10.0 [\#179](https://github.com/liyasthomas/postwoman/pull/179) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||
- 🐛 Fixed sitemap configuration [\#177](https://github.com/liyasthomas/postwoman/pull/177) ([NicoPennec](https://github.com/NicoPennec))
|
||||
- Code Refactoring [\#173](https://github.com/liyasthomas/postwoman/pull/173) ([edisonaugusthy](https://github.com/edisonaugusthy))
|
||||
- Added Black Theme [\#172](https://github.com/liyasthomas/postwoman/pull/172) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||
|
||||
## [v0.1.0](https://github.com/liyasthomas/postwoman/tree/v0.1.0) (2019-08-22)
|
||||
|
||||
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/91c08a5e6305cc95a0df46a33fdd0013bf7339b4...v0.1.0)
|
||||
|
||||
|
||||
|
||||
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
||||
|
||||
@@ -34,12 +34,12 @@ Examples of unacceptable behavior by participants include:
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
behavior. Maintainers are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
that are not aligned with our Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
|
||||
226
CONTRIBUTING.md
226
CONTRIBUTING.md
@@ -1,180 +1,92 @@
|
||||
# Introduction
|
||||
# Contributing
|
||||
|
||||
### Write something nice here!
|
||||
When contributing to this repository, please first discuss the change you wish to make via issue,
|
||||
email, or any other method with the owners of this repository before making a change.
|
||||
|
||||
>First off, thank you for considering contributing to Active Admin. It's people like you that make Active Admin such a great tool.
|
||||
Please note we have a code of conduct, please follow it in all your interactions with the project.
|
||||
|
||||
[source: [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Read The Docs](http://read-the-docs.readthedocs.org/en/latest/contribute.html) [2] [Mustache.js](https://github.com/janl/mustache.js/#contributing)
|
||||
## Pull Request Process
|
||||
|
||||
### Tell them why they should read your guidelines.
|
||||
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
|
||||
build.
|
||||
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/).
|
||||
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.
|
||||
|
||||
>Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests.
|
||||
## Code of Conduct
|
||||
|
||||
[source: [Hoodie](https://github.com/hoodiehq/hoodie/blob/master/CONTRIBUTING.md)]
|
||||
### Our Pledge
|
||||
|
||||
### Explain what kinds of contributions you are looking for.
|
||||
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.
|
||||
|
||||
Keep an open mind! Improving documentation, bug triaging, or writing tutorials are all examples of helpful contributions that mean less work for you.
|
||||
### Our Standards
|
||||
|
||||
> Elasticsearch is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into Elasticsearch itself.
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
[source: [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Devise](https://github.com/plataformatec/devise/wiki/Contributing) [2] [Geocoder](https://github.com/alexreisner/geocoder#known-issue) (“known issue”)
|
||||
* 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
|
||||
|
||||
### Explain contributions you are NOT looking for (if any).
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
Again, defining this up front means less work for you. If someone ignores your guide and submits something you don’t want, you can simply close it and point to your policy.
|
||||
* 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
|
||||
|
||||
> Please, don't use the issue tracker for [support questions]. Check whether the #pocoo IRC channel on Freenode can help with your issue. If your problem is not strictly Werkzeug or Flask specific, #python is generally more active. Stack Overflow is also worth considering.
|
||||
### Our Responsibilities
|
||||
|
||||
[source: [Flask](https://github.com/pallets/flask/blob/master/CONTRIBUTING.rst)] **Need more inspiration?** [1] [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CONTRIBUTING.md#about-to-create-a-new-github-issue) [2] [Read the Docs](http://read-the-docs.readthedocs.org/en/latest/open-source-philosophy.html#unsupported)
|
||||
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.
|
||||
|
||||
# Ground Rules
|
||||
### Set expectations for behavior (yours, and theirs).
|
||||
This includes not just how to communicate with others (being respectful, considerate, etc) but also technical responsibilities (importance of testing, project dependencies, etc). Mention and link to your code of conduct, if you have one.
|
||||
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.
|
||||
|
||||
> Responsibilities
|
||||
> * Ensure cross-platform compatibility for every change that's accepted. Windows, Mac, Debian & Ubuntu Linux.
|
||||
> * Ensure that code that goes into core meets all requirements in this checklist: https://gist.github.com/audreyr/4feef90445b9680475f2
|
||||
> * Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback.
|
||||
> * Don't add any classes to the codebase unless absolutely needed. Err on the side of using functions.
|
||||
> * Keep feature versions as small as possible, preferably one new feature per version.
|
||||
> * Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/).
|
||||
### Scope
|
||||
|
||||
[source: [cookiecutter](https://github.com/audreyr/cookiecutter/blob/master/CONTRIBUTING.rst)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#community-code-of-conduct) [2] [geocoder](https://github.com/alexreisner/geocoder#contributing)
|
||||
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.
|
||||
|
||||
# Your First Contribution
|
||||
Help people who are new to your project understand where they can be most helpful. This is also a good time to let people know if you follow a label convention for flagging beginner issues.
|
||||
### Enforcement
|
||||
|
||||
> Unsure where to begin contributing to Atom? You can start by looking through these beginner and help-wanted issues:
|
||||
> Beginner issues - issues which should only require a few lines of code, and a test or two.
|
||||
> Help wanted issues - issues which should be a bit more involved than beginner issues.
|
||||
> Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have.
|
||||
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.
|
||||
|
||||
[source: [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#your-first-code-contribution)] **Need more inspiration?** [1] [Read the Docs](http://docs.readthedocs.org/en/latest/contribute.html#contributing-to-development) [2] [Django](https://docs.djangoproject.com/en/dev/internals/contributing/new-contributors/#first-steps) (scroll down to "Guidelines" as well)
|
||||
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.
|
||||
|
||||
### Bonus points: Add a link to a resource for people who have never contributed to open source before.
|
||||
Here are a couple of friendly tutorials you can include: http://makeapullrequest.com/ and http://www.firsttimersonly.com/
|
||||
### Attribution
|
||||
|
||||
> Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[source: [React](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#pull-requests)]
|
||||
|
||||
As a side note, it helps to use newcomer-friendly language throughout the rest of your document. Here are a couple of examples from [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md):
|
||||
|
||||
>At this point, you're ready to make your changes! Feel free to ask for help; everyone is a beginner at first :smile_cat:
|
||||
>
|
||||
>If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your branch so it's easier to merge.
|
||||
|
||||
# Getting started
|
||||
### Give them a quick walkthrough of how to submit a contribution.
|
||||
How you write this is up to you, but some things you may want to include:
|
||||
|
||||
* Let them know if they need to sign a CLA, agree to a DCO, or get any other legal stuff out of the way
|
||||
* If tests are required for contributions, let them know, and explain how to run the tests
|
||||
* If you use anything other than GitHub to manage issues (ex. JIRA or Trac), let them know which tools they’ll need to contribute
|
||||
|
||||
>For something that is bigger than a one or two line fix:
|
||||
|
||||
>1. Create your own fork of the code
|
||||
>2. Do the changes in your fork
|
||||
>3. If you like the change and think the project could use it:
|
||||
* Be sure you have followed the code style for the project.
|
||||
* Sign the Contributor License Agreement, CLA, with the jQuery Foundation.
|
||||
* Note the jQuery Foundation Code of Conduct.
|
||||
* Send a pull request indicating that you have a CLA on file.
|
||||
|
||||
[source: [Requirejs](http://requirejs.org/docs/contributing.html)] **Need more inspiration?** [1] [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md#1-where-do-i-go-from-here) [2] [Node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#code-contributions) [3] [Ember.js](https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md#pull-requests)
|
||||
|
||||
### If you have a different process for small or "obvious" fixes, let them know.
|
||||
|
||||
> Small contributions such as fixing spelling errors, where the content is small enough to not be considered intellectual property, can be submitted by a contributor as a patch, without a CLA.
|
||||
>
|
||||
>As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following:
|
||||
>* Spelling / grammar fixes
|
||||
>* Typo correction, white space and formatting changes
|
||||
>* Comment clean up
|
||||
>* Bug fixes that change default return values or error codes stored in constants
|
||||
>* Adding logging messages or debugging output
|
||||
>* Changes to ‘metadata’ files like Gemfile, .gitignore, build scripts, etc.
|
||||
>* Moving source files from one directory or package to another
|
||||
|
||||
[source: [Chef](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#chef-obvious-fix-policy)] **Need more inspiration?** [1] [Puppet](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md#making-trivial-changes)
|
||||
|
||||
# How to report a bug
|
||||
### Explain security disclosures first!
|
||||
At bare minimum, include this sentence:
|
||||
> If you find a security vulnerability, do NOT open an issue. Email XXXX instead.
|
||||
|
||||
If you don’t want to use your personal contact information, set up a “security@” email address. Larger projects might have more formal processes for disclosing security, including encrypted communication. (Disclosure: I am not a security expert.)
|
||||
|
||||
> Any security issues should be submitted directly to security@travis-ci.org
|
||||
> In order to determine whether you are dealing with a security issue, ask yourself these two questions:
|
||||
> * Can I access something that's not mine, or something I shouldn't have access to?
|
||||
> * Can I disable something for other people?
|
||||
>
|
||||
> If the answer to either of those two questions are "yes", then you're probably dealing with a security issue. Note that even if you answer "no" to both questions, you may still be dealing with a security issue, so if you're unsure, just email us at security@travis-ci.org.
|
||||
|
||||
[source: [Travis CI](https://github.com/travis-ci/travis-ci/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#security) [2] [Express.js](https://github.com/expressjs/express/blob/master/Security.md)
|
||||
|
||||
### Tell your contributors how to file a bug report.
|
||||
You can even include a template so people can just copy-paste (again, less work for you).
|
||||
|
||||
> When filing an issue, make sure to answer these five questions:
|
||||
>
|
||||
> 1. What version of Go are you using (go version)?
|
||||
> 2. What operating system and processor architecture are you using?
|
||||
> 3. What did you do?
|
||||
> 4. What did you expect to see?
|
||||
> 5. What did you see instead?
|
||||
> General questions should go to the golang-nuts mailing list instead of the issue tracker. The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
[source: [Go](https://github.com/golang/go/blob/master/CONTRIBUTING.md#filing-issues)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#other-bugs ) [2] [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#reporting-bugs) (includes template)
|
||||
|
||||
# How to suggest a feature or enhancement
|
||||
### If you have a particular roadmap, goals, or philosophy for development, share it here.
|
||||
This information will give contributors context before they make suggestions that may not align with the project’s needs.
|
||||
|
||||
> The Express philosophy is to provide small, robust tooling for HTTP servers, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs.
|
||||
>
|
||||
> Express does not force you to use any specific ORM or template engine. With support for over 14 template engines via Consolidate.js, you can quickly craft your perfect framework.
|
||||
|
||||
[source: [Express](https://github.com/expressjs/express#philosophy)] **Need more inspiration?** [Active Admin](https://github.com/activeadmin/activeadmin#goals)
|
||||
|
||||
### Explain your desired process for suggesting a feature.
|
||||
If there is back-and-forth or signoff required, say so. Ask them to scope the feature, thinking through why it’s needed and how it might work.
|
||||
|
||||
> If you find yourself wishing for a feature that doesn't exist in Elasticsearch, you are probably not alone. There are bound to be others out there with similar needs. Many of the features that Elasticsearch has today have been added because our users saw the need. Open an issue on our issues list on GitHub which describes the feature you would like to see, why you need it, and how it should work.
|
||||
|
||||
[source: [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#feature-requests)] **Need more inspiration?** [1] [Hoodie](https://github.com/hoodiehq/hoodie/blob/master/CONTRIBUTING.md#feature-requests) [2] [Ember.js](https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md#requesting-a-feature)
|
||||
|
||||
# Code review process
|
||||
### Explain how a contribution gets accepted after it’s been submitted.
|
||||
Who reviews it? Who needs to sign off before it’s accepted? When should a contributor expect to hear from you? How can contributors get commit access, if at all?
|
||||
|
||||
> The core team looks at Pull Requests on a regular basis in a weekly triage meeting that we hold in a public Google Hangout. The hangout is announced in the weekly status updates that are sent to the puppet-dev list. Notes are posted to the Puppet Community community-triage repo and include a link to a YouTube recording of the hangout.
|
||||
> After feedback has been given we expect responses within two weeks. After two weeks we may close the pull request if it isn't showing any activity.
|
||||
|
||||
[source: [Puppet](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md#submitting-changes)] **Need more inspiration?** [1] [Meteor](https://meteor.hackpad.com/Responding-to-GitHub-Issues-SKE2u3tkSiH ) [2] [Express.js](https://github.com/expressjs/express/blob/master/Contributing.md#becoming-a-committer)
|
||||
|
||||
# Community
|
||||
If there are other channels you use besides GitHub to discuss contributions, mention them here. You can also list the author, maintainers, and/or contributors here, or set expectations for response time.
|
||||
|
||||
> You can chat with the core team on https://gitter.im/cucumber/cucumber. We try to have office hours on Fridays.
|
||||
|
||||
[source: [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CONTRIBUTING.md#talking-with-other-devs)] **Need more inspiration?**
|
||||
[1] [Chef](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#-developer-office-hours) [2] [Cookiecutter](https://github.com/audreyr/cookiecutter#community)
|
||||
|
||||
# BONUS: Code, commit message and labeling conventions
|
||||
These sections are not necessary, but can help streamline the contributions you receive.
|
||||
|
||||
### Explain your preferred style for code, if you have any.
|
||||
|
||||
**Need inspiration?** [1] [Requirejs](http://requirejs.org/docs/contributing.html#codestyle) [2] [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#contributing-to-the-elasticsearch-codebase)
|
||||
|
||||
### Explain if you use any commit message conventions.
|
||||
|
||||
**Need inspiration?** [1] [Angular](https://github.com/angular/material/blob/master/.github/CONTRIBUTING.md#submit) [2] [Node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#step-3-commit)
|
||||
|
||||
### Explain if you use any labeling conventions for issues.
|
||||
|
||||
**Need inspiration?** [1] [StandardIssueLabels](https://github.com/wagenet/StandardIssueLabels#standardissuelabels) [2] [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#issue-and-pull-request-labels)
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
||||
21
Dockerfile
Normal file
21
Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM node:12.10.0-alpine
|
||||
|
||||
LABEL maintainer="Liyas Thomas (liyascthomas@gmail.com)"
|
||||
|
||||
# Add git as the prebuild target requires it to parse version information
|
||||
RUN apk add --update --no-cache \
|
||||
git
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
463
README.md
463
README.md
@@ -1,116 +1,461 @@
|
||||
```
|
||||
When I wrote this, only God and I understood what I was doing. Now, only God knows.
|
||||
```
|
||||
<div align="center">
|
||||
<a href="https://liyas-thomas.firebaseapp.com"><img src="https://raw.githubusercontent.com/liyasthomas/templates/master/assets/logo.gif" alt="Liyas Thomas" width="200"></a>
|
||||
<a href="https://postwoman.io"><img src="static/logo.png" alt="Postwoman.io logo" height="160"></a>
|
||||
<br>
|
||||
<h1>Liyas Thomas</h1>
|
||||
<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>
|
||||
<br>
|
||||
<p>
|
||||
<b>A free, fast and beautiful API request builder</b>
|
||||
</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>
|
||||
</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)
|
||||
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
[](https://travis-ci.org/liyasthomas/postwoman) [](https://github.com/liyasthomas/postwoman/releases/latest) [](https://github.com/liyasthomas/postwoman/archive/master.zip) [](https://github.com/liyasthomas/postwoman/blob/master/LICENSE) [](https://github.com/liyasthomas/postwoman/issues) [](https://liyas-thomas.firebaseapp.com) [](https://www.paypal.me/liyascthomas)
|
||||
**Read: _[Story behind Postwoman](https://dev.to/liyasthomas/i-created-postwoman-an-online-open-source-api-request-builder-41md), [Postwoman v1.0](https://dev.to/liyasthomas/postwoman-v1-0-a-free-fast-beautiful-alternative-to-postman-mn0)_**
|
||||
|
||||
# <img src="icons/icon-48x48.png" alt="postwoman" width="32"> Postwoman
|
||||
**Chat: _[Telegram](https://t.me/postwoman_app), [Discord](https://discord.gg/GAMWxmR)_**
|
||||
|
||||
### 👽 API request builder by [Liyas Thomas](https://github.com/liyasthomas)
|
||||
**Donate: _[PayPal](https://www.paypal.me/liyascthomas), [Open Collective](https://opencollective.com/postwoman), [Patreon](https://www.patreon.com/liyasthomas)_**
|
||||
|
||||
<div align="center">
|
||||
<br>
|
||||
<img src="icons/screely.png" alt="postwoman" width="100%">
|
||||
<img src="icons/screely2.png" alt="postwoman" width="100%">
|
||||
<img src="static/images/screenshot1.png" alt="Screenshot1" width="100%">
|
||||
<br>
|
||||
</div>
|
||||
|
||||
### Features :sparkles:
|
||||
### Features ✨
|
||||
|
||||
:heart: **Lightweight and minimal**: Crafted with minimalistic UI design
|
||||
❤️ **Lightweight**: Crafted with minimalistic UI design - simple design is the best design.
|
||||
|
||||
:electric_plug: **Real-time demo**: Send requests and get response right away!
|
||||
⚡️ **Fast**: Send requests and get/copy responses in real-time - fast software is the best software.
|
||||
|
||||
:robot: **VIBGYOR**: Neon combination of colors for background and foreground
|
||||
**Methods:**
|
||||
- `GET` - Retrieve information about the REST API resource
|
||||
- `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.
|
||||
|
||||
:sparkles: **PWA**: Install as a PWA on your device
|
||||
🌈 **Make it yours**: Customizable combinations for background, foreground and accent colors: because customization is freedom. [Customize now ✨](https://postwoman.io/settings).
|
||||
|
||||
---
|
||||
**Customizations:**
|
||||
- Choose theme: Kinda Dark (default), Clearly White, Just Black and System theme
|
||||
- Choose accent color: Green (default), Yellow, Pink, Red, Purple, Orange, Cyan and Blue
|
||||
- Toggle multi-colored headings
|
||||
|
||||
## Demo
|
||||
_Customized themes are synced with local session storage_
|
||||
|
||||
[https://liyasthomas.github.io/postwoman](https://liyasthomas.github.io/postwoman)
|
||||
🔥 **PWA**: Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
|
||||
|
||||
1. Specify your request method
|
||||
2. Type in your API URL
|
||||
3. Add API path
|
||||
4. Send request
|
||||
5. Get response!
|
||||
**Features:**
|
||||
- Instant loading with [Service Workers](https://developers.google.com/web/fundamentals/primers/service-workers)
|
||||
- Offline support
|
||||
- Low RAM/memory and CPU usage
|
||||
- Add to Home Screen
|
||||
- Desktop PWA
|
||||
- ([full features](https://developers.google.com/web/progressive-web-apps))
|
||||
|
||||
🚀 **Request**: Retrieve response from endpoint instantly.
|
||||
|
||||
- Choose `method`
|
||||
- Enter `URL` and `Path`
|
||||
- Send
|
||||
|
||||
**Features:**
|
||||
- Copy/share public "Share URL"
|
||||
- Generate request code for `JavaScript XHR`, `Fetch` and `cURL`
|
||||
- Copy generated request code to clipboard
|
||||
- Import `cURL`
|
||||
- Label requests
|
||||
|
||||
🔌 **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.
|
||||
|
||||
🌩 **Socket.IO**: Send and Receive data with socketio server. SocketIO is popular websocket solution.
|
||||
|
||||
🦟 **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.
|
||||
|
||||
- Set endpoint and get schemas
|
||||
- Multi-column docs
|
||||
- Set custom request headers
|
||||
- Query schema
|
||||
- Get query response
|
||||
|
||||
🔐 **Authentication**: Allows to identify the end user.
|
||||
|
||||
**Types:**
|
||||
- None
|
||||
- Basic
|
||||
- Bearer Token
|
||||
- OAuth 2.0
|
||||
- OIDC Access Token/PKCE (Proof Key for Code Exchange)
|
||||
|
||||
📢 **Headers**: Describes the format the body of your request is being sent as.
|
||||
|
||||
- Add or remove Header list
|
||||
|
||||
📫 **Parameters**: Use request parameters to set varying parts in simulated requests.
|
||||
|
||||
📃 **Request Body**: Used to send and receive data via the REST API.
|
||||
|
||||
**Options:**
|
||||
- Set `Content Type`
|
||||
- Add or remove Parameter list
|
||||
- Toggle between key-value and RAW input Parameter list
|
||||
|
||||
👋 **Responses**: Contains the status line, headers and the message/response body.
|
||||
|
||||
- Copy response to clipboard
|
||||
- Download response to as a file
|
||||
- View preview of HTML responses
|
||||
|
||||
⏰ **History**: Request entries are synced with local session storage to reuse with a single click.
|
||||
|
||||
**Fields:**
|
||||
- Star
|
||||
- Label
|
||||
- Method
|
||||
- Status code
|
||||
- URL
|
||||
- Path
|
||||
- Timestamp
|
||||
- Duration
|
||||
- Pre-request script
|
||||
|
||||
_History entries can be sorted by any fields_
|
||||
|
||||
_Histories can be deleted one-by-one or all together_
|
||||
|
||||
📁 **Collections**: Keep your API requests organized with collections and folders. Reuse them with a single click.
|
||||
|
||||
**Options:**
|
||||
- Create infinite collections, folders and requests
|
||||
- Edit, delete, move, export, import and replace
|
||||
|
||||
_Collections are synced with local session storage_
|
||||
|
||||
🌐 **Proxy**: Enable Proxy Mode from Settings to access blocked APIs.
|
||||
|
||||
**Features:**
|
||||
- Hide your IP address
|
||||
- Fixes [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross Origin Resource Sharing) issues
|
||||
- Access APIs served in non-HTTPS (`http://`)
|
||||
- Use custom Proxy URL
|
||||
|
||||
_Official Postwoman Proxy is hosted by Apollo Software - **[Privacy Policy](https://apollosoftware.xyz/legal/postwoman)**_
|
||||
|
||||
📜 **Pre-Request Scripts β**: Snippets of code associated with a request that are executed before the request is sent.
|
||||
|
||||
**Use-cases:**
|
||||
- Include timestamp in the request headers
|
||||
- Send a random alphanumeric string in the URL parameters
|
||||
|
||||
_Requests with Pre-Request Scripts are indicated in History entries_
|
||||
|
||||
📄 **API Documentation**: Create and share dynamic API documentation easily, quickly.
|
||||
|
||||
**Usage:**
|
||||
1. Add your requests to Collections and Folders
|
||||
2. Export Collections and easily share your APIs with the rest of your team
|
||||
3. Import Collections and Generate Documentation on-the-go
|
||||
|
||||
⌨️ **Keyboard Shortcuts**: Optimized for efficiency.
|
||||
|
||||
**Shortcuts:**
|
||||
- Send Request <kbd>Ctrl</kbd> + <kbd>G</kbd>
|
||||
- Save to Collections <kbd>Ctrl</kbd> + <kbd>S</kbd>
|
||||
- Copy Request Link <kbd>Ctrl</kbd> + <kbd>K</kbd>
|
||||
- Reset Request <kbd>Ctrl</kbd> + <kbd>L</kbd>
|
||||
|
||||
🌎 **i18n β**: Experience the app in your own language.
|
||||
|
||||
1. Scroll down to the footer
|
||||
2. Click "Choose Language" icon button
|
||||
3. Select your language from the menu
|
||||
|
||||
_Keep in mind: Translations aren't available for all source and target language combinations_
|
||||
|
||||
**To provide a localized experience for users around the world, you can add you own translations.**
|
||||
|
||||
_**All `i18n` contributions are welcome to `i18n` [branch](https://github.com/liyasthomas/postwoman/tree/i18n) only!**_
|
||||
|
||||
📦 **Add-ons**: Official add-ons for Postwoman.
|
||||
|
||||
- **[Proxy β](https://github.com/postwoman-io/postwoman-proxy)** - A simple proxy server created for Postwoman
|
||||
- **[CLI β](https://github.com/postwoman-io/postwoman-cli)** - A CLI solution for Postwoman
|
||||
- **Browser Extensions** - Browser extensions that simplifies access to Postwoman
|
||||
|
||||
[ **Firefox**](https://addons.mozilla.org/en-US/firefox/addon/postwoman) | [ **Chrome**](https://chrome.google.com/webstore/detail/postwoman-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld) ([GitHub](https://github.com/AndrewBastin/postwoman-extension))
|
||||
|
||||
>**Extensions fixes `CORS` issues.**
|
||||
|
||||
_Add-ons are developed and maintained under **[Official Postwoman Organization](https://github.com/postwoman-io)**._
|
||||
|
||||
☁️ **Auth + Sync**: Sign in and sync in real-time.
|
||||
|
||||
**Sign in with:**
|
||||
- Google
|
||||
- GitHub
|
||||
|
||||
**Sync:**
|
||||
- History
|
||||
- Collections
|
||||
- Environments
|
||||
- Notes
|
||||
|
||||
✅ **Post-Request Tests β**: Write tests associated with a request that are executed after the request response.
|
||||
|
||||
**Use-cases:**
|
||||
- Check the status code as an integer
|
||||
- Filter response headers
|
||||
- Parse the response data
|
||||
|
||||
📝 **Notes** : Instantly jot down notes, tasks or whatever you feel like as they come to your mind.
|
||||
|
||||
_Notes are only available for signed-in users_
|
||||
|
||||
🌱 **Environments** : Environment variables allow you to store and reuse values in your requests and scripts.
|
||||
|
||||
**Use-cases:**
|
||||
- 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)
|
||||
|
||||
[postwoman.io](https://postwoman.io)
|
||||
|
||||
<a href="https://www.netlify.com">
|
||||
<img src="https://www.netlify.com/img/global/badges/netlify-light.svg"/>
|
||||
</a>
|
||||
|
||||
## Usage 💡
|
||||
|
||||
1. Specify your request `method`
|
||||
2. Type in your API `URL` and `path`
|
||||
3. Send request
|
||||
4. Get response
|
||||
|
||||
You're done!
|
||||
|
||||
---
|
||||
## Built with 🔧
|
||||
|
||||
## Built with
|
||||
|
||||
* **[Chromium](https://github.com/chromium/chromium)** - Thanks for being so fast!
|
||||
* [Chromium](https://github.com/chromium/chromium) - Thanks for being so fast!
|
||||
* HTML - For the web framework
|
||||
* CSS - For styling components
|
||||
* JavaScript - For magic!
|
||||
* [Vue](https://vuejs.org/) - To add to the JavaScript magic!
|
||||
* [Nuxt](https://nuxtjs.org/) - To add to the Vue magic!
|
||||
|
||||
---
|
||||
## Developing 👷
|
||||
|
||||
## Contributing
|
||||
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`.
|
||||
|
||||
_Sample keys only works with the [production build](https://postwoman.io)._
|
||||
|
||||
#### Use a browser based development environment:
|
||||
|
||||
[](https://gitpod.io/#https://github.com/liyasthomas/postwoman)
|
||||
|
||||
#### Or, with local development environment:
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
2. Install dependencies by running `npm install` within the directory that you cloned (probably `postwoman`).
|
||||
3. Start the development server with `npm run dev`.
|
||||
4. Open development site by going to [http://localhost:3000](http://localhost:3000) in your browser.
|
||||
|
||||
#### Or, with 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)
|
||||
|
||||
```bash
|
||||
#pull
|
||||
docker pull liyasthomas/postwoman
|
||||
|
||||
#run
|
||||
docker run -p 3000:3000 liyasthomas/postwoman:latest
|
||||
|
||||
#build
|
||||
docker build -t postwoman:latest
|
||||
```
|
||||
|
||||
## 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`.
|
||||
|
||||
## Contributing 🍰
|
||||
|
||||
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 [Travis CI](https://travis-ci.com) for continuous integration. Check out our [Travis CI Status](https://travis-ci.org/liyasthomas/postwoman).
|
||||
|
||||
---
|
||||
|
||||
## Versioning
|
||||
## 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
|
||||
## Change log 📝
|
||||
|
||||
See the [CHANGELOG](CHANGELOG.md) file for details.
|
||||
|
||||
---
|
||||
|
||||
## Authors
|
||||
## Authors 🔮
|
||||
|
||||
### Lead Developers
|
||||
* [**Liyas Thomas**](https://github.com/liyasthomas) - *Author*
|
||||
|
||||
* **[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
|
||||
* [Liyas Thomas](https://github.com/liyasthomas)
|
||||
|
||||
### Contributors
|
||||
* [Liyas Thomas](https://github.com/liyasthomas)
|
||||
* ([contributors](https://github.com/liyasthomas/postwoman/graphs/contributors))
|
||||
|
||||
### Thanks
|
||||
* [Dribbble](https://dribbble.com)
|
||||
### 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
|
||||
|
||||
## License
|
||||
* [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 🙏
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
* Hat tip to anyone who's code was used
|
||||
* Hat tip to anyone whose code was used
|
||||
* Inspirations:
|
||||
* [Dribbble](https://dribbble.com)
|
||||
* [Dribbble](https://dribbble.com)
|
||||
|
||||
## Badges
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Preview</th>
|
||||
<th>Markdown code</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/Tested_on-Postwoman-202124?logo=Postwoman"/></a><br/><sub>Default<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/Tested_on-Postwoman-success?logo=Postwoman"/></a><br/><sub>Success<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/Tested_on-Postwoman-critical?logo=Postwoman"/></a><br/><sub>Critical<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/Tested_on-Postwoman-blueviolet?logo=Postwoman"/></a><br/><sub>Custom<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="200px"><a href="https://postwoman.io"><br/><img src="https://img.shields.io/badge/your_text-Postwoman-hex_color_code?logo=Postwoman"/></a><br/><sub>Customize<sub></td>
|
||||
<td><code>[](https://postwoman.io)</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div align="center">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
7
assets/README.md
Normal file
7
assets/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# ASSETS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).
|
||||
72
assets/css/fonts.scss
Normal file
72
assets/css/fonts.scss
Normal file
@@ -0,0 +1,72 @@
|
||||
// @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-v48.woff2") format("woff2");
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: "Material Icons";
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
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"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~static/fonts/poppins-v9-latin-500.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* poppins-700 - latin */
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
font-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"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~static/fonts/poppins-v9-latin-700.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* roboto-mono-regular - latin */
|
||||
@font-face {
|
||||
font-family: "Roboto Mono";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local("Roboto Mono"), local("RobotoMono-Regular"),
|
||||
url("~static/fonts/roboto-mono-v7-latin-regular.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("~static/fonts/roboto-mono-v7-latin-regular.woff") format("woff");
|
||||
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
933
assets/css/styles.scss
Normal file
933
assets/css/styles.scss
Normal file
@@ -0,0 +1,933 @@
|
||||
$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;
|
||||
}
|
||||
|
||||
.nav-first {
|
||||
z-index: 1;
|
||||
height: 100vh;
|
||||
padding: 0 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex: 1;
|
||||
order: 2;
|
||||
position: relative;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.nav-second {
|
||||
display: flex;
|
||||
width: 10%;
|
||||
order: 3;
|
||||
// comment below this to display
|
||||
display: none;
|
||||
}
|
||||
|
||||
nav.primary-nav {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
svg {
|
||||
fill: var(--fg-light-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
a {
|
||||
display: 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;
|
||||
height: 52px;
|
||||
|
||||
&: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;
|
||||
border-top: 1px dashed var(--brd-color);
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
display: 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);
|
||||
margin: 8px 0;
|
||||
|
||||
&:hover {
|
||||
color: var(--fg-color);
|
||||
}
|
||||
|
||||
&.current {
|
||||
color: var(--ac-color);
|
||||
fill: var(--ac-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-bottom: 1px dashed var(--brd-color);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
$bgcolor: var(--tt-color);
|
||||
$fgcolor: var(--fg-color);
|
||||
display: block !important;
|
||||
z-index: 10000;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
.tooltip-inner {
|
||||
background: $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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
h3.title {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.info:not(.toasted) {
|
||||
margin-left: 4px;
|
||||
color: var(--fg-light-color);
|
||||
}
|
||||
|
||||
.bg-color {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 4px;
|
||||
padding: 6px 16px;
|
||||
border-radius: 20px;
|
||||
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;
|
||||
}
|
||||
|
||||
&:not([disabled]):hover,
|
||||
&:not([disabled]):active,
|
||||
&:not([disabled]):focus {
|
||||
color: var(--act-color);
|
||||
fill: var(--act-color);
|
||||
box-shadow: inset 0 0 0 2px var(--fg-color);
|
||||
}
|
||||
|
||||
&.icon {
|
||||
background-color: transparent;
|
||||
color: var(--fg-light-color);
|
||||
fill: var(--fg-light-color);
|
||||
border-radius: 8px;
|
||||
|
||||
&:not([disabled]):hover,
|
||||
&:not([disabled]):active,
|
||||
&:not([disabled]):focus {
|
||||
color: var(--fg-color);
|
||||
fill: var(--fg-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.primary {
|
||||
color: var(--ac-color);
|
||||
|
||||
&:not([disabled]):hover,
|
||||
&:not([disabled]):active,
|
||||
&:not([disabled]):focus {
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes beat {
|
||||
30% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.material-icons:active {
|
||||
animation: beat 0.5s forwards 1;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 16px 0;
|
||||
border-radius: 16px;
|
||||
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: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;
|
||||
|
||||
&.response-headers {
|
||||
display: inline-flex;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
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 wrap;
|
||||
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: 16px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.nav-first {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
height: auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
background-color: var(--bg-color);
|
||||
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
nav.primary-nav {
|
||||
flex-flow: row nowrap;
|
||||
overflow: auto;
|
||||
justify-content: space-between;
|
||||
background-color: var(--bg-dark-color);
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
margin: 8px;
|
||||
flex: 1;
|
||||
|
||||
&.nuxt-link-exact-active {
|
||||
background-color: transparent;
|
||||
color: var(--ac-color);
|
||||
fill: var(--ac-color);
|
||||
|
||||
svg {
|
||||
fill: var(--ac-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nav.secondary-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 0 8px 68px;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
ul li,
|
||||
ol li {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hide-on-small-screen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hide-on-large-screen,
|
||||
.show-on-small-screen {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.sticky-inner {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inner-left {
|
||||
order: 0;
|
||||
}
|
||||
|
||||
.inner-right {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.toasted-container {
|
||||
margin-bottom: 68px;
|
||||
}
|
||||
}
|
||||
107
assets/css/themes.scss
Normal file
107
assets/css/themes.scss
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
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;
|
||||
}
|
||||
}
|
||||
222
assets/js/curlparser.js
Normal file
222
assets/js/curlparser.js
Normal file
@@ -0,0 +1,222 @@
|
||||
import * as cookie from "cookie"
|
||||
import * as URL from "url"
|
||||
import * as querystring from "querystring"
|
||||
|
||||
/**
|
||||
* given this: [ 'msg1=value1', 'msg2=value2' ]
|
||||
* output this: 'msg1=value1&msg2=value2'
|
||||
* @param dataArguments
|
||||
*/
|
||||
const joinDataArguments = dataArguments => {
|
||||
let data = ""
|
||||
dataArguments.forEach((argument, i) => {
|
||||
if (i === 0) {
|
||||
data += argument
|
||||
} else {
|
||||
data += `&${argument}`
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
const parseCurlCommand = curlCommand => {
|
||||
let newlineFound = /\r|\n/.exec(curlCommand)
|
||||
if (newlineFound) {
|
||||
// remove newlines
|
||||
curlCommand = curlCommand.replace(/\r|\n/g, "")
|
||||
}
|
||||
// yargs parses -XPOST as separate arguments. just prescreen for it.
|
||||
curlCommand = curlCommand.replace(/ -XPOST/, " -X POST")
|
||||
curlCommand = curlCommand.replace(/ -XGET/, " -X GET")
|
||||
curlCommand = curlCommand.replace(/ -XPUT/, " -X PUT")
|
||||
curlCommand = curlCommand.replace(/ -XPATCH/, " -X PATCH")
|
||||
curlCommand = curlCommand.replace(/ -XDELETE/, " -X DELETE")
|
||||
curlCommand = curlCommand.trim()
|
||||
let parsedArguments = require("yargs-parser")(curlCommand)
|
||||
let cookieString
|
||||
let cookies
|
||||
let url = parsedArguments._[1]
|
||||
if (!url) {
|
||||
for (let argName in parsedArguments) {
|
||||
if (typeof parsedArguments[argName] === "string") {
|
||||
if (["http", "www."].includes(parsedArguments[argName])) {
|
||||
url = parsedArguments[argName]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let headers
|
||||
|
||||
const parseHeaders = headerFieldName => {
|
||||
if (parsedArguments[headerFieldName]) {
|
||||
if (!headers) {
|
||||
headers = {}
|
||||
}
|
||||
if (!Array.isArray(parsedArguments[headerFieldName])) {
|
||||
parsedArguments[headerFieldName] = [parsedArguments[headerFieldName]]
|
||||
}
|
||||
parsedArguments[headerFieldName].forEach(header => {
|
||||
if (header.includes("Cookie")) {
|
||||
// stupid javascript tricks: closure
|
||||
cookieString = header
|
||||
} else {
|
||||
let colonIndex = header.indexOf(":")
|
||||
let headerName = header.substring(0, colonIndex)
|
||||
let headerValue = header.substring(colonIndex + 1).trim()
|
||||
headers[headerName] = headerValue
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
parseHeaders("H")
|
||||
parseHeaders("header")
|
||||
if (parsedArguments.A) {
|
||||
if (!headers) {
|
||||
headers = []
|
||||
}
|
||||
headers["User-Agent"] = parsedArguments.A
|
||||
} else if (parsedArguments["user-agent"]) {
|
||||
if (!headers) {
|
||||
headers = []
|
||||
}
|
||||
headers["User-Agent"] = parsedArguments["user-agent"]
|
||||
}
|
||||
|
||||
if (parsedArguments.b) {
|
||||
cookieString = parsedArguments.b
|
||||
}
|
||||
if (parsedArguments.cookie) {
|
||||
cookieString = parsedArguments.cookie
|
||||
}
|
||||
let multipartUploads
|
||||
if (parsedArguments.F) {
|
||||
multipartUploads = {}
|
||||
if (!Array.isArray(parsedArguments.F)) {
|
||||
parsedArguments.F = [parsedArguments.F]
|
||||
}
|
||||
parsedArguments.F.forEach(multipartArgument => {
|
||||
// input looks like key=value. value could be json or a file path prepended with an @
|
||||
const [key, value] = multipartArgument.split("=", 2)
|
||||
multipartUploads[key] = value
|
||||
})
|
||||
}
|
||||
if (cookieString) {
|
||||
const cookieParseOptions = {
|
||||
decode: s => s,
|
||||
}
|
||||
// separate out cookie headers into separate data structure
|
||||
// note: cookie is case insensitive
|
||||
cookies = cookie.parse(cookieString.replace(/^Cookie: /gi, ""), cookieParseOptions)
|
||||
}
|
||||
let method
|
||||
if (parsedArguments.X === "POST") {
|
||||
method = "post"
|
||||
} else if (parsedArguments.X === "PUT" || parsedArguments["T"]) {
|
||||
method = "put"
|
||||
} else if (parsedArguments.X === "PATCH") {
|
||||
method = "patch"
|
||||
} else if (parsedArguments.X === "DELETE") {
|
||||
method = "delete"
|
||||
} else if (parsedArguments.X === "OPTIONS") {
|
||||
method = "options"
|
||||
} else if (
|
||||
(parsedArguments["d"] ||
|
||||
parsedArguments["data"] ||
|
||||
parsedArguments["data-ascii"] ||
|
||||
parsedArguments["data-binary"] ||
|
||||
parsedArguments["F"] ||
|
||||
parsedArguments["form"]) &&
|
||||
!(parsedArguments["G"] || parsedArguments["get"])
|
||||
) {
|
||||
method = "post"
|
||||
} else if (parsedArguments["I"] || parsedArguments["head"]) {
|
||||
method = "head"
|
||||
} else {
|
||||
method = "get"
|
||||
}
|
||||
|
||||
let compressed = !!parsedArguments.compressed
|
||||
let urlObject = URL.parse(url) // eslint-disable-line
|
||||
|
||||
// if GET request with data, convert data to query string
|
||||
// NB: the -G flag does not change the http verb. It just moves the data into the url.
|
||||
if (parsedArguments["G"] || parsedArguments["get"]) {
|
||||
urlObject.query = urlObject.query ? urlObject.query : ""
|
||||
let option = "d" in parsedArguments ? "d" : "data" in parsedArguments ? "data" : null
|
||||
if (option) {
|
||||
let urlQueryString = ""
|
||||
|
||||
if (!url.includes("?")) {
|
||||
url += "?"
|
||||
} else {
|
||||
urlQueryString += "&"
|
||||
}
|
||||
|
||||
if (typeof parsedArguments[option] === "object") {
|
||||
urlQueryString += parsedArguments[option].join("&")
|
||||
} else {
|
||||
urlQueryString += parsedArguments[option]
|
||||
}
|
||||
urlObject.query += urlQueryString
|
||||
url += urlQueryString
|
||||
delete parsedArguments[option]
|
||||
}
|
||||
}
|
||||
let query = querystring.parse(urlObject.query, null, null, {
|
||||
maxKeys: 10000,
|
||||
})
|
||||
|
||||
urlObject.search = null // Clean out the search/query portion.
|
||||
const request = {
|
||||
url,
|
||||
urlWithoutQuery: URL.format(urlObject),
|
||||
}
|
||||
if (compressed) {
|
||||
request["compressed"] = true
|
||||
}
|
||||
|
||||
if (Object.keys(query).length > 0) {
|
||||
request.query = query
|
||||
}
|
||||
if (headers) {
|
||||
request.headers = headers
|
||||
}
|
||||
request["method"] = method
|
||||
|
||||
if (cookies) {
|
||||
request.cookies = cookies
|
||||
request.cookieString = cookieString.replace("Cookie: ", "")
|
||||
}
|
||||
if (multipartUploads) {
|
||||
request.multipartUploads = multipartUploads
|
||||
}
|
||||
if (parsedArguments.data) {
|
||||
request.data = parsedArguments.data
|
||||
} else if (parsedArguments["data-binary"]) {
|
||||
request.data = parsedArguments["data-binary"]
|
||||
request.isDataBinary = true
|
||||
} else if (parsedArguments["d"]) {
|
||||
request.data = parsedArguments["d"]
|
||||
} else if (parsedArguments["data-ascii"]) {
|
||||
request.data = parsedArguments["data-ascii"]
|
||||
}
|
||||
|
||||
if (parsedArguments["u"]) {
|
||||
request.auth = parsedArguments["u"]
|
||||
}
|
||||
if (parsedArguments["user"]) {
|
||||
request.auth = parsedArguments["user"]
|
||||
}
|
||||
if (Array.isArray(request.data)) {
|
||||
request.dataArray = request.data
|
||||
request.data = joinDataArguments(request.data)
|
||||
}
|
||||
|
||||
if (parsedArguments["k"] || parsedArguments["insecure"]) {
|
||||
request.insecure = true
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
export default parseCurlCommand
|
||||
227
assets/js/oauth.js
Normal file
227
assets/js/oauth.js
Normal file
@@ -0,0 +1,227 @@
|
||||
const redirectUri = `${window.location.origin}/`
|
||||
|
||||
// GENERAL HELPER FUNCTIONS
|
||||
|
||||
/**
|
||||
* Makes a POST request and parse the response as JSON
|
||||
*
|
||||
* @param {String} url - The resource
|
||||
* @param {Object} params - Configuration options
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const sendPostRequest = async (url, params) => {
|
||||
const body = Object.keys(params)
|
||||
.map(key => `${key}=${params[key]}`)
|
||||
.join("&")
|
||||
const options = {
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
},
|
||||
body,
|
||||
}
|
||||
try {
|
||||
const response = await fetch(url, options)
|
||||
const data = await response.json()
|
||||
return data
|
||||
} catch (err) {
|
||||
console.error("Request failed", err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a query string into an object
|
||||
*
|
||||
* @param {String} searchQuery - The search query params
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const parseQueryString = searchQuery => {
|
||||
if (searchQuery === "") {
|
||||
return {}
|
||||
}
|
||||
const segments = searchQuery.split("&").map(s => s.split("="))
|
||||
const queryString = segments.reduce((obj, el) => ({ ...obj, [el[0]]: el[1] }), {})
|
||||
return queryString
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OAuth configuration from OpenID Discovery endpoint
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const getTokenConfiguration = async endpoint => {
|
||||
const options = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
}
|
||||
try {
|
||||
const response = await fetch(endpoint, options)
|
||||
const config = await response.json()
|
||||
return config
|
||||
} catch (err) {
|
||||
console.error("Request failed", err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// PKCE HELPER FUNCTIONS
|
||||
|
||||
/**
|
||||
* Generates a secure random string using the browser crypto functions
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const generateRandomString = () => {
|
||||
const array = new Uint32Array(28)
|
||||
window.crypto.getRandomValues(array)
|
||||
return Array.from(array, dec => `0${dec.toString(16)}`.substr(-2)).join("")
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the SHA256 hash of the input text
|
||||
*
|
||||
* @returns {Promise<ArrayBuffer>}
|
||||
*/
|
||||
|
||||
const sha256 = plain => {
|
||||
const encoder = new TextEncoder()
|
||||
const data = encoder.encode(plain)
|
||||
return window.crypto.subtle.digest("SHA-256", data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the input string into Base64 format
|
||||
*
|
||||
* @param {String} str - The string to be converted
|
||||
* @returns {Promise<ArrayBuffer>}
|
||||
*/
|
||||
|
||||
const base64urlencode = (
|
||||
str // Converts the ArrayBuffer to string using Uint8 array to convert to what btoa accepts.
|
||||
) =>
|
||||
// btoa accepts chars only within ascii 0-255 and base64 encodes them.
|
||||
// Then convert the base64 encoded to base64url encoded
|
||||
// (replace + with -, replace / with _, trim trailing =)
|
||||
btoa(String.fromCharCode.apply(null, new Uint8Array(str)))
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=+$/, "")
|
||||
|
||||
/**
|
||||
* Return the base64-urlencoded sha256 hash for the PKCE challenge
|
||||
*
|
||||
* @param {String} v - The randomly generated string
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
const pkceChallengeFromVerifier = async v => {
|
||||
const hashed = await sha256(v)
|
||||
return base64urlencode(hashed)
|
||||
}
|
||||
|
||||
// OAUTH REQUEST
|
||||
|
||||
/**
|
||||
* Initiates PKCE Auth Code flow when requested
|
||||
*
|
||||
* @param {Object} - The necessary params
|
||||
* @returns {Void}
|
||||
*/
|
||||
|
||||
const tokenRequest = async ({
|
||||
oidcDiscoveryUrl,
|
||||
grantType,
|
||||
authUrl,
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
scope,
|
||||
}) => {
|
||||
// Check oauth configuration
|
||||
if (oidcDiscoveryUrl !== "") {
|
||||
const { authorization_endpoint, token_endpoint } = await getTokenConfiguration(oidcDiscoveryUrl)
|
||||
authUrl = authorization_endpoint
|
||||
accessTokenUrl = token_endpoint
|
||||
}
|
||||
|
||||
// Store oauth information
|
||||
localStorage.setItem("token_endpoint", accessTokenUrl)
|
||||
localStorage.setItem("client_id", clientId)
|
||||
|
||||
// Create and store a random state value
|
||||
const state = generateRandomString()
|
||||
localStorage.setItem("pkce_state", state)
|
||||
|
||||
// Create and store a new PKCE code_verifier (the plaintext random secret)
|
||||
const code_verifier = generateRandomString()
|
||||
localStorage.setItem("pkce_code_verifier", code_verifier)
|
||||
|
||||
// Hash and base64-urlencode the secret to use as the challenge
|
||||
const code_challenge = await pkceChallengeFromVerifier(code_verifier)
|
||||
|
||||
// Build the authorization URL
|
||||
const buildUrl = () =>
|
||||
`${authUrl + `?response_type=${grantType}`}&client_id=${encodeURIComponent(
|
||||
clientId
|
||||
)}&state=${encodeURIComponent(state)}&scope=${encodeURIComponent(
|
||||
scope
|
||||
)}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${encodeURIComponent(
|
||||
code_challenge
|
||||
)}&code_challenge_method=S256`
|
||||
|
||||
// Redirect to the authorization server
|
||||
window.location = buildUrl()
|
||||
}
|
||||
|
||||
// OAUTH REDIRECT HANDLING
|
||||
|
||||
/**
|
||||
* Handle the redirect back from the authorization server and
|
||||
* get an access token from the token endpoint
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
const oauthRedirect = async () => {
|
||||
let tokenResponse = ""
|
||||
let q = parseQueryString(window.location.search.substring(1))
|
||||
// Check if the server returned an error string
|
||||
if (q.error) {
|
||||
alert(`Error returned from authorization server: ${q.error}`)
|
||||
}
|
||||
// If the server returned an authorization code, attempt to exchange it for an access token
|
||||
if (q.code) {
|
||||
// Verify state matches what we set at the beginning
|
||||
if (localStorage.getItem("pkce_state") != q.state) {
|
||||
alert("Invalid state")
|
||||
} else {
|
||||
try {
|
||||
// Exchange the authorization code for an access token
|
||||
tokenResponse = await sendPostRequest(localStorage.getItem("token_endpoint"), {
|
||||
grant_type: "authorization_code",
|
||||
code: q.code,
|
||||
client_id: localStorage.getItem("client_id"),
|
||||
redirect_uri: redirectUri,
|
||||
code_verifier: localStorage.getItem("pkce_code_verifier"),
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(`${error.error}\n\n${error.error_description}`)
|
||||
}
|
||||
}
|
||||
// Clean these up since we don't need them anymore
|
||||
localStorage.removeItem("pkce_state")
|
||||
localStorage.removeItem("pkce_code_verifier")
|
||||
localStorage.removeItem("token_endpoint")
|
||||
localStorage.removeItem("client_id")
|
||||
return tokenResponse
|
||||
}
|
||||
return tokenResponse
|
||||
}
|
||||
|
||||
export { tokenRequest, oauthRedirect }
|
||||
51
assets/js/pwa.js
Normal file
51
assets/js/pwa.js
Normal file
@@ -0,0 +1,51 @@
|
||||
export default () => {
|
||||
//*** Determine whether or not the PWA has been installed. ***//
|
||||
|
||||
// Step 1: Check local storage
|
||||
let pwaInstalled = localStorage.getItem("pwaInstalled") === "yes"
|
||||
|
||||
// Step 2: Check if the display-mode is standalone. (Only permitted for PWAs.)
|
||||
if (!pwaInstalled && window.matchMedia("(display-mode: standalone)").matches) {
|
||||
localStorage.setItem("pwaInstalled", "yes")
|
||||
pwaInstalled = true
|
||||
}
|
||||
|
||||
// Step 3: Check if the navigator is in standalone mode. (Again, only permitted for PWAs.)
|
||||
if (!pwaInstalled && window.navigator.standalone === true) {
|
||||
localStorage.setItem("pwaInstalled", "yes")
|
||||
pwaInstalled = true
|
||||
}
|
||||
|
||||
//*** If the PWA has not been installed, show the install PWA prompt.. ***//
|
||||
let deferredPrompt = null
|
||||
window.addEventListener("beforeinstallprompt", event => {
|
||||
deferredPrompt = event
|
||||
|
||||
// Show the install button if the prompt appeared.
|
||||
if (!pwaInstalled) {
|
||||
document.querySelector("#installPWA").style.display = "inline-flex"
|
||||
}
|
||||
})
|
||||
|
||||
// When the app is installed, remove install prompts.
|
||||
window.addEventListener("appinstalled", event => {
|
||||
localStorage.setItem("pwaInstalled", "yes")
|
||||
pwaInstalled = true
|
||||
document.getElementById("installPWA").style.display = "none"
|
||||
})
|
||||
|
||||
// When the app is uninstalled, add the prompts back
|
||||
return async () => {
|
||||
if (deferredPrompt) {
|
||||
deferredPrompt.prompt()
|
||||
let outcome = await deferredPrompt.userChoice
|
||||
|
||||
if (outcome === "accepted") {
|
||||
console.log("Postwoman was installed successfully.")
|
||||
} else {
|
||||
console.log("Postwoman could not be installed. (Installation rejected by user.)")
|
||||
}
|
||||
deferredPrompt = null
|
||||
}
|
||||
}
|
||||
}
|
||||
59
build.js
Normal file
59
build.js
Normal file
@@ -0,0 +1,59 @@
|
||||
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)
|
||||
}
|
||||
7
components/README.md
Normal file
7
components/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# COMPONENTS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
The components directory contains your Vue.js Components.
|
||||
|
||||
_Nuxt.js doesn't supercharge these components._
|
||||
84
components/collections/addCollection.vue
Normal file
84
components/collections/addCollection.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<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 "../../functions/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")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
73
components/collections/addFolder.vue
Normal file
73
components/collections/addFolder.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<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>
|
||||
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()
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
120
components/collections/collection.vue
Normal file
120
components/collections/collection.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<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</i>
|
||||
<span>{{ collection.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('more')">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="$emit('add-folder')" v-close-popover>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("new_folder") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="$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 v-show="showChildren">
|
||||
<ul>
|
||||
<li v-for="(folder, index) in collection.folders" :key="folder.name">
|
||||
<folder
|
||||
:folder="folder"
|
||||
:folderIndex="index"
|
||||
:collection-index="collectionIndex"
|
||||
@edit-folder="editFolder(collectionIndex, folder, index)"
|
||||
@edit-request="$emit('edit-request', $event)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="collection.folders.length === 0 && collection.requests.length === 0">
|
||||
<label>{{ $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"
|
||||
@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>
|
||||
export default {
|
||||
components: {
|
||||
folder: () => import("./folder"),
|
||||
request: () => import("./request"),
|
||||
},
|
||||
props: {
|
||||
collectionIndex: Number,
|
||||
collection: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showChildren: false,
|
||||
selectedFolder: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
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,
|
||||
})
|
||||
},
|
||||
editFolder(collectionIndex, folder, folderIndex) {
|
||||
this.$emit("edit-folder", { collectionIndex, folder, folderIndex })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
81
components/collections/editCollection.vue
Normal file
81
components/collections/editCollection.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<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>
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingCollection: Object,
|
||||
editingCollectionIndex: Number,
|
||||
},
|
||||
components: {
|
||||
modal: () => import("../../components/ui/modal"),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
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")
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
71
components/collections/editFolder.vue
Normal file
71
components/collections/editFolder.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<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>
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
collection: Object,
|
||||
collectionIndex: Number,
|
||||
folder: Object,
|
||||
folderIndex: Number,
|
||||
},
|
||||
components: {
|
||||
modal: () => import("../../components/ui/modal"),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: undefined,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editFolder() {
|
||||
this.$store.commit("postwoman/editFolder", {
|
||||
collectionIndex: this.$props.collectionIndex,
|
||||
folder: { ...this.$props.folder, name: this.$data.name },
|
||||
folderIndex: this.$props.folderIndex,
|
||||
})
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
139
components/collections/editRequest.vue
Normal file
139
components/collections/editRequest.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<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>
|
||||
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: {
|
||||
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()
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
106
components/collections/folder.vue
Normal file
106
components/collections/folder.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<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="$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"
|
||||
@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>
|
||||
export default {
|
||||
props: {
|
||||
folder: Object,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number,
|
||||
},
|
||||
components: {
|
||||
request: () => import("./request"),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showChildren: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
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,
|
||||
})
|
||||
},
|
||||
editFolder() {
|
||||
this.$emit("edit-folder")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
288
components/collections/importExportCollections.vue
Normal file
288
components/collections/importExportCollections.vue
Normal file
@@ -0,0 +1,288 @@
|
||||
<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 "../../functions/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/importCollections", collections)
|
||||
this.fileImported()
|
||||
}
|
||||
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()
|
||||
}
|
||||
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()
|
||||
},
|
||||
fileImported() {
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
icon: "folder_shared",
|
||||
})
|
||||
},
|
||||
failedImport() {
|
||||
this.$toast.error(this.$t("import_failed"), {
|
||||
icon: "error",
|
||||
})
|
||||
},
|
||||
parsePostmanCollection(collection, folders = true) {
|
||||
let postwomanCollection = folders
|
||||
? [
|
||||
{
|
||||
name: "",
|
||||
folders: [],
|
||||
requests: [],
|
||||
},
|
||||
]
|
||||
: {
|
||||
name: "",
|
||||
requests: [],
|
||||
}
|
||||
for (let collectionItem of collection.item) {
|
||||
if (collectionItem.request) {
|
||||
if (postwomanCollection[0]) {
|
||||
postwomanCollection[0].name = collection.info ? collection.info.name : ""
|
||||
postwomanCollection[0].requests.push(this.parsePostmanRequest(collectionItem))
|
||||
} else {
|
||||
postwomanCollection.name = collection.name ? collection.name : ""
|
||||
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
|
||||
}
|
||||
} else if (collectionItem.item) {
|
||||
if (collectionItem.item[0]) {
|
||||
postwomanCollection[0].folders.push(this.parsePostmanCollection(collectionItem, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
return postwomanCollection
|
||||
},
|
||||
parsePostmanRequest(requestObject) {
|
||||
let pwRequest = {
|
||||
url: "",
|
||||
path: "",
|
||||
method: "",
|
||||
auth: "",
|
||||
httpUser: "",
|
||||
httpPassword: "",
|
||||
passwordFieldType: "password",
|
||||
bearerToken: "",
|
||||
headers: [],
|
||||
params: [],
|
||||
bodyParams: [],
|
||||
rawParams: "",
|
||||
rawInput: false,
|
||||
contentType: "",
|
||||
requestType: "",
|
||||
name: "",
|
||||
}
|
||||
|
||||
pwRequest.name = requestObject.name
|
||||
let requestObjectUrl = requestObject.request.url.raw.match(
|
||||
/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/
|
||||
)
|
||||
if (requestObjectUrl) {
|
||||
pwRequest.url = requestObjectUrl[1]
|
||||
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
|
||||
}
|
||||
pwRequest.method = requestObject.request.method
|
||||
let itemAuth = requestObject.request.auth ? requestObject.request.auth : ""
|
||||
let authType = itemAuth ? itemAuth.type : ""
|
||||
if (authType === "basic") {
|
||||
pwRequest.auth = "Basic Auth"
|
||||
pwRequest.httpUser =
|
||||
itemAuth.basic[0].key === "username" ? itemAuth.basic[0].value : itemAuth.basic[1].value
|
||||
pwRequest.httpPassword =
|
||||
itemAuth.basic[0].key === "password" ? itemAuth.basic[0].value : itemAuth.basic[1].value
|
||||
} else if (authType === "oauth2") {
|
||||
pwRequest.auth = "OAuth 2.0"
|
||||
pwRequest.bearerToken =
|
||||
itemAuth.oauth2[0].key === "accessToken"
|
||||
? itemAuth.oauth2[0].value
|
||||
: itemAuth.oauth2[1].value
|
||||
} else if (authType === "bearer") {
|
||||
pwRequest.auth = "Bearer Token"
|
||||
pwRequest.bearerToken = itemAuth.bearer[0].value
|
||||
}
|
||||
let requestObjectHeaders = requestObject.request.header
|
||||
if (requestObjectHeaders) {
|
||||
pwRequest.headers = requestObjectHeaders
|
||||
for (let header of pwRequest.headers) {
|
||||
delete header.name
|
||||
delete header.type
|
||||
}
|
||||
}
|
||||
let requestObjectParams = requestObject.request.url.query
|
||||
if (requestObjectParams) {
|
||||
pwRequest.params = requestObjectParams
|
||||
for (let param of pwRequest.params) {
|
||||
delete param.disabled
|
||||
}
|
||||
}
|
||||
if (requestObject.request.body) {
|
||||
if (requestObject.request.body.mode === "urlencoded") {
|
||||
let params = requestObject.request.body.urlencoded
|
||||
pwRequest.bodyParams = params ? params : []
|
||||
for (let param of pwRequest.bodyParams) {
|
||||
delete param.type
|
||||
}
|
||||
} else if (requestObject.request.body.mode === "raw") {
|
||||
pwRequest.rawInput = true
|
||||
pwRequest.rawParams = requestObject.request.body.raw
|
||||
}
|
||||
}
|
||||
return pwRequest
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
232
components/collections/index.vue
Normal file
232
components/collections/index.vue
Normal file
@@ -0,0 +1,232 @@
|
||||
<!--
|
||||
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</i>
|
||||
</button>
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="collections.length === 0" class="info">
|
||||
Create new collection
|
||||
</p>
|
||||
<virtual-list
|
||||
class="virtual-list"
|
||||
:class="{ filled: collections.length }"
|
||||
:size="152"
|
||||
:remain="Math.min(5, collections.length)"
|
||||
>
|
||||
<ul>
|
||||
<li v-for="(collection, index) in collections" :key="collection.name">
|
||||
<collection
|
||||
:collection-index="index"
|
||||
:collection="collection"
|
||||
@edit-collection="editCollection(collection, index)"
|
||||
@add-folder="addFolder(collection, index)"
|
||||
@edit-folder="editFolder($event)"
|
||||
@edit-request="editRequest($event)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="collections.length === 0">
|
||||
<label>Collections are empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
</virtual-list>
|
||||
<nuxt-link :to="localePath('doc')" :aria-label="$t('documentation')">
|
||||
<button class="icon">
|
||||
<i class="material-icons">books</i>
|
||||
<span>{{ $t("generate_docs") }}</span>
|
||||
</button>
|
||||
</nuxt-link>
|
||||
</pw-section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 286px);
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import collection from "./collection"
|
||||
import { fb } from "../../functions/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"),
|
||||
VirtualList: () => import("vue-virtual-scroll-list"),
|
||||
},
|
||||
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 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>
|
||||
66
components/collections/request.vue
Normal file
66
components/collections/request.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="selectRequest()" v-tooltip="$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>
|
||||
export default {
|
||||
props: {
|
||||
request: Object,
|
||||
collectionIndex: Number,
|
||||
folderIndex: Number,
|
||||
requestIndex: Number,
|
||||
},
|
||||
methods: {
|
||||
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,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
190
components/collections/saveRequestAs.vue
Normal file
190
components/collections/saveRequestAs.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<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 "../../functions/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
|
||||
},
|
||||
},
|
||||
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>
|
||||
92
components/environments/addEnvironment.vue
Normal file
92
components/environments/addEnvironment.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">{{ $t("new_environment") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
:placeholder="$t('my_new_environment')"
|
||||
@keyup.enter="addNewEnvironment"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button class="icon primary" @click="addNewEnvironment">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fb } from "../../functions/fb"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
components: {
|
||||
modal: () => import("../../components/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.$data.name = undefined
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
207
components/environments/editEnvironment.vue
Normal file
207
components/environments/editEnvironment.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">{{ $t("edit_environment") }}</h3>
|
||||
<div>
|
||||
<button class="icon" @click="hideModal">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="body">
|
||||
<ul>
|
||||
<li>
|
||||
<input
|
||||
type="text"
|
||||
v-model="name"
|
||||
:placeholder="editingEnvironment.name"
|
||||
@keyup.enter="saveEnvironment"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="variableList">{{ $t("env_variable_list") }}</label>
|
||||
<div>
|
||||
<button class="icon" @click="clearContent($event)" v-tooltip.bottom="$t('clear')">
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
id="variableList"
|
||||
readonly
|
||||
v-textarea-auto-height="variableString"
|
||||
v-model="variableString"
|
||||
:placeholder="$t('add_one_variable')"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(variable, index) in this.editingEnvCopy.variables" :key="index">
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('parameter_count', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="variable.key"
|
||||
@change="
|
||||
$store.commit('postwoman/setVariableKey', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="
|
||||
typeof variable.value === 'string' ? variable.value : JSON.stringify(variable.value)
|
||||
"
|
||||
@change="
|
||||
$store.commit('postwoman/setVariableValue', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="removeEnvironmentVariable(index)"
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
id="variable"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addEnvironmentVariable">
|
||||
<i class="material-icons">add</i>
|
||||
<span>{{ $t("add_new") }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="flex-wrap">
|
||||
<span></span>
|
||||
<span>
|
||||
<button class="icon" @click="hideModal">
|
||||
{{ $t("cancel") }}
|
||||
</button>
|
||||
<button class="icon primary" @click="saveEnvironment">
|
||||
{{ $t("save") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import textareaAutoHeight from "../../directives/textareaAutoHeight"
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
textareaAutoHeight,
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingEnvironment: Object,
|
||||
editingEnvironmentIndex: Number,
|
||||
},
|
||||
components: {
|
||||
modal: () => import("../../components/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: {
|
||||
clearContent(e) {
|
||||
this.$store.commit("postwoman/removeVariables", [])
|
||||
e.target.innerHTML = this.doneButton
|
||||
this.$toast.info(this.$t("cleared"), {
|
||||
icon: "clear_all",
|
||||
})
|
||||
setTimeout(() => (e.target.innerHTML = '<i class="material-icons">clear_all</i>'), 1000)
|
||||
},
|
||||
addEnvironmentVariable() {
|
||||
let value = { key: "", value: "" }
|
||||
this.$store.commit("postwoman/addVariable", value)
|
||||
},
|
||||
removeEnvironmentVariable(index) {
|
||||
let variableIndex = index
|
||||
const oldVariables = this.editingEnvCopy.variables.slice()
|
||||
const newVariables = this.editingEnvCopy.variables.filter(
|
||||
(variable, index) => variableIndex !== index
|
||||
)
|
||||
|
||||
this.$store.commit("postwoman/removeVariable", newVariables)
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
action: {
|
||||
text: this.$t("undo"),
|
||||
onClick: (e, toastObject) => {
|
||||
this.$store.commit("postwoman/removeVariable", oldVariables)
|
||||
toastObject.remove()
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
saveEnvironment() {
|
||||
if (!this.$data.name) {
|
||||
this.$toast.info(this.$t("invalid_environment_name"))
|
||||
return
|
||||
}
|
||||
const environmentUpdated = {
|
||||
...this.editingEnvCopy,
|
||||
name: this.$data.name,
|
||||
}
|
||||
this.$store.commit("postwoman/saveEnvironment", {
|
||||
environment: environmentUpdated,
|
||||
environmentIndex: this.$props.editingEnvironmentIndex,
|
||||
})
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
hideModal() {
|
||||
this.$data.name = undefined
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
57
components/environments/environment.vue
Normal file
57
components/environments/environment.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="flex-wrap">
|
||||
<div>
|
||||
<button class="icon" @click="$emit('select-environment')" v-tooltip="$t('use_environment')">
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
<span>{{ environment.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<v-popover>
|
||||
<button class="tooltip-target icon" v-tooltip="$t('more')">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="$emit('edit-environment')" v-close-popover>
|
||||
<i class="material-icons">create</i>
|
||||
<span>{{ $t("edit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="removeEnvironment" v-close-popover>
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t("delete") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: flex;
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid var(--brd-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
environment: Object,
|
||||
environmentIndex: Number,
|
||||
},
|
||||
methods: {
|
||||
removeEnvironment() {
|
||||
if (!confirm("Are you sure you want to remove this environment?")) return
|
||||
this.$store.commit("postwoman/removeEnvironment", this.environmentIndex)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
173
components/environments/importExportEnvironment.vue
Normal file
173
components/environments/importExportEnvironment.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<modal v-if="show" @close="hideModal">
|
||||
<div slot="header">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<h3 class="title">{{ $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 "../../functions/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()
|
||||
},
|
||||
importFromJSON() {
|
||||
let reader = new FileReader()
|
||||
reader.onload = event => {
|
||||
let content = event.target.result
|
||||
let importFileObj = JSON.parse(content)
|
||||
if (importFileObj["_postman_variable_scope"] === "environment") {
|
||||
this.importFromPostman(importFileObj)
|
||||
} else {
|
||||
this.importFromPostwoman(importFileObj)
|
||||
}
|
||||
}
|
||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
||||
},
|
||||
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()
|
||||
},
|
||||
fileImported() {
|
||||
this.$toast.info(this.$t("file_imported"), {
|
||||
icon: "folder_shared",
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
137
components/environments/index.vue
Normal file
137
components/environments/index.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<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">
|
||||
Create new environment
|
||||
</p>
|
||||
<virtual-list
|
||||
class="virtual-list"
|
||||
:class="{ filled: environments.length }"
|
||||
:size="152"
|
||||
:remain="Math.min(5, environments.length)"
|
||||
>
|
||||
<ul>
|
||||
<li v-for="(environment, index) in environments" :key="environment.name">
|
||||
<environment
|
||||
:environmentIndex="index"
|
||||
:environment="environment"
|
||||
@edit-environment="editEnvironment(environment, index)"
|
||||
@select-environment="$emit('use-environment', environment)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="environments.length === 0">
|
||||
<label>Environments are empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
</virtual-list>
|
||||
</pw-section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 241px);
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import environment from "./environment"
|
||||
import { fb } from "../../functions/fb"
|
||||
|
||||
const updateOnLocalStorage = (propertyName, property) =>
|
||||
window.localStorage.setItem(propertyName, JSON.stringify(property))
|
||||
|
||||
export default {
|
||||
components: {
|
||||
environment,
|
||||
"pw-section": () => import("../layout/section"),
|
||||
addEnvironment: () => import("./addEnvironment"),
|
||||
editEnvironment: () => import("./editEnvironment"),
|
||||
importExportEnvironment: () => import("./importExportEnvironment"),
|
||||
VirtualList: () => import("vue-virtual-scroll-list"),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModalImportExport: false,
|
||||
showModalAdd: false,
|
||||
showModalEdit: false,
|
||||
editingEnvironment: undefined,
|
||||
editingEnvironmentIndex: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
environments() {
|
||||
return this.$store.state.postwoman.environments
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
this._keyListener = function(e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
this.showModalImportExport = false
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", this._keyListener.bind(this))
|
||||
},
|
||||
methods: {
|
||||
displayModalAdd(shouldDisplay) {
|
||||
this.showModalAdd = shouldDisplay
|
||||
},
|
||||
displayModalEdit(shouldDisplay) {
|
||||
this.showModalEdit = shouldDisplay
|
||||
|
||||
if (!shouldDisplay) this.resetSelectedData()
|
||||
},
|
||||
displayModalImportExport(shouldDisplay) {
|
||||
this.showModalImportExport = shouldDisplay
|
||||
},
|
||||
editEnvironment(environment, environmentIndex) {
|
||||
this.$data.editingEnvironment = environment
|
||||
this.$data.editingEnvironmentIndex = environmentIndex
|
||||
this.displayModalEdit(true)
|
||||
this.syncEnvironments
|
||||
},
|
||||
resetSelectedData() {
|
||||
this.$data.editingEnvironment = undefined
|
||||
this.$data.editingEnvironmentIndex = undefined
|
||||
},
|
||||
syncEnvironments() {
|
||||
if (fb.currentUser !== null) {
|
||||
if (fb.currentSettings[1].value) {
|
||||
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
78
components/firebase/feeds.vue
Normal file
78
components/firebase/feeds.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<virtual-list
|
||||
v-if="fb.currentFeeds.length !== 0"
|
||||
class="virtual-list"
|
||||
:class="{ filled: fb.currentFeeds.length }"
|
||||
:size="180"
|
||||
:remain="Math.min(6, fb.currentFeeds.length)"
|
||||
>
|
||||
<ul v-for="feed in fb.currentFeeds" :key="feed.id" class="entry">
|
||||
<div class="show-on-large-screen">
|
||||
<li class="info">
|
||||
<label>
|
||||
{{ feed.label || $t("no_label") }}
|
||||
</label>
|
||||
</li>
|
||||
<button class="icon" @click="deleteFeed(feed)">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="show-on-large-screen">
|
||||
<li class="info clamb-3">
|
||||
<label>{{ feed.message || $t("empty") }}</label>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</virtual-list>
|
||||
<ul v-else>
|
||||
<li>
|
||||
<label class="info">{{ $t("empty") }}</label>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.virtual-list {
|
||||
max-height: calc(100vh - 294px);
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.entry {
|
||||
border-bottom: 1px dashed var(--brd-color);
|
||||
padding: 0 0 8px;
|
||||
}
|
||||
|
||||
.clamb-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { fb } from "../../functions/fb"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VirtualList: () => import("vue-virtual-scroll-list"),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fb,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteFeed(feed) {
|
||||
fb.deleteFeed(feed.id)
|
||||
this.$toast.error(this.$t("deleted"), {
|
||||
icon: "delete",
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
69
components/firebase/inputform.vue
Normal file
69
components/firebase/inputform.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul>
|
||||
<div class="show-on-large-screen">
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('label')"
|
||||
type="text"
|
||||
autofocus
|
||||
v-model="message"
|
||||
:placeholder="$t('paste_a_note')"
|
||||
@keyup.enter="formPost"
|
||||
/>
|
||||
</li>
|
||||
</div>
|
||||
<div class="show-on-large-screen">
|
||||
<li>
|
||||
<input
|
||||
:aria-label="$t('label')"
|
||||
type="text"
|
||||
autofocus
|
||||
v-model="label"
|
||||
:placeholder="$t('label')"
|
||||
@keyup.enter="formPost"
|
||||
/>
|
||||
</li>
|
||||
<button
|
||||
class="icon"
|
||||
:disabled="!(this.message || this.label)"
|
||||
value="Save"
|
||||
@click="formPost"
|
||||
>
|
||||
<i class="material-icons">add</i>
|
||||
<span>Add</span>
|
||||
</button>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
ul,
|
||||
ol {
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { fb } from "../../functions/fb"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
message: null,
|
||||
label: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formPost() {
|
||||
if (!(this.message || this.label)) {
|
||||
return
|
||||
}
|
||||
fb.writeFeeds(this.message, this.label)
|
||||
this.message = null
|
||||
this.label = null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
262
components/firebase/login.vue
Normal file
262
components/firebase/login.vue
Normal file
@@ -0,0 +1,262 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<button class="icon" @click="signInWithGoogle" v-close-popover>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Google</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="signInWithGithub" v-close-popover>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||
/>
|
||||
</svg>
|
||||
<span>GitHub</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import firebase from "firebase/app"
|
||||
import { fb } from "../../functions/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.
|
||||
var pendingCred = err.credential
|
||||
// The provider account's email address.
|
||||
var email = err.email
|
||||
// Get sign-in methods for this email.
|
||||
firebase
|
||||
.auth()
|
||||
.fetchSignInMethodsForEmail(email)
|
||||
.then(function(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.
|
||||
var password = promptUserForPassword() // TODO: implement promptUserForPassword.
|
||||
auth
|
||||
.signInWithEmailAndPassword(email, password)
|
||||
.then(function(user) {
|
||||
// Step 4a.
|
||||
return user.linkWithCredential(pendingCred)
|
||||
})
|
||||
.then(function() {
|
||||
// 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.
|
||||
var 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(function(result) {
|
||||
// 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.
|
||||
result.user
|
||||
.linkAndRetrieveDataWithCredential(pendingCred)
|
||||
.then(function(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.
|
||||
var pendingCred = err.credential
|
||||
// The provider account's email address.
|
||||
var email = err.email
|
||||
// Get sign-in methods for this email.
|
||||
firebase
|
||||
.auth()
|
||||
.fetchSignInMethodsForEmail(email)
|
||||
.then(function(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.
|
||||
var password = promptUserForPassword() // TODO: implement promptUserForPassword.
|
||||
firebase
|
||||
.auth()
|
||||
.signInWithEmailAndPassword(email, password)
|
||||
.then(function(user) {
|
||||
// Step 4a.
|
||||
return user.linkWithCredential(pendingCred)
|
||||
})
|
||||
.then(function() {
|
||||
// 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.
|
||||
var 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(function(result) {
|
||||
// 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.
|
||||
result.user
|
||||
.linkAndRetrieveDataWithCredential(pendingCred)
|
||||
.then(function(usercred) {
|
||||
self.showLoginSuccess()
|
||||
})
|
||||
})
|
||||
|
||||
toastObject.remove()
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
38
components/firebase/logout.vue
Normal file
38
components/firebase/logout.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<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 "../../functions/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>
|
||||
38
components/graphql/argument.vue
Normal file
38
components/graphql/argument.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<span>
|
||||
<span class="argumentName">
|
||||
{{ argName }}
|
||||
</span>
|
||||
:
|
||||
<typelink :type="argType" :jumpTypeCallback="jumpCallback" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
<script>
|
||||
import typelink from "./typelink"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
typelink: typelink,
|
||||
},
|
||||
|
||||
props: {
|
||||
gqlArg: Object,
|
||||
},
|
||||
|
||||
computed: {
|
||||
argName() {
|
||||
return this.gqlArg.name
|
||||
},
|
||||
argType() {
|
||||
return this.gqlArg.type
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
jumpCallback(typeName) {},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
88
components/graphql/field.vue
Normal file
88
components/graphql/field.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="field-box">
|
||||
<div class="field-title">
|
||||
{{ fieldName }}
|
||||
<span v-if="fieldArgs.length > 0">
|
||||
(
|
||||
<span v-for="(field, index) in fieldArgs" :key="index">
|
||||
{{ field.name }}:
|
||||
<typelink :gqlType="field.type" :jumpTypeCallback="jumpTypeCallback" />
|
||||
<span v-if="index !== fieldArgs.length - 1">
|
||||
,
|
||||
</span>
|
||||
</span>
|
||||
) </span
|
||||
>:
|
||||
<typelink :gqlType="gqlField.type" :jumpTypeCallback="jumpTypeCallback" />
|
||||
</div>
|
||||
<div class="field-desc" v-if="gqlField.description">
|
||||
{{ gqlField.description }}
|
||||
</div>
|
||||
|
||||
<div class="field-deprecated" v-if="gqlField.isDeprecated">
|
||||
{{ $t("deprecated") }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.field-box {
|
||||
padding: 16px;
|
||||
margin: 4px;
|
||||
border-bottom: 1px dashed var(--brd-color);
|
||||
}
|
||||
|
||||
.field-deprecated {
|
||||
background-color: yellow;
|
||||
color: black;
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
margin: 4px 0;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.field-desc {
|
||||
color: var(--fg-light-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import typelink from "./typelink"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
typelink: typelink,
|
||||
},
|
||||
|
||||
props: {
|
||||
gqlField: Object,
|
||||
jumpTypeCallback: Function,
|
||||
},
|
||||
|
||||
computed: {
|
||||
fieldString() {
|
||||
const args = (this.gqlField.args || []).reduce((acc, arg, index) => {
|
||||
return (
|
||||
acc +
|
||||
`${arg.name}: ${arg.type.toString()}${
|
||||
index !== this.gqlField.args.length - 1 ? ", " : ""
|
||||
}`
|
||||
)
|
||||
}, "")
|
||||
const argsString = args.length > 0 ? `(${args})` : ""
|
||||
return `${this.gqlField.name}${argsString}: ${this.gqlField.type.toString()}`
|
||||
},
|
||||
|
||||
fieldName() {
|
||||
return this.gqlField.name
|
||||
},
|
||||
|
||||
fieldArgs() {
|
||||
return this.gqlField.args || []
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
226
components/graphql/queryeditor.vue
Normal file
226
components/graphql/queryeditor.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<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 "~/functions/syntax/gqlQueryLangMode"
|
||||
|
||||
import * as gql from "graphql"
|
||||
import { getAutocompleteSuggestions } from "graphql-language-service-interface"
|
||||
import debounce from "../../functions/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>
|
||||
44
components/graphql/type.vue
Normal file
44
components/graphql/type.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="type-box">
|
||||
<div class="type-title">{{ gqlType.name }}</div>
|
||||
<div class="type-desc" v-if="gqlType.description">
|
||||
{{ gqlType.description }}
|
||||
</div>
|
||||
|
||||
<div v-if="gqlType.getFields">
|
||||
<h5>{{ $t("fields") }}</h5>
|
||||
<div v-for="field in gqlType.getFields()" :key="field.name">
|
||||
<gql-field :gqlField="field" :jumpTypeCallback="jumpTypeCallback" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.type-box {
|
||||
padding: 16px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.type-desc {
|
||||
color: var(--fg-light-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.type-title {
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {
|
||||
"gql-field": () => import("./field"),
|
||||
},
|
||||
|
||||
props: {
|
||||
gqlType: {},
|
||||
jumpTypeCallback: Function,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
34
components/graphql/typelink.vue
Normal file
34
components/graphql/typelink.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<span class="typelink" @click="jumpToType">{{ typeString }}</span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.typelink {
|
||||
color: var(--ac-color);
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
gqlType: null,
|
||||
// (typeName: string) => void
|
||||
jumpTypeCallback: Function,
|
||||
},
|
||||
|
||||
computed: {
|
||||
typeString() {
|
||||
return this.gqlType.toString()
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
jumpToType() {
|
||||
this.jumpTypeCallback(this.gqlType)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
159
components/layout/contributors.vue
Normal file
159
components/layout/contributors.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div>
|
||||
<p class="info">
|
||||
{{ $t("donate_info1") }}
|
||||
</p>
|
||||
<p class="info">
|
||||
{{ $t("donate_info2") }}
|
||||
</p>
|
||||
<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>
|
||||
🔥 HOT
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://www.paypal.me/liyascthomas"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('one_time')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">payment</i>
|
||||
<span>{{ $t("paypal") }}</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://www.patreon.com/liyasthomas"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
v-tooltip.right="$t('recurring')"
|
||||
>
|
||||
<button class="icon">
|
||||
<i class="material-icons">local_parking</i>
|
||||
<span>{{ $t("patreon") }}</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div 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</span>
|
||||
</button>
|
||||
</a>
|
||||
🎉 NEW
|
||||
</span>
|
||||
</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://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="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;
|
||||
flex-flow: row nowrap;
|
||||
overflow: auto;
|
||||
padding: 8px 0;
|
||||
margin: 8px 0;
|
||||
max-width: calc(100vw - 72px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
</script>
|
||||
98
components/layout/footer.vue
Normal file
98
components/layout/footer.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<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:liyascthomas@gmail.com" target="_blank" rel="noopener">
|
||||
<button class="icon" v-tooltip="$t('contact_us')">
|
||||
<i class="material-icons">email</i>
|
||||
</button>
|
||||
</a>
|
||||
<v-popover>
|
||||
<button class="icon" v-tooltip="$t('choose_language')">
|
||||
<i class="material-icons">translate</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div v-for="locale in availableLocales" :key="locale.code">
|
||||
<nuxt-link :to="switchLocalePath(locale.code)">
|
||||
<button class="icon" v-close-popover>
|
||||
{{ locale.name }}
|
||||
</button>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
</v-popover>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
</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(i => i.code !== this.$i18n.locale)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
416
components/layout/header.vue
Normal file
416
components/layout/header.vue
Normal file
@@ -0,0 +1,416 @@
|
||||
<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>
|
||||
<a
|
||||
href="https://github.com/liyasthomas/postwoman"
|
||||
target="_blank"
|
||||
aria-label="GitHub"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" aria-label="GitHub" v-tooltip="'GitHub'">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="material-icons"
|
||||
>
|
||||
<path
|
||||
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</a>
|
||||
<button
|
||||
class="icon"
|
||||
id="installPWA"
|
||||
@click.prevent="showInstallPrompt()"
|
||||
v-tooltip="$t('install_pwa')"
|
||||
>
|
||||
<i class="material-icons">offline_bolt</i>
|
||||
</button>
|
||||
<v-popover v-if="fb.currentUser === null">
|
||||
<button class="icon" v-tooltip="$t('login_with')">
|
||||
<i class="material-icons">vpn_key</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">more_vert</i>
|
||||
</button>
|
||||
<template slot="popover">
|
||||
<div>
|
||||
<button class="icon" @click="showExtensions = true" v-close-popover>
|
||||
<i class="material-icons">extension</i>
|
||||
<span>{{ $t("extensions") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="showShortcuts = true" v-close-popover>
|
||||
<i class="material-icons">keyboard</i>
|
||||
<span>{{ $t("shortcuts") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="icon" @click="showSupport = true" v-close-popover>
|
||||
<i class="material-icons">favorite</i>
|
||||
<span>{{ $t("support_us") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
onClick="window.open('https://twitter.com/share?text=👽 Postwoman • A free, fast and beautiful API request builder - Web alternative to Postman - Helps you create requests faster, saving precious time on development.&url=https://postwoman.io&hashtags=postwoman&via=liyasthomas');"
|
||||
v-close-popover
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"
|
||||
/>
|
||||
</svg>
|
||||
<span>{{ $t("tweet") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="navigatorShare"
|
||||
class="icon"
|
||||
@click="nativeShare"
|
||||
v-close-popover
|
||||
v-tooltip="$t('more')"
|
||||
>
|
||||
<i class="material-icons">share</i>
|
||||
</button>
|
||||
</div>
|
||||
</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"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm8.003 8.657c-1.276-3.321-4.46-4.605-5.534-4.537 3.529 1.376 4.373 6.059 4.06 7.441-.307-1.621-1.286-3.017-1.872-3.385 3.417 8.005-4.835 10.465-7.353 7.687.649.168 1.931.085 2.891-.557.898-.602.983-.638 1.56-.683.686-.053-.041-1.406-1.539-1.177-.616.094-1.632.819-2.88.341-1.508-.576-1.46-2.634.096-2.015.337-.437.088-1.263.088-1.263.452-.414 1.022-.706 1.37-.911.228-.135.829-.507.795-1.23-.123-.096-.32-.219-.766-.193-1.736.11-1.852-.518-1.967-.808.078-.668.524-1.534 1.361-1.931-1.257-.193-2.28.397-2.789 1.154-.809-.174-1.305-.183-2.118-.031-.316-.24-.666-.67-.878-1.181 1.832-2.066 4.499-3.378 7.472-3.378 5.912 0 8.263 4.283 8.003 6.657z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Firefox</span>
|
||||
<span class="icon" v-if="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"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M2.897 4.181c2.43-2.828 5.763-4.181 9.072-4.181 4.288 0 8.535 2.273 10.717 6.554-2.722.001-6.984 0-9.293 0-1.674.001-2.755-.037-3.926.579-1.376.724-2.415 2.067-2.777 3.644l-3.793-6.596zm5.11 7.819c0 2.2 1.789 3.99 3.988 3.99s3.988-1.79 3.988-3.99-1.789-3.991-3.988-3.991-3.988 1.791-3.988 3.991zm5.536 5.223c-2.238.666-4.858-.073-6.293-2.549-1.095-1.891-3.989-6.933-5.305-9.225-1.33 2.04-1.945 4.294-1.945 6.507 0 5.448 3.726 10.65 9.673 11.818l3.87-6.551zm2.158-9.214c1.864 1.734 2.271 4.542 1.007 6.719-.951 1.641-3.988 6.766-5.46 9.248 7.189.443 12.752-5.36 12.752-11.972 0-1.313-.22-2.66-.69-3.995h-7.609z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Chrome</span>
|
||||
<span class="icon" v-if="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 "../../functions/strategies/ExtensionStrategy"
|
||||
import { getPlatformSpecialKey } from "../../functions/platformutils"
|
||||
import firebase from "firebase/app"
|
||||
import { fb } from "../../functions/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 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)
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
}, 15000)
|
||||
|
||||
this._keyListener = function (e) {
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
this.showExtensions = this.showShortcuts = this.showSupport = false
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", this._keyListener.bind(this))
|
||||
})()
|
||||
},
|
||||
|
||||
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>
|
||||
479
components/layout/history.vue
Normal file
479
components/layout/history.vue
Normal file
@@ -0,0 +1,479 @@
|
||||
<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>
|
||||
<virtual-list
|
||||
class="virtual-list"
|
||||
:class="{ filled: filteredHistory.length }"
|
||||
:size="185"
|
||||
:remain="Math.min(5, 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>
|
||||
</virtual-list>
|
||||
<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">
|
||||
{{ $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">
|
||||
{{ $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 - 294px);
|
||||
|
||||
[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 "../../functions/fb"
|
||||
|
||||
const updateOnLocalStorage = (propertyName, property) =>
|
||||
window.localStorage.setItem(propertyName, JSON.stringify(property))
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"pw-section": () => import("../layout/section"),
|
||||
VirtualList: () => import("vue-virtual-scroll-list"),
|
||||
},
|
||||
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>
|
||||
56
components/layout/logo.vue
Normal file
56
components/layout/logo.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 612.001 612.001"
|
||||
style="enable-background:new 0 0 612.001 612.001;"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<defs id="defs11" />
|
||||
<g id="g3826" transform="translate(-516.40798,-163.88978)">
|
||||
<circle
|
||||
:fill="color"
|
||||
transform="scale(1,-1)"
|
||||
style="stroke-width:1.19531453"
|
||||
r="178.70923"
|
||||
cy="-501.55591"
|
||||
cx="822.40845"
|
||||
id="circle3814"
|
||||
/>
|
||||
<g id="g3820" transform="translate(516.40798,163.89028)">
|
||||
<g id="g3818">
|
||||
<path
|
||||
:fill="color"
|
||||
id="path3816"
|
||||
data-old_color="#202124"
|
||||
class="active-path"
|
||||
data-original="#202124"
|
||||
d="M 64.601,236.822 C 64.601,394.256 192.786,612 306.001,612 412.582,612 547.4,394.256 547.4,236.822 547.4,79.388 439.322,0 306,0 172.678,0 64.601,79.388 64.601,236.822 Z m 304.12,116.415 c 29.475,-29.475 70.598,-40.195 108.552,-32.173 8.021,37.954 -2.698,79.077 -32.173,108.552 -29.475,29.475 -70.598,40.195 -108.552,32.173 -8.022,-37.955 2.698,-79.078 32.173,-108.552 z M 134.727,321.063 c 37.954,-8.021 79.077,2.698 108.552,32.173 29.475,29.475 40.195,70.598 32.173,108.552 -37.954,8.021 -79.077,-2.698 -108.552,-32.173 -29.475,-29.476 -40.194,-70.598 -32.173,-108.552 z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#circle3814 {
|
||||
/* fill: var(--fg-color); */
|
||||
fill: transparent;
|
||||
}
|
||||
/* #path3816 {
|
||||
fill: var(--bg-color);
|
||||
} */
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
55
components/layout/section.vue
Normal file
55
components/layout/section.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<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.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>
|
||||
304
components/layout/sidenav.vue
Normal file
304
components/layout/sidenav.vue
Normal file
@@ -0,0 +1,304 @@
|
||||
<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">settings_input_hdmi</i>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
:to="localePath('graphql')"
|
||||
:class="linkActive('/graphql')"
|
||||
v-tooltip.right="$t('graphql')"
|
||||
:aria-label="$t('graphql')"
|
||||
>
|
||||
<svg
|
||||
class="material-icons"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 400 400"
|
||||
>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="122"
|
||||
y="-0.4"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 163.3196 363.3136)"
|
||||
width="16.6"
|
||||
height="320.3"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="39.8" y="272.2" width="320.3" height="16.6" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="37.9"
|
||||
y="312.2"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 83.0693 663.3409)"
|
||||
width="185"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="177.1"
|
||||
y="71.1"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 463.3409 283.0693)"
|
||||
width="185"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="122.1"
|
||||
y="-13"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 126.7903 232.1221)"
|
||||
width="16.6"
|
||||
height="185"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="109.6"
|
||||
y="151.6"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 266.0828 473.3766)"
|
||||
width="320.3"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g><rect x="52.5" y="107.5" width="16.6" height="185" /></g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="330.9" y="107.5" width="16.6" height="185" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="262.4"
|
||||
y="240.1"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 126.7953 714.2875)"
|
||||
width="14.5"
|
||||
height="160.9"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
d="M369.5,297.9c-9.6,16.7-31,22.4-47.7,12.8c-16.7-9.6-22.4-31-12.8-47.7c9.6-16.7,31-22.4,47.7-12.8C373.5,259.9,379.2,281.2,369.5,297.9"
|
||||
/>
|
||||
<path
|
||||
d="M90.9,137c-9.6,16.7-31,22.4-47.7,12.8c-16.7-9.6-22.4-31-12.8-47.7c9.6-16.7,31-22.4,47.7-12.8C94.8,99,100.5,120.3,90.9,137"
|
||||
/>
|
||||
<path
|
||||
d="M30.5,297.9c-9.6-16.7-3.9-38,12.8-47.7c16.7-9.6,38-3.9,47.7,12.8c9.6,16.7,3.9,38-12.8,47.7C61.4,320.3,40.1,314.6,30.5,297.9"
|
||||
/>
|
||||
<path
|
||||
d="M309.1,137c-9.6-16.7-3.9-38,12.8-47.7c16.7-9.6,38-3.9,47.7,12.8c9.6,16.7,3.9,38-12.8,47.7C340.1,159.4,318.7,153.7,309.1,137"
|
||||
/>
|
||||
<path
|
||||
d="M200,395.8c-19.3,0-34.9-15.6-34.9-34.9c0-19.3,15.6-34.9,34.9-34.9c19.3,0,34.9,15.6,34.9,34.9C234.9,380.1,219.3,395.8,200,395.8"
|
||||
/>
|
||||
<path
|
||||
d="M200,74c-19.3,0-34.9-15.6-34.9-34.9c0-19.3,15.6-34.9,34.9-34.9c19.3,0,34.9,15.6,34.9,34.9C234.9,58.4,219.3,74,200,74"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
:to="localePath('doc')"
|
||||
:class="linkActive('/doc')"
|
||||
v-tooltip.right="$t('documentation')"
|
||||
:aria-label="$t('documentation')"
|
||||
>
|
||||
<i class="material-icons">books</i>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
:to="localePath('settings')"
|
||||
:class="linkActive('/settings')"
|
||||
v-tooltip.right="$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 === '/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 === '/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 === '/doc'">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#collections" v-tooltip.right="$t('collections')">
|
||||
<i class="material-icons">folder</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#documentation" v-tooltip.right="'Documentation'">
|
||||
<i class="material-icons">insert_drive_file</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div v-else-if="$route.path === '/settings'">
|
||||
<nav class="secondary-nav">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#account" v-tooltip.right="$t('account')">
|
||||
<i class="material-icons">person</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#theme" v-tooltip.right="$t('theme')">
|
||||
<i class="material-icons">brush</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#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>
|
||||
|
||||
<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(link => {
|
||||
let section = document.querySelector(link.hash)
|
||||
|
||||
if (
|
||||
section &&
|
||||
section.offsetTop <= fromTop &&
|
||||
section.offsetTop + section.offsetHeight > fromTop
|
||||
) {
|
||||
link.classList.add("current")
|
||||
} else {
|
||||
link.classList.remove("current")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route() {
|
||||
// this.$toast.clear();
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
58
components/realtime/log.vue
Normal file
58
components/realtime/log.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<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;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { getSourcePrefix } from "~/functions/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>
|
||||
261
components/realtime/mqtt.vue
Normal file
261
components/realtime/mqtt.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<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 "~/functions/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>
|
||||
227
components/realtime/socketio.vue
Normal file
227
components/realtime/socketio.vue
Normal file
@@ -0,0 +1,227 @@
|
||||
<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 "~/functions/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>
|
||||
160
components/realtime/sse.vue
Normal file
160
components/realtime/sse.vue
Normal file
@@ -0,0 +1,160 @@
|
||||
<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 { sseValid } from "~/functions/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 sseValid(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 = event => {
|
||||
this.events.log.push({
|
||||
payload: event.data,
|
||||
source: "server",
|
||||
ts: new Date().toLocaleTimeString(),
|
||||
})
|
||||
}
|
||||
} catch (ex) {
|
||||
this.handleSSEError(ex)
|
||||
this.$toast.error(this.$t("something_went_wrong"), {
|
||||
icon: "error",
|
||||
})
|
||||
}
|
||||
} else {
|
||||
this.events.log = [
|
||||
{
|
||||
payload: this.$t("browser_support_sse"),
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString(),
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
handleSSEError(error) {
|
||||
this.stop()
|
||||
this.connectionSSEState = false
|
||||
this.events.log.push({
|
||||
payload: this.$t("error_occurred"),
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString(),
|
||||
})
|
||||
if (error !== null)
|
||||
this.events.log.push({
|
||||
payload: error,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString(),
|
||||
})
|
||||
},
|
||||
stop() {
|
||||
this.sse.onclose()
|
||||
this.sse.close()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
216
components/realtime/websocket.vue
Normal file
216
components/realtime/websocket.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<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 "~/functions/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 = (event) => {
|
||||
this.communication.log.push({
|
||||
payload: event.data,
|
||||
source: "server",
|
||||
ts: new Date().toLocaleTimeString(),
|
||||
})
|
||||
}
|
||||
} catch (ex) {
|
||||
this.handleError(ex)
|
||||
this.$toast.error(this.$t("something_went_wrong"), {
|
||||
icon: "error",
|
||||
})
|
||||
}
|
||||
},
|
||||
disconnect() {
|
||||
this.socket.close()
|
||||
},
|
||||
handleError(error) {
|
||||
this.disconnect()
|
||||
this.connectionState = false
|
||||
this.communication.log.push({
|
||||
payload: this.$t("error_occurred"),
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString(),
|
||||
})
|
||||
if (error !== null)
|
||||
this.communication.log.push({
|
||||
payload: error,
|
||||
source: "info",
|
||||
color: "#ff5555",
|
||||
ts: new Date().toLocaleTimeString(),
|
||||
})
|
||||
},
|
||||
sendMessage() {
|
||||
const message = this.communication.input
|
||||
this.socket.send(message)
|
||||
this.communication.log.push({
|
||||
payload: message,
|
||||
source: "client",
|
||||
ts: new Date().toLocaleTimeString(),
|
||||
})
|
||||
this.communication.input = ""
|
||||
},
|
||||
walkHistory(direction) {
|
||||
const clientMessages = this.communication.log.filter((msg) => msg.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>
|
||||
59
components/settings/swatch.vue
Normal file
59
components/settings/swatch.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<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>
|
||||
147
components/ui/ace-editor.vue
Normal file
147
components/ui/ace-editor.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<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 "../../functions/jsonParse"
|
||||
import debounce from "../../functions/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>
|
||||
217
components/ui/autocomplete.vue
Normal file
217
components/ui/autocomplete.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div class="autocomplete-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="placeholder"
|
||||
v-model="text"
|
||||
@input="updateSuggestions"
|
||||
@keyup="updateSuggestions"
|
||||
@click="updateSuggestions"
|
||||
@keydown="handleKeystroke"
|
||||
ref="acInput"
|
||||
:spellcheck="spellcheck"
|
||||
:autocapitalize="spellcheck"
|
||||
:autocorrect="spellcheck"
|
||||
/>
|
||||
<ul
|
||||
class="suggestions"
|
||||
v-if="suggestions.length > 0 && suggestionsVisible"
|
||||
:style="{ transform: `translate(${suggestionsOffsetLeft}px, 0)` }"
|
||||
>
|
||||
<li
|
||||
v-for="(suggestion, index) in suggestions"
|
||||
@click.prevent="forceSuggestion(suggestion)"
|
||||
:class="{ active: currentSuggestionIndex === index }"
|
||||
:key="index"
|
||||
>
|
||||
{{ suggestion }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.autocomplete-wrapper {
|
||||
position: relative;
|
||||
|
||||
input:focus + ul.suggestions,
|
||||
ul.suggestions:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul.suggestions {
|
||||
display: none;
|
||||
background-color: var(--atc-color);
|
||||
position: absolute;
|
||||
top: calc(100% - 4px);
|
||||
margin: 0 4px;
|
||||
left: 0;
|
||||
padding: 0;
|
||||
border-radius: 0 0 8px 8px;
|
||||
z-index: 9999;
|
||||
transition: transform 0.2s ease-out;
|
||||
box-shadow: 0 5px 30px rgba(black, 0.1);
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 8px 16px;
|
||||
font-size: 16px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-weight: 400;
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const KEY_TAB = 9
|
||||
const KEY_ESC = 27
|
||||
|
||||
const KEY_ARROW_UP = 38
|
||||
const KEY_ARROW_DOWN = 40
|
||||
|
||||
export default {
|
||||
props: {
|
||||
spellcheck: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false,
|
||||
},
|
||||
|
||||
source: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
text() {
|
||||
this.$emit("input", this.text)
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
text: this.value,
|
||||
selectionStart: 0,
|
||||
suggestionsOffsetLeft: 0,
|
||||
currentSuggestionIndex: -1,
|
||||
suggestionsVisible: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateSuggestions(event) {
|
||||
// Hide suggestions if ESC pressed.
|
||||
if (event.which && event.which === KEY_ESC) {
|
||||
event.preventDefault()
|
||||
this.suggestionsVisible = false
|
||||
this.currentSuggestionIndex = -1
|
||||
return
|
||||
}
|
||||
|
||||
// As suggestions is a reactive property, this implicitly
|
||||
// causes suggestions to update.
|
||||
this.selectionStart = this.$refs.acInput.selectionStart
|
||||
this.suggestionsOffsetLeft = 12 * this.selectionStart
|
||||
this.suggestionsVisible = true
|
||||
},
|
||||
|
||||
forceSuggestion(text) {
|
||||
let input = this.text.substring(0, this.selectionStart)
|
||||
this.text = input + text
|
||||
|
||||
this.selectionStart = this.text.length
|
||||
this.suggestionsVisible = true
|
||||
this.currentSuggestionIndex = -1
|
||||
},
|
||||
|
||||
handleKeystroke(event) {
|
||||
switch (event.which) {
|
||||
case KEY_ARROW_UP:
|
||||
event.preventDefault()
|
||||
this.currentSuggestionIndex =
|
||||
this.currentSuggestionIndex - 1 >= 0 ? this.currentSuggestionIndex - 1 : 0
|
||||
break
|
||||
|
||||
case KEY_ARROW_DOWN:
|
||||
event.preventDefault()
|
||||
this.currentSuggestionIndex =
|
||||
this.currentSuggestionIndex < this.suggestions.length - 1
|
||||
? this.currentSuggestionIndex + 1
|
||||
: this.suggestions.length - 1
|
||||
break
|
||||
|
||||
case KEY_TAB:
|
||||
event.preventDefault()
|
||||
let activeSuggestion = this.suggestions[
|
||||
this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0
|
||||
]
|
||||
if (activeSuggestion) {
|
||||
let input = this.text.substring(0, this.selectionStart)
|
||||
this.text = input + activeSuggestion
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Gets the suggestions list to be displayed under the input box.
|
||||
*
|
||||
* @returns {default.props.source|{type, required}}
|
||||
*/
|
||||
suggestions() {
|
||||
let input = this.text.substring(0, this.selectionStart)
|
||||
|
||||
return (
|
||||
this.source
|
||||
.filter(entry => {
|
||||
return (
|
||||
entry.toLowerCase().startsWith(input.toLowerCase()) &&
|
||||
input.toLowerCase() !== entry.toLowerCase()
|
||||
)
|
||||
})
|
||||
// Cut off the part that's already been typed.
|
||||
.map(entry => entry.substring(this.selectionStart))
|
||||
// We only want the top 6 suggestions.
|
||||
.slice(0, 6)
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.updateSuggestions({
|
||||
target: this.$refs.acInput,
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
101
components/ui/modal.vue
Normal file
101
components/ui/modal.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<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: 16px;
|
||||
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>
|
||||
36
components/ui/tab.vue
Normal file
36
components/ui/tab.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<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.name.toLowerCase().replace(/ /g, "-")}`
|
||||
// },
|
||||
// },
|
||||
|
||||
mounted() {
|
||||
this.isActive = this.selected
|
||||
},
|
||||
}
|
||||
</script>
|
||||
96
components/ui/tabs.vue
Normal file
96
components/ui/tabs.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="tabs-wrapper">
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li v-for="tab in tabs" :class="{ 'is-active': tab.isActive }">
|
||||
<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: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
.material-icons {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
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>
|
||||
98
components/ui/toggle.vue
Normal file
98
components/ui/toggle.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<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>
|
||||
9
cypress.json
Normal file
9
cypress.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"integrationFolder": "tests/e2e/integration",
|
||||
"screenshotsFolder": "tests/e2e/screenshots",
|
||||
"fixturesFolder": "tests/e2e/fixtures",
|
||||
"supportFile": "tests/e2e/support",
|
||||
"pluginsFile": false,
|
||||
"video": false
|
||||
}
|
||||
6
database.rules.json
Normal file
6
database.rules.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"rules": {
|
||||
".read": false,
|
||||
".write": false
|
||||
}
|
||||
}
|
||||
8
directives/textareaAutoHeight.js
Normal file
8
directives/textareaAutoHeight.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
name: "textareaAutoHeight",
|
||||
update({ scrollHeight, clientHeight, style }) {
|
||||
if (scrollHeight !== clientHeight) {
|
||||
style.minHeight = `${scrollHeight}px`
|
||||
}
|
||||
},
|
||||
}
|
||||
20
docker-compose.yml
Normal file
20
docker-compose.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
volumes:
|
||||
- "./.postwoman:/app/.postwoman"
|
||||
- "./assets:/app/assets"
|
||||
- "./directives:/app/directives"
|
||||
- "./layouts:/app/layouts"
|
||||
- "./middleware:/app/middleware"
|
||||
- "./pages:/app/pages"
|
||||
- "./plugins:/app/plugins"
|
||||
- "./static:/app/static"
|
||||
- "./store:/app/store"
|
||||
- "./components:/app/components"
|
||||
ports:
|
||||
- "3000:3000"
|
||||
command: "npm run dev"
|
||||
15
docs/index.html
Normal file
15
docs/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Postwoman</title>
|
||||
<meta http-equiv="refresh" content="0; url=https://postwoman.io" />
|
||||
<link rel="canonical" href="https://postwoman.io" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
Redirecting to postwoman.io
|
||||
</body>
|
||||
</html>
|
||||
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
18
firebase.json
Normal file
18
firebase.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
26
firestore.indexes.json
Normal file
26
firestore.indexes.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
// 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": []
|
||||
}
|
||||
14
firestore.rules
Normal file
14
firestore.rules
Normal file
@@ -0,0 +1,14 @@
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents {
|
||||
match /{document=**} {
|
||||
allow read, write: if request.auth.uid != null;
|
||||
}
|
||||
// Make sure the uid of the requesting user matches name of the user
|
||||
// document. The wildcard expression {userId} makes the userId variable
|
||||
// available in rules.
|
||||
match /users/{userId} {
|
||||
allow read, update, delete: if request.auth.uid == userId;
|
||||
allow create: if request.auth.uid != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
functions/.gitignore
vendored
Normal file
1
functions/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
21
functions/api.js
Normal file
21
functions/api.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// Docs on event and context https://www.netlify.com/docs/functions/#the-handler-method
|
||||
exports.handler = async (event, context) => {
|
||||
switch (event.httpMethod) {
|
||||
case "GET":
|
||||
try {
|
||||
const name = event.queryStringParameters.name || "World"
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ message: `Hello ${name}` }),
|
||||
}
|
||||
} catch (err) {
|
||||
return { statusCode: 500, body: err.toString() }
|
||||
}
|
||||
|
||||
default:
|
||||
return { statusCode: 405, body: "Method Not Allowed" }
|
||||
}
|
||||
}
|
||||
11
functions/editorutils.js
Normal file
11
functions/editorutils.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const mimeToMode = {
|
||||
"text/plain": "plain_text",
|
||||
"text/html": "html",
|
||||
"application/xml": "xml",
|
||||
"application/hal+json": "json",
|
||||
"application/json": "json",
|
||||
}
|
||||
|
||||
export function getEditorLangForMimeType(mimeType) {
|
||||
return mimeToMode[mimeType] || "plain_text"
|
||||
}
|
||||
221
functions/fb.js
Normal file
221
functions/fb.js
Normal file
@@ -0,0 +1,221 @@
|
||||
import firebase from "firebase/app"
|
||||
import "firebase/firestore"
|
||||
import "firebase/auth"
|
||||
|
||||
// Initialize Firebase, copied from cloud console
|
||||
const firebaseConfig = {
|
||||
apiKey: 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: collection,
|
||||
}
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("collections")
|
||||
.doc("sync")
|
||||
.set(cl)
|
||||
.catch((e) => console.error("error updating", cl, e))
|
||||
},
|
||||
writeEnvironments: async (environment) => {
|
||||
const ev = {
|
||||
updatedOn: new Date(),
|
||||
author: fb.currentUser.uid,
|
||||
author_name: fb.currentUser.displayName,
|
||||
author_image: fb.currentUser.photoURL,
|
||||
environment: environment,
|
||||
}
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("environments")
|
||||
.doc("sync")
|
||||
.set(ev)
|
||||
.catch((e) => console.error("error updating", ev, e))
|
||||
},
|
||||
}
|
||||
|
||||
// When a user logs in or out, save that in the store
|
||||
firebase.auth().onAuthStateChanged((user) => {
|
||||
if (user) {
|
||||
fb.currentUser = user
|
||||
fb.currentUser.providerData.forEach((profile) => {
|
||||
let us = {
|
||||
updatedOn: new Date(),
|
||||
provider: profile.providerId,
|
||||
name: profile.displayName,
|
||||
email: profile.email,
|
||||
photoUrl: profile.photoURL,
|
||||
uid: profile.uid,
|
||||
}
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.set(us)
|
||||
.catch((e) => console.error("error updating", us, e))
|
||||
})
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("feeds")
|
||||
.orderBy("createdOn", "desc")
|
||||
.onSnapshot((feedsRef) => {
|
||||
const feeds = []
|
||||
feedsRef.forEach((doc) => {
|
||||
const feed = doc.data()
|
||||
feed.id = doc.id
|
||||
feeds.push(feed)
|
||||
})
|
||||
fb.currentFeeds = feeds
|
||||
})
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("settings")
|
||||
.onSnapshot((settingsRef) => {
|
||||
const settings = []
|
||||
settingsRef.forEach((doc) => {
|
||||
const setting = doc.data()
|
||||
setting.id = doc.id
|
||||
settings.push(setting)
|
||||
})
|
||||
fb.currentSettings = settings
|
||||
})
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("history")
|
||||
.onSnapshot((historyRef) => {
|
||||
const history = []
|
||||
historyRef.forEach((doc) => {
|
||||
const entry = doc.data()
|
||||
entry.id = doc.id
|
||||
history.push(entry)
|
||||
})
|
||||
fb.currentHistory = history
|
||||
})
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("collections")
|
||||
.onSnapshot((collectionsRef) => {
|
||||
const collections = []
|
||||
collectionsRef.forEach((doc) => {
|
||||
const collection = doc.data()
|
||||
collection.id = doc.id
|
||||
collections.push(collection)
|
||||
})
|
||||
fb.currentCollections = collections[0].collection
|
||||
})
|
||||
|
||||
usersCollection
|
||||
.doc(fb.currentUser.uid)
|
||||
.collection("environments")
|
||||
.onSnapshot((environmentsRef) => {
|
||||
const environments = []
|
||||
environmentsRef.forEach((doc) => {
|
||||
const environment = doc.data()
|
||||
environment.id = doc.id
|
||||
environments.push(environment)
|
||||
})
|
||||
fb.currentEnvironments = environments[0].environment
|
||||
})
|
||||
} else {
|
||||
fb.currentUser = null
|
||||
}
|
||||
})
|
||||
124
functions/headers.js
Normal file
124
functions/headers.js
Normal file
@@ -0,0 +1,124 @@
|
||||
export const commonHeaders = [
|
||||
"WWW-Authenticate",
|
||||
"Authorization",
|
||||
"Proxy-Authenticate",
|
||||
"Proxy-Authorization",
|
||||
"Age",
|
||||
"Cache-Control",
|
||||
"Clear-Site-Data",
|
||||
"Expires",
|
||||
"Pragma",
|
||||
"Warning",
|
||||
"Accept-CH",
|
||||
"Accept-CH-Lifetime",
|
||||
"Early-Data",
|
||||
"Content-DPR",
|
||||
"DPR",
|
||||
"Device-Memory",
|
||||
"Save-Data",
|
||||
"Viewport-Width",
|
||||
"Width",
|
||||
"Last-Modified",
|
||||
"ETag",
|
||||
"If-Match",
|
||||
"If-None-Match",
|
||||
"If-Modified-Since",
|
||||
"If-Unmodified-Since",
|
||||
"Vary",
|
||||
"Connection",
|
||||
"Keep-Alive",
|
||||
"Accept",
|
||||
"Accept-Charset",
|
||||
"Accept-Encoding",
|
||||
"Accept-Language",
|
||||
"Expect",
|
||||
"Max-Forwards",
|
||||
"Cookie",
|
||||
"Set-Cookie",
|
||||
"Cookie2",
|
||||
"Set-Cookie2",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Access-Control-Allow-Credentials",
|
||||
"Access-Control-Allow-Headers",
|
||||
"Access-Control-Allow-Methods",
|
||||
"Access-Control-Expose-Headers",
|
||||
"Access-Control-Max-Age",
|
||||
"Access-Control-Request-Headers",
|
||||
"Access-Control-Request-Method",
|
||||
"Origin",
|
||||
"Service-Worker-Allowed",
|
||||
"Timing-Allow-Origin",
|
||||
"X-Permitted-Cross-Domain-Policies",
|
||||
"DNT",
|
||||
"Tk",
|
||||
"Content-Disposition",
|
||||
"Content-Length",
|
||||
"Content-Type",
|
||||
"Content-Encoding",
|
||||
"Content-Language",
|
||||
"Content-Location",
|
||||
"Forwarded",
|
||||
"X-Forwarded-For",
|
||||
"X-Forwarded-Host",
|
||||
"X-Forwarded-Proto",
|
||||
"Via",
|
||||
"Location",
|
||||
"From",
|
||||
"Host",
|
||||
"Referer",
|
||||
"Referrer-Policy",
|
||||
"User-Agent",
|
||||
"Allow",
|
||||
"Server",
|
||||
"Accept-Ranges",
|
||||
"Range",
|
||||
"If-Range",
|
||||
"Content-Range",
|
||||
"Cross-Origin-Opener-Policy",
|
||||
"Cross-Origin-Resource-Policy",
|
||||
"Content-Security-Policy",
|
||||
"Content-Security-Policy-Report-Only",
|
||||
"Expect-CT",
|
||||
"Feature-Policy",
|
||||
"Public-Key-Pins",
|
||||
"Public-Key-Pins-Report-Only",
|
||||
"Strict-Transport-Security",
|
||||
"Upgrade-Insecure-Requests",
|
||||
"X-Content-Type-Options",
|
||||
"X-Download-Options",
|
||||
"X-Frame-Options",
|
||||
"X-Powered-By",
|
||||
"X-XSS-Protection",
|
||||
"Last-Event-ID",
|
||||
"NEL",
|
||||
"Ping-From",
|
||||
"Ping-To",
|
||||
"Report-To",
|
||||
"Transfer-Encoding",
|
||||
"TE",
|
||||
"Trailer",
|
||||
"Sec-WebSocket-Key",
|
||||
"Sec-WebSocket-Extensions",
|
||||
"Sec-WebSocket-Accept",
|
||||
"Sec-WebSocket-Protocol",
|
||||
"Sec-WebSocket-Version",
|
||||
"Accept-Push-Policy",
|
||||
"Accept-Signature",
|
||||
"Alt-Svc",
|
||||
"Date",
|
||||
"Large-Allocation",
|
||||
"Link",
|
||||
"Push-Policy",
|
||||
"Retry-After",
|
||||
"Signature",
|
||||
"Signed-Headers",
|
||||
"Server-Timing",
|
||||
"SourceMap",
|
||||
"Upgrade",
|
||||
"X-DNS-Prefetch-Control",
|
||||
"X-Firefox-Spdy",
|
||||
"X-Pingback",
|
||||
"X-Requested-With",
|
||||
"X-Robots-Tag",
|
||||
"X-UA-Compatible",
|
||||
]
|
||||
8
functions/index.js
Normal file
8
functions/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// 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!");
|
||||
// });
|
||||
310
functions/jsonParse.js
Normal file
310
functions/jsonParse.js
Normal file
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* Copyright (c) 2019 GraphQL Contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This JSON parser simply walks the input, generating an AST. Use this in lieu
|
||||
* of JSON.parse if you need character offset parse errors and an AST parse tree
|
||||
* with location information.
|
||||
*
|
||||
* If an error is encountered, a SyntaxError will be thrown, with properties:
|
||||
*
|
||||
* - message: string
|
||||
* - start: int - the start inclusive offset of the syntax error
|
||||
* - end: int - the end exclusive offset of the syntax error
|
||||
*
|
||||
*/
|
||||
export default function jsonParse(str) {
|
||||
string = str
|
||||
strLen = str.length
|
||||
start = end = lastEnd = -1
|
||||
ch()
|
||||
lex()
|
||||
const ast = parseObj()
|
||||
expect("EOF")
|
||||
return ast
|
||||
}
|
||||
|
||||
let string
|
||||
let strLen
|
||||
let start
|
||||
let end
|
||||
let lastEnd
|
||||
let code
|
||||
let kind
|
||||
|
||||
function parseObj() {
|
||||
const nodeStart = start
|
||||
const members = []
|
||||
expect("{")
|
||||
if (!skip("}")) {
|
||||
do {
|
||||
members.push(parseMember())
|
||||
} while (skip(","))
|
||||
expect("}")
|
||||
}
|
||||
return {
|
||||
kind: "Object",
|
||||
start: nodeStart,
|
||||
end: lastEnd,
|
||||
members,
|
||||
}
|
||||
}
|
||||
|
||||
function parseMember() {
|
||||
const nodeStart = start
|
||||
const key = kind === "String" ? curToken() : null
|
||||
expect("String")
|
||||
expect(":")
|
||||
const value = parseVal()
|
||||
return {
|
||||
kind: "Member",
|
||||
start: nodeStart,
|
||||
end: lastEnd,
|
||||
key,
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
function parseArr() {
|
||||
const nodeStart = start
|
||||
const values = []
|
||||
expect("[")
|
||||
if (!skip("]")) {
|
||||
do {
|
||||
values.push(parseVal())
|
||||
} while (skip(","))
|
||||
expect("]")
|
||||
}
|
||||
return {
|
||||
kind: "Array",
|
||||
start: nodeStart,
|
||||
end: lastEnd,
|
||||
values,
|
||||
}
|
||||
}
|
||||
|
||||
function parseVal() {
|
||||
switch (kind) {
|
||||
case "[":
|
||||
return parseArr()
|
||||
case "{":
|
||||
return parseObj()
|
||||
case "String":
|
||||
case "Number":
|
||||
case "Boolean":
|
||||
case "Null":
|
||||
const token = curToken()
|
||||
lex()
|
||||
return token
|
||||
}
|
||||
return expect("Value")
|
||||
}
|
||||
|
||||
function curToken() {
|
||||
return { kind, start, end, value: JSON.parse(string.slice(start, end)) }
|
||||
}
|
||||
|
||||
function expect(str) {
|
||||
if (kind === str) {
|
||||
lex()
|
||||
return
|
||||
}
|
||||
|
||||
let found
|
||||
if (kind === "EOF") {
|
||||
found = "[end of file]"
|
||||
} else if (end - start > 1) {
|
||||
found = "`" + string.slice(start, end) + "`"
|
||||
} else {
|
||||
const match = string.slice(start).match(/^.+?\b/)
|
||||
found = "`" + (match ? match[0] : string[start]) + "`"
|
||||
}
|
||||
|
||||
throw syntaxError(`Expected ${str} but found ${found}.`)
|
||||
}
|
||||
|
||||
function syntaxError(message) {
|
||||
return { message, start, end }
|
||||
}
|
||||
|
||||
function skip(k) {
|
||||
if (kind === k) {
|
||||
lex()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function ch() {
|
||||
if (end < strLen) {
|
||||
end++
|
||||
code = end === strLen ? 0 : string.charCodeAt(end)
|
||||
}
|
||||
}
|
||||
|
||||
function lex() {
|
||||
lastEnd = end
|
||||
|
||||
while (code === 9 || code === 10 || code === 13 || code === 32) {
|
||||
ch()
|
||||
}
|
||||
|
||||
if (code === 0) {
|
||||
kind = "EOF"
|
||||
return
|
||||
}
|
||||
|
||||
start = end
|
||||
|
||||
switch (code) {
|
||||
// "
|
||||
case 34:
|
||||
kind = "String"
|
||||
return readString()
|
||||
// -, 0-9
|
||||
case 45:
|
||||
case 48:
|
||||
case 49:
|
||||
case 50:
|
||||
case 51:
|
||||
case 52:
|
||||
case 53:
|
||||
case 54:
|
||||
case 55:
|
||||
case 56:
|
||||
case 57:
|
||||
kind = "Number"
|
||||
return readNumber()
|
||||
// f
|
||||
case 102:
|
||||
if (string.slice(start, start + 5) !== "false") {
|
||||
break
|
||||
}
|
||||
end += 4
|
||||
ch()
|
||||
|
||||
kind = "Boolean"
|
||||
return
|
||||
// n
|
||||
case 110:
|
||||
if (string.slice(start, start + 4) !== "null") {
|
||||
break
|
||||
}
|
||||
end += 3
|
||||
ch()
|
||||
|
||||
kind = "Null"
|
||||
return
|
||||
// t
|
||||
case 116:
|
||||
if (string.slice(start, start + 4) !== "true") {
|
||||
break
|
||||
}
|
||||
end += 3
|
||||
ch()
|
||||
|
||||
kind = "Boolean"
|
||||
return
|
||||
}
|
||||
|
||||
kind = string[start]
|
||||
ch()
|
||||
}
|
||||
|
||||
function readString() {
|
||||
ch()
|
||||
while (code !== 34 && code > 31) {
|
||||
if (code === 92) {
|
||||
// \
|
||||
ch()
|
||||
switch (code) {
|
||||
case 34: // "
|
||||
case 47: // /
|
||||
case 92: // \
|
||||
case 98: // b
|
||||
case 102: // f
|
||||
case 110: // n
|
||||
case 114: // r
|
||||
case 116: // t
|
||||
ch()
|
||||
break
|
||||
case 117: // u
|
||||
ch()
|
||||
readHex()
|
||||
readHex()
|
||||
readHex()
|
||||
readHex()
|
||||
break
|
||||
default:
|
||||
throw syntaxError("Bad character escape sequence.")
|
||||
}
|
||||
} else if (end === strLen) {
|
||||
throw syntaxError("Unterminated string.")
|
||||
} else {
|
||||
ch()
|
||||
}
|
||||
}
|
||||
|
||||
if (code === 34) {
|
||||
ch()
|
||||
return
|
||||
}
|
||||
|
||||
throw syntaxError("Unterminated string.")
|
||||
}
|
||||
|
||||
function readHex() {
|
||||
if (
|
||||
(code >= 48 && code <= 57) || // 0-9
|
||||
(code >= 65 && code <= 70) || // A-F
|
||||
(code >= 97 && code <= 102) // a-f
|
||||
) {
|
||||
return ch()
|
||||
}
|
||||
throw syntaxError("Expected hexadecimal digit.")
|
||||
}
|
||||
|
||||
function readNumber() {
|
||||
if (code === 45) {
|
||||
// -
|
||||
ch()
|
||||
}
|
||||
|
||||
if (code === 48) {
|
||||
// 0
|
||||
ch()
|
||||
} else {
|
||||
readDigits()
|
||||
}
|
||||
|
||||
if (code === 46) {
|
||||
// .
|
||||
ch()
|
||||
readDigits()
|
||||
}
|
||||
|
||||
if (code === 69 || code === 101) {
|
||||
// E e
|
||||
ch()
|
||||
if (code === 43 || code === 45) {
|
||||
// + -
|
||||
ch()
|
||||
}
|
||||
readDigits()
|
||||
}
|
||||
}
|
||||
|
||||
function readDigits() {
|
||||
if (code < 48 || code > 57) {
|
||||
// 0 - 9
|
||||
throw syntaxError("Expected decimal digit.")
|
||||
}
|
||||
do {
|
||||
ch()
|
||||
} while (code >= 48 && code <= 57) // 0 - 9
|
||||
}
|
||||
19
functions/network.js
Normal file
19
functions/network.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import AxiosStrategy from "./strategies/AxiosStrategy"
|
||||
import ExtensionStrategy, { hasExtensionInstalled } from "./strategies/ExtensionStrategy"
|
||||
|
||||
const isExtensionsAllowed = ({ state }) =>
|
||||
typeof state.postwoman.settings.EXTENSIONS_ENABLED === "undefined" ||
|
||||
state.postwoman.settings.EXTENSIONS_ENABLED
|
||||
|
||||
const runAppropriateStrategy = (req, store) => {
|
||||
if (isExtensionsAllowed(store) && hasExtensionInstalled()) {
|
||||
return ExtensionStrategy(req, store)
|
||||
}
|
||||
|
||||
return AxiosStrategy(req, store)
|
||||
}
|
||||
|
||||
const sendNetworkRequest = (req, store) =>
|
||||
runAppropriateStrategy(req, store).finally(() => window.$nuxt.$loading.finish())
|
||||
|
||||
export { sendNetworkRequest }
|
||||
1915
functions/package-lock.json
generated
Normal file
1915
functions/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
functions/package.json
Normal file
22
functions/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
3
functions/platformutils.js
Normal file
3
functions/platformutils.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export function getPlatformSpecialKey() {
|
||||
return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? "⌘" : "Ctrl"
|
||||
}
|
||||
147
functions/postwomanTesting.js
Normal file
147
functions/postwomanTesting.js
Normal file
@@ -0,0 +1,147 @@
|
||||
const PASS = "PASS"
|
||||
const FAIL = "FAIL"
|
||||
const ERROR = "ERROR"
|
||||
|
||||
const styles = {
|
||||
[PASS]: { icon: "check", class: "success-response" },
|
||||
[FAIL]: { icon: "close", class: "cl-error-response" },
|
||||
[ERROR]: { icon: "close", class: "cl-error-response" },
|
||||
none: { icon: "", class: "" },
|
||||
}
|
||||
|
||||
// TODO: probably have to use a more global state for `test`
|
||||
|
||||
export default function runTestScriptWithVariables(script, variables) {
|
||||
let pw = {
|
||||
_errors: [],
|
||||
_testReports: [],
|
||||
_report: "",
|
||||
expect(value) {
|
||||
try {
|
||||
return expect(value, this._testReports)
|
||||
} catch (e) {
|
||||
pw._testReports.push({ result: ERROR, message: e })
|
||||
}
|
||||
},
|
||||
test: (descriptor, func) => test(descriptor, func, pw._testReports),
|
||||
// globals that the script is allowed to have access to.
|
||||
}
|
||||
Object.assign(pw, variables)
|
||||
|
||||
// run pre-request script within this function so that it has access to the pw object.
|
||||
new Function("pw", script)(pw)
|
||||
//
|
||||
const testReports = pw._testReports.map(item => {
|
||||
if (item.result) {
|
||||
item.styles = styles[item.result]
|
||||
} else {
|
||||
item.styles = styles.none
|
||||
}
|
||||
return item
|
||||
})
|
||||
return { report: pw._report, errors: pw._errors, testResults: testReports }
|
||||
}
|
||||
|
||||
function test(descriptor, func, _testReports) {
|
||||
_testReports.push({ startBlock: descriptor })
|
||||
try {
|
||||
func()
|
||||
} catch (e) {
|
||||
_testReports.push({ result: ERROR, message: e })
|
||||
}
|
||||
_testReports.push({ endBlock: true })
|
||||
|
||||
// TODO: Organize and generate text report of each {descriptor: true} section in testReports.
|
||||
// add checkmark or x depending on if each testReport is pass=true or pass=false
|
||||
}
|
||||
|
||||
function expect(expectValue, _testReports) {
|
||||
return new Expectation(expectValue, null, _testReports)
|
||||
}
|
||||
|
||||
class Expectation {
|
||||
constructor(expectValue, _not, _testReports) {
|
||||
this.expectValue = expectValue
|
||||
this.not = _not || new Expectation(this.expectValue, true, _testReports)
|
||||
this._testReports = _testReports // this values is used within Test.it, which wraps Expectation and passes _testReports value.
|
||||
this._satisfies = function(expectValue, targetValue) {
|
||||
// Used for testing if two values match the expectation, which could be === OR !==, depending on if not
|
||||
// was used. Expectation#_satisfies prevents the need to have an if(this.not) branch in every test method.
|
||||
// Signature is _satisfies([expectValue,] targetValue): if only one argument is given, it is assumed the targetValue, and expectValue is set to this.expectValue
|
||||
if (!targetValue) {
|
||||
targetValue = expectValue
|
||||
expectValue = this.expectValue
|
||||
}
|
||||
if (this.not === true) {
|
||||
// test the inverse. this.not is always truthly, but an Expectation that is inverted will always be strictly `true`
|
||||
return expectValue !== targetValue
|
||||
} else {
|
||||
return expectValue === targetValue
|
||||
}
|
||||
}
|
||||
}
|
||||
_fmtNot(message) {
|
||||
// given a string with "(not)" in it, replaces with "not" or "", depending if the expectation is expecting the positive or inverse (this._not)
|
||||
if (this.not === true) {
|
||||
return message.replace("(not)", "not ")
|
||||
} else {
|
||||
return message.replace("(not)", "")
|
||||
}
|
||||
}
|
||||
_fail(message) {
|
||||
this._testReports.push({ result: FAIL, message })
|
||||
}
|
||||
_pass(message) {
|
||||
this._testReports.push({ result: PASS })
|
||||
}
|
||||
// TEST METHODS DEFINED BELOW
|
||||
// these are the usual methods that would follow expect(...)
|
||||
toBe(value) {
|
||||
return this._satisfies(value)
|
||||
? this._pass()
|
||||
: this._fail(this._fmtNot(`Expected ${this.expectValue} (not)to be ${value}`))
|
||||
}
|
||||
toHaveProperty(value) {
|
||||
return this._satisfies(this.expectValue.hasOwnProperty(value), true)
|
||||
? this._pass()
|
||||
: this._fail(
|
||||
this._fmtNot(`Expected object ${this.expectValue} to (not)have property ${value}`)
|
||||
)
|
||||
}
|
||||
toBeLevel2xx() {
|
||||
const code = parseInt(this.expectValue)
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(`Expected 200-level status but could not parse value ${this.expectValue}`)
|
||||
}
|
||||
return this._satisfies(code >= 200 && code < 300)
|
||||
? this._pass()
|
||||
: this._fail(this._fmtNot(`Expected ${this.expectValue} to (not)be 200-level status`))
|
||||
}
|
||||
toBeLevel3xx() {
|
||||
const code = parseInt(this.expectValue)
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(`Expected 300-level status but could not parse value ${this.expectValue}`)
|
||||
}
|
||||
return this._satisfies(code >= 300 && code < 400)
|
||||
? this._pass()
|
||||
: this._fail(this._fmtNot(`Expected ${this.expectValue} to (not)be 300-level status`))
|
||||
}
|
||||
toBeLevel4xx() {
|
||||
const code = parseInt(this.expectValue)
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(`Expected 400-level status but could not parse value ${this.expectValue}`)
|
||||
}
|
||||
return this._satisfies(code >= 400 && code < 500)
|
||||
? this._pass()
|
||||
: this._fail(this._fmtNot(`Expected ${this.expectValue} to (not)be 400-level status`))
|
||||
}
|
||||
toBeLevel5xx() {
|
||||
const code = parseInt(this.expectValue)
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(`Expected 500-level status but could not parse value ${this.expectValue}`)
|
||||
}
|
||||
return this._satisfies(code >= 500 && code < 600)
|
||||
? this._pass()
|
||||
: this._fail(this._fmtNot(`Expected ${this.expectValue} to (not)be 500-level status`))
|
||||
}
|
||||
}
|
||||
20
functions/preRequest.js
Normal file
20
functions/preRequest.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export default function getEnvironmentVariablesFromScript(script) {
|
||||
let _variables = {}
|
||||
|
||||
// the pw object is the proxy by which pre-request scripts can pass variables to the request.
|
||||
// for security and control purposes, this is the only way a pre-request script should modify variables.
|
||||
let pw = {
|
||||
environment: {
|
||||
set: (key, value) => (_variables[key] = value),
|
||||
},
|
||||
env: {
|
||||
set: (key, value) => (_variables[key] = value),
|
||||
},
|
||||
// globals that the script is allowed to have access to.
|
||||
}
|
||||
|
||||
// run pre-request script within this function so that it has access to the pw object.
|
||||
new Function("pw", script)(pw)
|
||||
|
||||
return _variables
|
||||
}
|
||||
17
functions/requestParams.js
Normal file
17
functions/requestParams.js
Normal file
@@ -0,0 +1,17 @@
|
||||
export function hasPathParams(params) {
|
||||
return params.some((p) => p.type === "path")
|
||||
}
|
||||
|
||||
export function addPathParamsToVariables(params, variables) {
|
||||
params
|
||||
.filter(({ key }) => !!key)
|
||||
.filter(({ type }) => type === "path")
|
||||
.forEach(p => variables[p.key] = p.value)
|
||||
return variables;
|
||||
}
|
||||
|
||||
export function getQueryParams(params) {
|
||||
return params
|
||||
.filter(({ key }) => !!key)
|
||||
.filter(({ type }) => type != "path")
|
||||
}
|
||||
23
functions/strategies/AxiosStrategy.js
Normal file
23
functions/strategies/AxiosStrategy.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import axios from "axios"
|
||||
|
||||
const axiosWithProxy = async (req, { state }) => {
|
||||
const { data } = await axios.post(
|
||||
state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/",
|
||||
req
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
const axiosWithoutProxy = async (req, _store) => {
|
||||
const res = await axios(req)
|
||||
return res
|
||||
}
|
||||
|
||||
const axiosStrategy = (req, store) => {
|
||||
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||
return axiosWithProxy(req, store)
|
||||
}
|
||||
return axiosWithoutProxy(req, store)
|
||||
}
|
||||
|
||||
export default axiosStrategy
|
||||
31
functions/strategies/ExtensionStrategy.js
Normal file
31
functions/strategies/ExtensionStrategy.js
Normal file
@@ -0,0 +1,31 @@
|
||||
export const hasExtensionInstalled = () =>
|
||||
typeof window.__POSTWOMAN_EXTENSION_HOOK__ !== "undefined"
|
||||
|
||||
export const hasChromeExtensionInstalled = () =>
|
||||
hasExtensionInstalled() && /Chrome/i.test(navigator.userAgent) && /Google/i.test(navigator.vendor)
|
||||
|
||||
export const hasFirefoxExtensionInstalled = () =>
|
||||
hasExtensionInstalled() && /Firefox/i.test(navigator.userAgent)
|
||||
|
||||
const extensionWithProxy = async (req, { state }) => {
|
||||
const { data } = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest({
|
||||
method: "post",
|
||||
url: state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/",
|
||||
data: req,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
const extensionWithoutProxy = async (req, _store) => {
|
||||
const res = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest(req)
|
||||
return res
|
||||
}
|
||||
|
||||
const extensionStrategy = (req, store) => {
|
||||
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||
return extensionWithProxy(req, store)
|
||||
}
|
||||
return extensionWithoutProxy(req, store)
|
||||
}
|
||||
|
||||
export default extensionStrategy
|
||||
115
functions/syntax/gqlQueryLangMode.js
Normal file
115
functions/syntax/gqlQueryLangMode.js
Normal file
@@ -0,0 +1,115 @@
|
||||
export function defineGQLLanguageMode(ace) {
|
||||
// Highlighting
|
||||
ace.define(
|
||||
"ace/mode/gql-query-highlight",
|
||||
["require", "exports", "ace/lib/oop", "ace/mode/text_highlight_rules"],
|
||||
(aceRequire, exports) => {
|
||||
const oop = aceRequire("ace/lib/oop")
|
||||
|
||||
const TextHighlightRules = aceRequire("ace/mode/text_highlight_rules").TextHighlightRules
|
||||
|
||||
const GQLQueryTextHighlightRules = function () {
|
||||
var keywords =
|
||||
"type|interface|union|enum|schema|input|implements|extends|scalar|fragment|query|mutation|subscription"
|
||||
|
||||
var dataTypes = "Int|Float|String|ID|Boolean"
|
||||
|
||||
var literalValues = "true|false|null"
|
||||
|
||||
var escapeRe = /\\(?:u[\da-fA-f]{4}|.)/
|
||||
|
||||
var keywordMapper = this.createKeywordMapper(
|
||||
{
|
||||
keyword: keywords,
|
||||
"storage.type": dataTypes,
|
||||
"constant.language": literalValues,
|
||||
},
|
||||
"identifier"
|
||||
)
|
||||
|
||||
this.$rules = {
|
||||
start: [
|
||||
{
|
||||
token: "comment",
|
||||
regex: "#.*$",
|
||||
},
|
||||
{
|
||||
token: "paren.lparen",
|
||||
regex: /[\[({]/,
|
||||
next: "start",
|
||||
},
|
||||
{
|
||||
token: "paren.rparen",
|
||||
regex: /[\])}]/,
|
||||
},
|
||||
{
|
||||
token: keywordMapper,
|
||||
regex: "[a-zA-Z_][a-zA-Z0-9_$]*\\b",
|
||||
},
|
||||
{
|
||||
token: "string", // character
|
||||
regex: "'(?:" + escapeRe + "|.)?'",
|
||||
},
|
||||
{
|
||||
token: "string.start",
|
||||
regex: '"',
|
||||
stateName: "qqstring",
|
||||
next: [
|
||||
{ token: "string", regex: /\\\s*$/, next: "qqstring" },
|
||||
{ token: "constant.language.escape", regex: escapeRe },
|
||||
{ token: "string.end", regex: '"|$', next: "start" },
|
||||
{ defaultToken: "string" },
|
||||
],
|
||||
},
|
||||
{
|
||||
token: "string.start",
|
||||
regex: "'",
|
||||
stateName: "singleQuoteString",
|
||||
next: [
|
||||
{ token: "string", regex: /\\\s*$/, next: "singleQuoteString" },
|
||||
{ token: "constant.language.escape", regex: escapeRe },
|
||||
{ token: "string.end", regex: "'|$", next: "start" },
|
||||
{ defaultToken: "string" },
|
||||
],
|
||||
},
|
||||
{
|
||||
token: "constant.numeric",
|
||||
regex: /\d+\.?\d*[eE]?[\+\-]?\d*/,
|
||||
},
|
||||
{
|
||||
token: "variable",
|
||||
regex: /\$[_A-Za-z][_0-9A-Za-z]*/,
|
||||
},
|
||||
],
|
||||
}
|
||||
this.normalizeRules()
|
||||
}
|
||||
|
||||
oop.inherits(GQLQueryTextHighlightRules, TextHighlightRules)
|
||||
|
||||
exports.GQLQueryTextHighlightRules = GQLQueryTextHighlightRules
|
||||
}
|
||||
)
|
||||
|
||||
// Language Mode Definition
|
||||
ace.define(
|
||||
"ace/mode/gql-query",
|
||||
["require", "exports", "ace/mode/text", "ace/mode/gql-query-highlight"],
|
||||
(aceRequire, exports) => {
|
||||
const oop = aceRequire("ace/lib/oop")
|
||||
const TextMode = aceRequire("ace/mode/text").Mode
|
||||
const GQLQueryTextHighlightRules = aceRequire("ace/mode/gql-query-highlight")
|
||||
.GQLQueryTextHighlightRules
|
||||
const FoldMode = aceRequire("ace/mode/folding/cstyle").FoldMode
|
||||
|
||||
const Mode = function () {
|
||||
this.HighlightRules = GQLQueryTextHighlightRules
|
||||
this.foldingRules = new FoldMode()
|
||||
}
|
||||
|
||||
oop.inherits(Mode, TextMode)
|
||||
|
||||
exports.Mode = Mode
|
||||
}
|
||||
)
|
||||
}
|
||||
7
functions/templating.js
Normal file
7
functions/templating.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function parseTemplateString(string, variables) {
|
||||
if (!variables || !string) {
|
||||
return string
|
||||
}
|
||||
const searchTerm = /<<([^>]*)>>/g // "<<myVariable>>"
|
||||
return decodeURI(string).replace(searchTerm, (match, p1) => variables[p1] || "")
|
||||
}
|
||||
15
functions/utils/debounce.js
Normal file
15
functions/utils/debounce.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// Debounce is a higher order function which makes its enclosed function be executed
|
||||
// only if the function wasn't called again till 'delay' time has passed, this helps reduce impact of heavy working
|
||||
// functions which might be called frequently
|
||||
// NOTE : Don't use lambda functions as this doesn't get bound properly in them, use the 'function (args) {}' format
|
||||
const debounce = (func, delay) => {
|
||||
let inDebounce
|
||||
return function() {
|
||||
const context = this
|
||||
const args = arguments
|
||||
clearTimeout(inDebounce)
|
||||
inDebounce = setTimeout(() => func.apply(context, args), delay)
|
||||
}
|
||||
}
|
||||
|
||||
export default debounce
|
||||
12
functions/utils/string.js
Normal file
12
functions/utils/string.js
Normal file
@@ -0,0 +1,12 @@
|
||||
export function getSourcePrefix(source) {
|
||||
const sourceEmojis = {
|
||||
// Source used for info messages.
|
||||
info: "\tℹ️ [INFO]:\t",
|
||||
// Source used for client to server messages.
|
||||
client: "\t👽 [SENT]:\t",
|
||||
// Source used for server to client messages.
|
||||
server: "\t📥 [RECEIVED]:\t",
|
||||
}
|
||||
if (Object.keys(sourceEmojis).includes(source)) return sourceEmojis[source]
|
||||
return ""
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user