Compare commits

...

541 Commits

Author SHA1 Message Date
Liyas Thomas
f6c952ffb0 v1.10.0 2021-04-10 14:49:43 +00:00
Joel D Souza
f6db530de2 Sync bodyParams and rawBodyParams (#1562)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
Co-authored-by: Joel DSouza <joel.dsouza@kapturecrm.com>
2021-04-10 01:03:37 -07:00
Liyas Thomas
db45f08905 fix: add query from cURL import - fixed #1582 2021-04-08 11:31:24 +00:00
Liyas Thomas
516d53f9bf chore(deps): bump + docs: add docker pulls badge 2021-04-02 06:46:50 +00:00
Liyas Thomas
c7e1adf638 chore(deps): bump + ui: full width container 2021-03-31 15:44:26 +00:00
Liyas Thomas
31147975c7 i18n translations (#1572)
* Suppletments some tranlations on zh-CN

* Update de-DE.json

Co-authored-by: mrhanson <chihanghanson@gmail.com>
Co-authored-by: 0xflotus <0xflotus@gmail.com>
2021-03-30 13:29:57 -07:00
Liyas Thomas
b9deec1487 chore(deps): bump + docs: remove absolute instructions from readme 2021-03-30 05:00:24 +00:00
Liyas Thomas
de57208bab Merge pull request #1566 from AndrewBastin/main
Bump minor dep updates and update Tab test spec
2021-03-28 17:25:23 +05:30
Andrew Bastin
07e966c640 Bump minor dep updates and update Tab test spec 2021-03-27 23:47:35 -04:00
Liyas Thomas
56982effcd feat: support custom router base property - fixed #1561 2021-03-24 13:18:33 +00:00
Liyas Thomas
8a52b0fbd6 fix: broken grapgql query fetch 2021-03-24 05:20:01 +00:00
Liyas Thomas
2190a1b6fd chore(deps): bump + fix: only allow one from extension and proxy 2021-03-23 19:48:00 +00:00
Andrew Bastin
c18c2ea9d4 Fix settings data migration issues 2021-03-23 11:34:19 -04:00
Andrew Bastin
5fce1118f6 Revamp of the Settings State System along with TypeScript support (#1560)
* Add vue-rx, rxjs and lodash as dependencies

* Added vue-rx plugin integration to nuxt config

* Initial settings store implementation

* Add babel plugin for private class properties to for Jest

* Add DispatchingStore test spec

* Initial settings code

* Reactive Streams for fb current user and id token

* Fix typo

* Migrate index and graphql pages to the new store

* Migrate network strategy to the new store

* Fixed Section.vue errors

* Fix getSettingSubject issue

* Migrate fb settings reference in components to the new state system

* Add typings for lodash as dev dependency

* Load setting

* Load initial sync setting values

* Update proxy url

* Add typescript support

* Rewrite Settings store to TypeScript

* Port Settings page to TypeScript as reference

* Move all store migrations to a separate file

* Delete test file for fb.js

* Add ts-jest as dev dependency

* Remove firebase-mock as dependency

* Remove FRAME_COLORS_ENABLED settings value
2021-03-23 11:18:14 -04:00
Liyas Thomas
64f64b9e31 fix: disable resize on ace editor - fixed #1110 2021-03-23 09:39:14 +00:00
Liyas Thomas
b4ac527638 fix: restore previous theme colors - fixed #1559 2021-03-22 16:36:57 +00:00
Liyas Thomas
352f3af737 refactor: show sub-folders before requests inside folders 2021-03-22 11:53:36 +00:00
Liyas Thomas
c7c221ad5e Merge pull request #1554 from farhan-tariq/show-sub-folders-first
Show sub-folders first instead of requests
2021-03-22 08:31:55 +05:30
Liyas Thomas
74adbae7ed chore(deps): bump 2021-03-22 02:55:47 +00:00
Farhan Tariq
8a5402932c Show sub-folders first instead of requests 2021-03-21 19:49:03 +05:00
Isha Gupta
e565f9bf72 GraphQL collections (#1536) 2021-03-18 20:25:12 +05:30
Liyas Thomas
942b86c647 refactor: upgrade tailwindcss 2021-03-17 18:00:58 +00:00
Liyas Thomas
1042310038 Merge pull request #1542 from JDevx97/fix/1541/query-parameters-url-encoding 2021-03-16 21:18:58 -07:00
Liyas Thomas
d0c261b9cd Merge branch 'main' into fix/1541/query-parameters-url-encoding 2021-03-16 21:12:52 -07:00
liyasthomas
61396cbb15 download fonts to same site origin 2021-03-17 09:23:16 +05:30
Jayce Dugan
87c6230ef2 Fix: Whitespace & URL encoding applied to query parameters.
fix: String.trim() applied to query parameter keys and values to remove additional whitespace.

fix: encodeURI() applied to query string before being applied to request url path.
2021-03-16 22:45:07 +00:00
Liyas Thomas
347ad94a43 fix: minor UI spacing issues 2021-03-16 09:11:19 +00:00
Liyas Thomas
f1389cdba0 TailwindCSS v2.x (#1540) 2021-03-16 01:49:21 -07:00
Liyas Thomas
5399ddf6ac docs: updated description 2021-03-14 15:15:57 +00:00
Liyas Thomas
a724dc1207 proxy.hoppscotch.io (#1532)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-03-14 08:54:15 +05:30
Liyas Thomas
c6af38f7dc docs: updated sponsors list 2021-03-12 15:41:44 +00:00
Liyas Thomas
4d0008186b fix: only show recent 50 synced history entries - fixed #1530 2021-03-12 17:35:16 +05:30
Liyas Thomas
df3df6697e chore(deps): bump + docs: update sponsor list 2021-03-12 08:39:21 +00:00
Liyas Thomas
13ff7b9088 docs: updated screenshots + feature list 2021-03-11 14:59:16 +00:00
Liyas Thomas
196d252fec Merge pull request #1533 from jamesgeorge007/docs/add-ons
docs: add reference to hopp-doc-gen add-on
2021-03-11 16:51:32 +05:30
jamesgeorge007
53dfdc440d docs: add reference to hopp-doc-gen 2021-03-11 15:25:18 +05:30
Liyas Thomas
988a99efb7 fix: history order on cloud sync 2021-03-11 13:51:22 +05:30
Liyas Thomas
17da230d72 fix: remove sort history entries + fix history entries order - fixed #1455 2021-03-11 06:53:49 +00:00
Liyas Thomas
c8cdfc8885 Merge pull request #1531 from hoppscotch/audit/pwa
Added source=pwa on PWA start URL
2021-03-11 08:52:45 +05:30
Osheen Sachdev
0b00842c50 [Feat: GraphQL sidebar] GraphQL History (#1528)
* Create REQUIREMENTS.md

* graphql history UI

* rest history emit

* removed requirements file

* add, delete, clear, star history and sync with firstore

* use history

* empty schema

* remove other tabs

* computed query, setting headers

* binding props

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>

* remove print

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>

* remove dummy data

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>

* add docs tab

* date, time attribute --> updatedOn

* Removed margin from sidebar Tab

* removed v-bind

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>

* use shortcut for v-bind

* use shortcut for v-bind

* use unfold icons

* use template literals in :key

* history clear bug

* delete history bug

* minor translation

* remove console logs

* remove unused css

* add stared style

* remove absolute styles

* tests for graphql card

* tests for rest card

* tests for clear history added

* mount, clear, use, delete history tests added

* Rename card.vue to Card.vue

* Rename card.vue to Card.vue

* use computed

Co-authored-by: Isha Gupta <40794215+IshaGupta18@users.noreply.github.com>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2021-03-10 22:12:09 -05:00
Liyas Thomas
17e405a39e feat: added source=pwa on PWA start URL 2021-03-10 23:51:58 +00:00
Liyas Thomas
51bd3455cc chore(deps): bump 2021-03-09 15:28:41 +00:00
Liyas Thomas
e292af75ad meta: updated sponsors link 2021-03-09 15:28:15 +00:00
Liyas Thomas
ef4566eb95 refactor: show interaction while dragging request in collections 2021-03-08 04:02:08 +00:00
Liyas Thomas
e7d1ffb7ae chore: deps bump 2021-03-06 17:36:56 +00:00
Liyas Thomas
4fded3ada3 i18n translations (#1527)
Co-authored-by: Yohann Fabri <11197007+YoranSys@users.noreply.github.com>
Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
Co-authored-by: UioSun <uio.sun@soundws.com>
2021-03-04 09:13:31 +05:30
Liyas Thomas
dc5ca76d05 rename all components to new namespace (#1515)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-03-01 09:28:14 +05:30
Liyas Thomas
37bdd525ea refactor: replace fonts with Google fonts module 2021-02-25 16:08:12 +00:00
Liyas Thomas
3f03806455 Set schedule interval to weekly 2021-02-23 17:14:33 +00:00
harshlele
ff7bb1f303 response outline for JSON responses (#1484)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-02-23 08:09:16 +05:30
Liyas Thomas
5fe1de170d chore(deps): bump 2021-02-23 01:49:23 +00:00
Liyas Thomas
72913ccece chore(deps): bump + sort tailwind classes 2021-02-20 15:39:37 +00:00
Liyas Thomas
a6207e7edf chore(deps): bump + docs: updated readme 2021-02-19 17:33:37 +00:00
Liyas Thomas
2972ac6328 Support multipart/form-data content-type (#1485)
* Initial UI refactor - move raw and key-value body to components and tabs

* Delete package-lock.json

* deps

* Add multipart/form-data as a content type

* fix: add default contentType value

* Allow http body param request body with multipart/form-data

* Add form data to vuex

* move raw body components to 'Raw Request Body' tab

* Add files addition logic

* Set Dockerfile to run nuxt in dev mode

* Set Dockerfile to run nuxt in dev mode

* Draft version of file upload

* refactor: clean up

* Add file chip to denote file input

* Remove console.log

* refactor(ui): matching styles

* refactor(ui): matching styles

* fix(ui): mobile responsiveness

* fix(ui): mobile responsiveness

* refactor: minor cleanup

* Remove file from any form of persistence

* Add warning that form data files will not be saved to local storage

* Add remove file functionality

* Prevent file from being saved to collections

* Remove console.log

* fix active toggle on multipart/form-data + cleanup

* auto import components

Co-authored-by: nelsontky <nelson@ccb.wtf>
2021-02-19 22:31:31 +05:30
Liyas Thomas
d90550438f chore(deps): bump 2021-02-15 18:13:40 +00:00
Liyas Thomas
aa0f08cba3 chore(deps): bump 2021-02-14 21:27:47 +05:30
Liyas Thomas
a1b39d4fbc docs: updated sponsorship assets 2021-02-13 07:22:27 +05:30
Liyas Thomas
091a160db3 docs: updated sponsors list 2021-02-13 00:52:44 +05:30
Liyas Thomas
03f6336ac5 chore(deps): bump 2021-02-12 23:57:14 +05:30
Liyas Thomas
24c4cfe586 chore(deps): bump 2021-02-10 20:19:09 +05:30
Liyas Thomas
ca0c858e0f fix: broken mobile responsiveness - fixed #1472 2021-02-10 19:53:47 +05:30
Andrew Bastin
2be9a0bdba Merge pull request #1471 from KoHcoJlb/interface_and_enum_types
graphql: show enums and interfaces
2021-02-08 21:18:24 -05:00
Andrew Bastin
d333a44e11 Minor cleanup of type.vue 2021-02-08 21:11:18 -05:00
Andrew Bastin
e8e855a36c Added tests for the added functionality in type.vue and typelink.vue 2021-02-08 21:08:30 -05:00
Igor Stuzhuk (KoHcoJlb)
09719a4ad3 fix type tests 2021-02-08 14:02:01 +02:00
Igor Stuzhuk (KoHcoJlb)
c9e542d6d5 graphql: show enums and interfaces
make scalars not clickable
2021-02-07 22:18:20 +02:00
Liyas Thomas
a32ce56295 i18n (#1470) 2021-02-07 06:52:21 +05:30
Andrew Bastin
279c8c14cc Disabling GQL schema polling if there was an error 2021-02-06 19:37:45 -05:00
Liyas Thomas
49bc2c2204 Merge branch 'main' of https://github.com/hoppscotch/hoppscotch 2021-02-05 06:07:55 +05:30
Liyas Thomas
c036c945bd chore(deps): bump 2021-02-05 06:02:09 +05:30
Andrew Bastin
2e2cce9fcb Merge pull request #1457 from KoHcoJlb/fix-graphql-types-view
Fix "jump to graphql type" and show graphql input types
2021-02-02 12:52:27 -05:00
Igor Stuzhuk (KoHcoJlb)
7f33798789 graphql: fix "jump to type" and show input types 2021-02-02 16:10:51 +02:00
Liyas Thomas
e299433e6e Merge branch 'main' of https://github.com/hoppscotch/hoppscotch 2021-02-02 10:23:11 +05:30
Liyas Thomas
c82cb67bb6 chore(deps): bump 2021-02-02 10:22:31 +05:30
Andrew Bastin
075e11a7e1 Fix extension tick mark not appearing 2021-02-01 10:03:18 -05:00
Liyas Thomas
2b2c968a97 fix: revert axios interceptor and use normal time data - fixed #1449 2021-01-31 08:18:25 +05:30
Liyas Thomas
bffdb39c02 fix: decode url and path before saving to collection - fixed #1451 2021-01-30 22:19:19 +05:30
Liyas Thomas
3dabd73e95 chore(deps): bump 2021-01-30 22:17:50 +05:30
Liyas Thomas
8ed30e7eda chore(deps): bump 2021-01-28 21:28:36 +05:30
Liyas Thomas
7e4297d9bf Accurate response time and size (#1441)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-01-28 13:09:31 +05:30
Liyas Thomas
99e634711e chore(deps): bump 2021-01-26 20:09:19 +05:30
Kévin Dunglas
07f370d6d2 feat: better media types detection for JSON, XML and HTML lenses (#1438)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2021-01-26 09:57:11 +05:30
Kévin Dunglas
d2dfb4c8df fix: status code detection (#1440) 2021-01-25 06:50:50 +05:30
Liyas Thomas
64ee01d9fe Merge pull request #1439 from dunglas/feat/jsonld
feat: add JSON-LD to the list of known content types
2021-01-25 06:38:34 +05:30
Kévin Dunglas
a8d5ab035d feat: add JSON-LD to the list of known content types 2021-01-24 18:25:39 +01:00
Liyas Thomas
f58c6807d0 fix: encoding parameters twice effecting codegen - fixed #1437 2021-01-24 07:42:07 +05:30
Liyas Thomas
ca66b47014 refactor: better copy 2021-01-23 19:43:11 +05:30
Liyas Thomas
290781e734 feat: 'New version found. Refresh to update' toast 2021-01-23 17:22:54 +05:30
Liyas Thomas
d403e10018 docs: added contributors welcome badge + updated sponsors list 2021-01-23 12:40:40 +05:30
Liyas Thomas
161a0e07f9 refactor(codeql): removed unused variables 2021-01-23 11:40:29 +05:30
Liyas Thomas
eb9b0c85a9 chore(deps): bump 2021-01-23 11:38:37 +05:30
John Harker
c7f7b96405 Update Proxy URL (#1436)
* Update proxy URLs

* Update URLs in Vue file

Had to edit with web editor to avoid vs code changing indentation.

* Delete settings.json

Co-authored-by: Sam Jakob Mearns <me@samjakob.com>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2021-01-22 10:43:44 +05:30
Andrew Bastin
c5dff96f57 Better JS language integration for Pre-Request scripts and Test scripts (#1422)
* Add tern as dependency

* Add build rule to transpile mjs

* Initial implementation of the auto complete engine

* Separate out the tern server code to separate file

* Added extra type defs for tern server

* Boost the pw completion result to the top of the list

* Added acorn and acorn-walk as dependency

* Semantic linting powered by tern

* Fix DeepCode warnings for js-editor

* Remove unused registerLint tern extension

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2021-01-22 07:04:02 +05:30
Liyas Thomas
ff418ad55f chore(deps): bump 2021-01-20 21:23:13 +05:30
Liyas Thomas
6b0da0a568 fix: delete request from collection modal not closing 2021-01-18 22:08:48 +05:30
Liyas Thomas
56151982a7 chore(deps): bump 2021-01-17 22:44:58 +05:30
Liyas Thomas
c8185050f6 feat: GitHub buttons 2021-01-16 18:40:18 +05:30
Liyas Thomas
ed20b009a5 refactor: removed sponsorship prompt 2021-01-16 17:00:03 +05:30
Liyas Thomas
bf0bcf2f72 chore(deps): bump 2021-01-15 23:27:07 +05:30
Liyas Thomas
d81e80f273 chore(deps): bump 2021-01-13 20:23:14 +05:30
Liyas Thomas
6afed0e035 chore(deps): bump + fix(typo): extension repo link 2021-01-12 13:21:48 +05:30
Liyas Thomas
0a39e7b02f feat: Active toggle for GraphQL headers 2021-01-08 21:55:31 +05:30
Liyas Thomas
01a45f3085 chore(deps): bump 2021-01-08 21:53:21 +05:30
Liyas Thomas
c61764cd8d chore(deps): bump 2021-01-06 08:47:37 +05:30
Liyas Thomas
ce20f5d0a2 chore(deps): bump 2021-01-05 05:48:25 +05:30
Liyas Thomas
ffa9210286 fix: broken file name extension while downloading response 2021-01-04 07:25:03 +05:30
Liyas Thomas
025ead4fa3 chore(deps): bump 2021-01-04 06:37:59 +05:30
Liyas Thomas
82de03101b fix: throwing error when settings key being empty - fixed #1409 2021-01-04 06:25:02 +05:30
Liyas Thomas
eae071f9b8 doc: updated screenshots 2021-01-02 23:09:46 +05:30
Liyas Thomas
aadad482c4 chore(deps): bump + updated sponsors list 2021-01-02 20:57:28 +05:30
Gerardyang
cb8b98f707 Fix a missing close parenthesis before '.then' (#1407) 2021-01-02 20:49:42 +05:30
Liyas Thomas
603a888e2e chore(deps): bump + fix: typo in docker commands 2020-12-31 17:22:58 +05:30
Liyas Thomas
9e3083cc5d Merge pull request #1404 from hoppscotch/dependabot/npm_and_yarn/v-tooltip-2.1.0
chore(deps): bump v-tooltip from 2.0.3 to 2.1.0
2020-12-31 05:42:48 +05:30
dependabot[bot]
ab9f2c0a66 chore(deps): bump v-tooltip from 2.0.3 to 2.1.0
Bumps [v-tooltip](https://github.com/Akryum/vue-tooltip) from 2.0.3 to 2.1.0.
- [Release notes](https://github.com/Akryum/vue-tooltip/releases)
- [Commits](https://github.com/Akryum/vue-tooltip/compare/v2.0.3...v2.1.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-31 00:05:42 +00:00
Liyas Thomas
e84888c27e fix: broken import cURL #1159 2020-12-30 18:43:56 +05:30
Liyas Thomas
4dfc91db0a chore(dev-deps): bump 2020-12-30 11:12:56 +05:30
Liyas Thomas
5705df96f3 Merge branch 'main' of https://github.com/hoppscotch/hoppscotch 2020-12-29 07:16:45 +05:30
Liyas Thomas
9ac0cf1406 fix: decodeURI() failing on malformated URI + chore(deps): bump 2020-12-29 07:13:46 +05:30
Liyas Thomas
71170a1c5d feat: input toggle for parameters and headers (#1388)
* feat: toggler for parameters and headers

* refactor: move bodyParams to separate component + feat: input toggle

* fix: backward copaitability

* Fixed issue with imported active prop mutations

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2020-12-28 06:02:41 +05:30
Liyas Thomas
353978f115 Merge branch 'main' of https://github.com/hoppscotch/hoppscotch into main 2020-12-27 18:38:14 +05:30
Liyas Thomas
e89065aafc chore(deps): bump 2020-12-27 18:37:45 +05:30
Andrew Bastin
ff35ccbaec Merge pull request #1398 from carlos-ds/unit-tests
Unit tests for the remaining test methods
2020-12-23 21:02:48 -05:00
Liyas Thomas
529d5b0535 Merge branch 'main' into unit-tests 2020-12-23 06:00:16 +05:30
Karel De Smet
15ecb19c65 Added extra test for error handling 2020-12-22 21:15:20 +01:00
Karel De Smet
a64b32d12c Test for toBeType() 2020-12-22 21:12:53 +01:00
Karel De Smet
f53ac25d90 Test for toHaveLength() 2020-12-22 20:05:56 +01:00
Karel De Smet
de1d06528e Test for toBeLevel5xx() 2020-12-22 19:33:27 +01:00
Karel De Smet
ea227e09fa Explicitly pass radix 10 to parseInt functions in toBeLevelxxx() to avoid unexpected results 2020-12-22 19:19:21 +01:00
Liyas Thomas
46cbd6dfd2 chore(deps): bump 2020-12-22 20:31:13 +05:30
Karel De Smet
a61e6efdd4 Test for toBeLevel4xx() 2020-12-22 08:10:54 +01:00
Karel De Smet
6536d71566 Merge https://github.com/hoppscotch/hoppscotch into unit-tests 2020-12-22 08:02:03 +01:00
Karel De Smet
d92412175c Test for toBeLevel3xx() 2020-12-22 08:01:55 +01:00
Liyas Thomas
a3b7f9a739 Updated collaborators list 2020-12-21 23:56:35 +05:30
Karel De Smet
5367a23112 #1396: Fixed assertions for toBeLevelxxx() functions (#1397)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-12-21 22:42:53 +05:30
Karel De Smet
3614f5b620 Test for toBeLevel2xx() 2020-12-21 17:35:40 +01:00
Liyas Thomas
eac7954570 chore(deps): bump 2020-12-21 21:39:28 +05:30
Liyas Thomas
7d08da22b2 Merge branch 'main' of https://github.com/hoppscotch/hoppscotch into main 2020-12-20 12:26:04 +05:30
Liyas Thomas
756acf1395 chore(deps): bump 2020-12-20 12:20:37 +05:30
Karel De Smet
a2a03c6b52 Unit tests for test functions toBe, toHaveProperty (#1392)
* Added unit tests for the post-request tests

* Added 2 top level functions to avoid duplication of code

* Simplified tests for toBe and error handling

* Added a test for asserting string values with toBe

* Added a test for negative assertion with toBe

* Added tests for toHaveProperty

* Imported PASS, FAIL and ERROR constants

* Use getTestResult and getErrors to avoid duplication of code

* Removed .vscode/settings.json

* Minor cleanup to postwomanTesting.spec.js

* Revert change of runTestScriptWithVariables back to default export

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2020-12-19 07:45:36 +05:30
Liyas Thomas
8e63a84152 Merge pull request #1393 from farhan-tariq/fix-for-collections
Fix when folder is having no request property
2020-12-18 17:21:28 +05:30
Farhan Tariq
38b3197912 Fix when folder is having no request property 2020-12-18 10:53:29 +00:00
Liyas Thomas
1d4576c7fd chore(refactor): removed absolute codes 2020-12-17 15:30:02 +05:30
Liyas Thomas
29a4dee91b chore(deps): bump + refactor: removed absolute code 2020-12-16 23:53:08 +05:30
Liyas Thomas
9e166774ef refactor(ui): faster animations + chore(deps): bump 2020-12-16 06:05:58 +05:30
Liyas Thomas
dd40df4b37 fix: minor UI transitions 2020-12-15 07:58:12 +05:30
Liyas Thomas
afd07562b4 chore(deps): bump 2020-12-15 03:44:20 +05:30
Liyas Thomas
81a6d821c0 feat: confirm modal + chore(deps): bump 2020-12-14 10:35:14 +05:30
Liyas Thomas
858ab252a6 fix: input borders 2020-12-12 19:16:37 +05:30
Liyas Thomas
a56e2bb577 chore(refactor): better inputs 2020-12-12 08:33:29 +05:30
Liyas Thomas
27980cf7c7 fix: tests 2020-12-11 22:39:50 +05:30
Liyas Thomas
5a7bcf32ea chore(refactor): modern UI 2020-12-11 22:24:34 +05:30
Liyas Thomas
773423069b Refactor UI 2020-12-11 15:59:03 +05:30
Liyas Thomas
1e6773deb5 Refactor: Modularize home page (#1372)
* Move HTTP headers section to component

* Move import cURL modal to component

* Move Notes, OAuth token modal to seperate components

* Fix deepcode analysis

* Move parameters section to seperate component

* Move codegen modal to component

* ES6
2020-12-11 05:59:29 +05:30
Liyas Thomas
a95d37d610 Updated screenshots 2020-12-10 16:45:39 +05:30
Liyas Thomas
bdf72eca70 fix: turn off permission of GitHub OAuth app fixed #1377 2020-12-10 16:12:38 +05:30
Liyas Thomas
3acb2a56a2 chore(deps): bump 2020-12-10 08:48:59 +05:30
Liyas Thomas
2f0e9d0681 chore(deps): bump 2020-12-10 01:02:49 +05:30
Hari Narayanan
bfa7eb0c19 export markdown from docs (#1358)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-12-09 10:41:05 +05:30
Andrew Bastin
27d71fb10f Merge pull request #1367 from hoppscotch/feat/gist
Collections and environments + GitHub gist
2020-12-08 23:08:24 -05:00
Andrew Bastin
eeb1ecd2df Added test case for signInUserWithGithub to check if adding 'repo gist' 2020-12-08 23:01:17 -05:00
Andrew Bastin
f21b5768a5 Fixed tests for fb.spec.js 2020-12-08 22:55:41 -05:00
Liyas Thomas
ba08883623 Final gist integration + clean up 2020-12-08 17:50:32 +05:30
Liyas Thomas
b1587950c6 Merge branch 'main' into feat/gist 2020-12-08 14:59:03 +05:30
Liyas Thomas
b473ed7cb7 Allow importing and exporting environments to gist 2020-12-08 12:18:28 +05:30
Liyas Thomas
2978c8adfe Allow importing collections from gist 2020-12-08 12:05:10 +05:30
Liyas Thomas
1f0111256b chore(fonts): bump 2020-12-08 11:07:25 +05:30
Liyas Thomas
346b980a1d Merge pull request #1366 from hoppscotch/main 2020-12-08 09:03:00 +05:30
Liyas Thomas
8000791e17 chore(deps): bump 2020-12-07 20:53:49 +05:30
Liyas Thomas
66077ea6d7 Replaced svg with icon font 2020-12-07 20:37:13 +05:30
Liyas Thomas
3d10a8f86a Initial MVP of GitHub gist support 2020-12-07 14:14:02 +05:30
Liyas Thomas
ebae9880dc Updated license copyright (c) 2020, migrated docker container to organization, chores 2020-12-06 12:04:55 +05:30
Liyas Thomas
2e72c16964 chore(deps): bump husky to v5 2020-12-06 11:00:00 +05:30
Liyas Thomas
6ebaea395d chore(deps): bump 2020-12-05 23:47:26 +05:30
Liyas Thomas
257ee9928f chpore(deps): bump 2020-12-04 23:37:13 +05:30
Liyas Thomas
2ac9f5918a chore(deps): bump + updated sponsor list 2020-12-04 09:59:19 +05:30
Liyas Thomas
9c4767bb68 Merge pull request #1365 from hoppscotch/dependabot/npm_and_yarn/nuxtjs/tailwindcss-3.3.1
Bump @nuxtjs/tailwindcss from 3.3.0 to 3.3.1
2020-12-04 07:09:49 +05:30
dependabot[bot]
cbd82988a8 Bump @nuxtjs/tailwindcss from 3.3.0 to 3.3.1
Bumps [@nuxtjs/tailwindcss](https://github.com/nuxt-community/tailwindcss-module) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/nuxt-community/tailwindcss-module/releases)
- [Changelog](https://github.com/nuxt-community/tailwindcss-module/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/tailwindcss-module/compare/v3.3.0...v3.3.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-04 00:05:30 +00:00
Liyas Thomas
d0a9445dfc Environment select box - cherry picked from #1359 2020-12-03 11:12:47 +05:30
Liyas Thomas
cd6a9bf9a8 chore(deps): bump 2020-12-03 05:46:26 +05:30
Liyas Thomas
8afead4e39 Set 'Ctrl + I' to reset request - fixed #1356 2020-12-02 10:23:11 +05:30
Karel De Smet
f279471858 Added assertion methods toHaveLength & toBeType (#1357)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-12-02 06:55:37 +05:30
Liyas Thomas
61fe518710 chore(deps): bump + better History UI 2020-12-01 22:26:13 +05:30
Hari Narayanan
b774a59db2 Web Worker regex test (#1354)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-12-01 21:21:13 +05:30
Liyas Thomas
63a1f52482 Set a max-width container wrapper 2020-12-01 05:46:25 +05:30
Liyas Thomas
ab5ea5948b Minor refactoring and code splitting 2020-12-01 05:21:14 +05:30
Liyas Thomas
a8fd0fcd19 Minor refactoring and code splitting 2020-12-01 05:09:16 +05:30
Liyas Thomas
6098d55b57 chore(deps): bump + fixed: #1352 2020-11-30 23:44:39 +05:30
Liyas Thomas
9ba2f80e3a Updated sponsors list 2020-11-30 19:12:03 +05:30
Liyas Thomas
3acf71f253 Updated sponsor list 2020-11-30 18:51:46 +05:30
Liyas Thomas
ad51597bf5 Malayalam + Dutch (#1351)
Co-authored-by: vachan-maker <65799568+vachan-maker@users.noreply.github.com>
Co-authored-by: Karel De Smet <karel.de.smet@outlook.com>
2020-11-30 08:16:16 +05:30
Liyas Thomas
60057eee6a Temporarily removed test case number in tab label. Fixed #1349 2020-11-30 08:14:22 +05:30
Liyas Thomas
3cfd0ba1f8 chore(deps): bump 2020-11-29 20:00:32 +05:30
Liyas Thomas
dec386840a Fixed missing 'Done' button 2020-11-29 13:10:14 +05:30
Liyas Thomas
355b9be3ff chores(deps): bump + fixed og:image 2020-11-28 17:23:10 +05:30
Hari Narayanan
57627367f5 duplicate identifier for requests fixed (#1346)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-11-27 17:12:33 +05:30
Liyas Thomas
a5a812d3f4 Merge pull request #1343 from hariaakash/main
Request method labels & custom method width fix
2020-11-26 06:51:31 +05:30
Hari Narayanan
5426273cf8 custom method width 2020-11-26 00:38:03 +05:30
Hari Narayanan
d9bab319e3 added request method label in collections 2020-11-25 23:54:20 +05:30
Liyas Thomas
77862cdf9b lgtm code review suggestions 2020-11-23 12:32:13 +05:30
Liyas Thomas
b8d68ee359 Modern build, fixed WebSocket freezing on invalid URLs 2020-11-22 07:05:04 +05:30
Liyas Thomas
307e434f91 chore(deps): bump 2020-11-21 23:28:09 +05:30
Liyas Thomas
f60b2eb8af chore(deps): bump 2020-11-20 10:24:30 +05:30
Liyas Thomas
9da1aa1421 Merge pull request #1332 from hoppscotch/dependabot/npm_and_yarn/socket.io-client-3.0.2
Bump socket.io-client from 3.0.1 to 3.0.2
2020-11-18 06:17:21 +05:30
dependabot[bot]
4d3964daeb Bump socket.io-client from 3.0.1 to 3.0.2
Bumps [socket.io-client](https://github.com/socketio/socket.io-client) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/socketio/socket.io-client/releases)
- [Changelog](https://github.com/socketio/socket.io-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-client/compare/3.0.1...3.0.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-18 00:07:32 +00:00
Liyas Thomas
b80f9d3517 chore(deps): bump 2020-11-14 06:17:37 +05:30
Liyas Thomas
46981f73a9 Updated sponsors list 2020-11-12 07:31:15 +05:30
Liyas Thomas
9b6394627b fixed #1323 + chore(deps) bump 2020-11-12 06:48:40 +05:30
Liyas Thomas
3a34331cc2 Replacing master branch with main branch 2020-11-11 08:55:44 +05:30
Liyas Thomas
95fe10b312 chore(deps): bump @nuxtjs/gtm 2020-11-11 08:34:00 +05:30
Liyas Thomas
f8d4303531 chore(deps): bump 2020-11-10 06:56:15 +05:30
Andrew Bastin
7d49f75682 Query arguments in GraphQL are shown separately with description (#1319)
* Query arguments in GraphQL are shown separately with description

* Update field.vue

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-11-09 07:16:47 +05:30
Liyas Thomas
a84a8e1a6e chore(deps): bump 2020-11-06 09:17:06 +05:30
Liyas Thomas
1fc2181937 chore(deps): bump 2020-11-05 13:32:27 +05:30
Liyas Thomas
7411f01fb5 Updated test snapshot 2020-11-03 14:06:24 +05:30
Liyas Thomas
2c9c598dd7 Merge branch 'master' of https://github.com/hoppscotch/hoppscotch 2020-11-03 13:48:18 +05:30
Liyas Thomas
3c00a3b1d0 Fixed Axios codegen missing raw post body 2020-11-03 13:47:17 +05:30
Liyas Thomas
660cdb575a Removed absolute Firebase files 2020-11-03 08:01:55 +05:30
Liyas Thomas
f20876a028 chore(deps): bump 2020-11-03 07:17:17 +05:30
Liyas Thomas
b31866cf32 Updated codegen list + removed hacktoberfest prompt 2020-11-02 18:05:29 +05:30
Liyas Thomas
bde27c3880 chore(deps): bump Firebase to v8.0 2020-11-02 14:59:08 +05:30
Liyas Thomas
80cb613888 Removed absolute PWA attributes 2020-11-02 10:33:01 +05:30
Liyas Thomas
23966b1103 Merge pull request #1312 from hoppscotch/sass
Replace node-sass with sass
2020-11-02 07:51:18 +05:30
Liyas Thomas
2fd897064a Replace node-sass with sass 2020-11-02 07:40:26 +05:30
Jyoti Gupta
846298210a [#1190] [Jyoti] Add codegen for nodeJs Unirest support (#1308)
* [#1190] [Jyoti] Add codegen for nodeJs Unirest

* Update and rename node-unirest.js to nodejs-unirest.js

* Update codegen.js

* [Jyoti] update snapshot for node-unirest-codegen

Co-authored-by: Jyoti Gupta <jyoti.gupta@INjyotigupta.local>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-11-02 06:23:13 +05:30
Liyas Thomas
f22e98ccca Updated test snapshots - fixing CI tests 2020-10-30 18:28:09 +05:30
Liyas Thomas
5cb6d10e3d Reorder codegens in alphabetical order 2020-10-30 18:16:34 +05:30
Liyas Thomas
5b4dd74503 Merge pull request #1307 from patidar-jaishree/add-c-libcurl-codegen
[Jaishree] Add C Libcurl Codegen
2020-10-29 18:44:38 +05:30
Jaishree Patidar
cd3cec5d48 [Jaishree] Add C Libcurl Codegen 2020-10-29 17:47:25 +05:30
Liyas Thomas
621888d6d4 Merge pull request #1306 from hoppscotch/fix/1304
Enable Workbox - fix #1304
2020-10-28 17:49:30 +05:30
Liyas Thomas
3e7fcaf764 Enable Workbox 2020-10-28 17:14:55 +05:30
Liyas Thomas
8fb44df59b Merge branch 'master' of https://github.com/hoppscotch/hoppscotch 2020-10-28 06:42:29 +05:30
Liyas Thomas
5b205d6e4a chore(deps): bump 2020-10-28 06:41:42 +05:30
Andrew Bastin
dc98ef8b57 Fix add folder to collections being broken when logged in (#1299)
* Moved add folder modal logic out

* Pass around folder paths for collections to fix folder not applying on
logged in users

* Remove unwanted use of folder value for addFolder store mutation

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-26 06:44:59 +05:30
Kunal Kumar
f9ae242792 Kunal - java-unirest 25 oct 2020 codegen added (#1301) 2020-10-26 00:00:36 +05:30
Shalika Singhal
438640d5b8 added codegen for java OK Http Client (#1300)
* added codegen for  java OK Http Client

* shalika- added snapshot test for -codegen- java -ok http client

* shalika- moving JavaOkHttpClientCodegen in alphabetical order in codegen
2020-10-25 23:07:28 +05:30
Liyas Thomas
078b755fb6 chore(deps-dev): bump 2020-10-24 22:57:37 +05:30
Liyas Thomas
501e01fe23 Update sponsor message 2020-10-24 17:36:33 +05:30
Liyas Thomas
ce992fc205 Merge branch 'master' of https://github.com/liyasthomas/postwoman 2020-10-23 10:21:28 +05:30
Liyas Thomas
8609dedafd chore(deps-dev): bump 2020-10-23 10:20:57 +05:30
Liyas Thomas
66f124b397 Use correct build command - #1295 2020-10-21 18:59:13 +05:30
Andrew Bastin
21cf439210 Added polling schema (#1288)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-21 14:03:03 +05:30
Liyas Thomas
91a7d20923 Lint + ES6 2020-10-21 13:49:02 +05:30
Liyas Thomas
f49c2138de Lint + ES6 2020-10-21 12:20:32 +05:30
Liyas Thomas
774853af7a Remove teams section from Firebase 2020-10-21 10:11:56 +05:30
Farhan Tariq
7e30a4a3d4 Subfolder functionality (#1194)
* Added functionality for sub-folders

* Edit Request name only. Drag and drop to move requests

* Refactor

* Move requests between folder or collections

* Functionality to save request in multiple folders

* Unnecessary Lang

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-21 06:41:45 +05:30
Liyas Thomas
b179731359 sponsor ad 2020-10-20 16:59:54 +05:30
Liyas Thomas
d6409d7152 Fixed selected codegen name, updated deps 2020-10-20 00:24:57 +05:30
Liyas Thomas
eaeefafe39 Lint, ES6, popover for codegen 2020-10-19 18:02:42 +05:30
Ben
59974edf80 feat:add code generator for Shell with HTTPie (#1290)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-19 07:54:07 +05:30
Scott Dutton
37f914d1cc Possible solution for Global variables (#1289)
fix for #1164

This takes enviroments called "Global" or "global" and appends them
after the current scripts.

Seems to work, but proabbly a cleaner way to acheive

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-19 07:41:01 +05:30
Scott Dutton
da92fd705b Replace headers in view (#1287)
fixs #1257

Cant add this in the same place as the URL as you end in an infinate
loop

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-19 07:21:40 +05:30
Liyas Thomas
00505b8af8 chore(deps): bump 2020-10-19 06:43:08 +05:30
Liyas Thomas
22a3bba6ab Cherry picking refactored fb.js from #879 by @AndrewBastin (#1286)
* Cherry picking refactored fb.js from #879 by @AndrewBastin

* Fixed a minor UI glitch in History section

* Removed logout success toast testcase

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2020-10-17 14:53:19 +05:30
Matthew Crumley
b6b3cbcb9a Add Salesforce Apex codegen (#1285) 2020-10-17 12:39:23 +05:30
Ben
de97048424 feat:add code generator for Ruby Net::HTTP (#1284) 2020-10-17 10:47:26 +05:30
Liyas Thomas
02471e6d60 Lint 2020-10-16 07:10:07 +05:30
Liyas Thomas
edf58ee897 Updated dependencies 2020-10-16 06:31:44 +05:30
Liyas Thomas
5ecfd278d4 i18n (#1277)
* improve pt-br translations

* Update french language

* Added i18n vi-VN translations (#1274)

Co-authored-by: Gustavo Cavalieri Fernandes <gugacavalieri@gmail.com>
Co-authored-by: Thomas Bnt <thomasbnt@protonmail.com>
Co-authored-by: Flyznex <thuanpt.dev@gmail.com>
2020-10-15 11:37:33 +05:30
Liyas Thomas
3327179270 Updated dependencies 2020-10-15 05:57:02 +05:30
Liyas Thomas
98462e8dca Updated deps 2020-10-14 08:51:33 +05:30
Liyas Thomas
2f515164b3 chore: updated deps 2020-10-13 19:08:20 +05:30
Liyas Thomas
3c8ceebedc Merge pull request #1262 from hoppscotch/dependabot/npm_and_yarn/jest-26.5.3
Bump jest from 26.5.2 to 26.5.3
2020-10-12 06:43:19 +05:30
dependabot[bot]
26fe93dfc0 Bump jest from 26.5.2 to 26.5.3
Bumps [jest](https://github.com/facebook/jest) from 26.5.2 to 26.5.3.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/compare/v26.5.2...v26.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-12 00:10:38 +00:00
Andrew Bastin
9113a826ce Added temporary hack to fix GQL trailing space errors 2020-10-11 13:17:49 -04:00
Liyas Thomas
eca1baf53e chore: updated deps 2020-10-11 12:37:32 +05:30
Jeff Sieu
6b3b8e6204 Remove extra console log (#1261)
* Add search bar for collections

* Remove console log

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-11 10:32:28 +05:30
Jeff Sieu
194a4f3adb Add search bar for collections (#1260)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-11 10:22:18 +05:30
Liyas Thomas
054f27083b Merge pull request #1259 from Ldoppea/feature/codegen-csharp-restsharp
feat:add code generator for C# RestSharp
2020-10-11 06:13:22 +05:30
Yannick Chiron
168d1dc678 feat:add code generator for C# RestSharp 2020-10-11 00:58:02 +02:00
Ldoppea
8cc5c62665 fix:add 'helpers' folder to docker compose volumes (#1258) 2020-10-10 16:52:02 +05:30
Raul Piraces Alastuey
82c874afb5 Add: Shell wget codegen (#1256)
* Add: Shell wget codegen

* Add missing snapshot for tests
2020-10-09 19:44:49 +05:30
YE Qing
1afd0381c1 Run tests against all codegen instead of individual ones (#1255)
* run tests against all codegens instead of individual ones

* table testing

* fixture is no longer needed

Co-authored-by: Qing Ye <ye.qing@go-jek.com>
2020-10-09 14:04:12 +05:30
Liyas Thomas
5f5589f1f1 Merge pull request #1254 from hoppscotch/dependabot/npm_and_yarn/firebase-7.23.0
Bump firebase from 7.22.1 to 7.23.0
2020-10-09 06:27:07 +05:30
dependabot[bot]
1cee408113 Bump firebase from 7.22.1 to 7.23.0
Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 7.22.1 to 7.23.0.
- [Release notes](https://github.com/firebase/firebase-js-sdk/releases)
- [Commits](https://github.com/firebase/firebase-js-sdk/compare/firebase@7.22.1...firebase@7.23.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-09 00:04:55 +00:00
luthrap
92854b9e84 Python http client codegen (#1252)
* add codegen for python http client

* update print header function
2020-10-08 23:29:00 +05:30
Liyas Thomas
a4c5817b54 Use function as default value for array prop 2020-10-08 20:00:20 +05:30
Liyas Thomas
6c82680066 Merge pull request #1249 from yq314/add-nodejs-native-codegen 2020-10-08 19:58:06 +05:30
Liyas Thomas
6135bc716e Merge branch 'master' into add-nodejs-native-codegen 2020-10-08 14:41:42 +05:30
Liyas Thomas
1639ea7552 Merge pull request #1250 from hoppscotch/dependabot/npm_and_yarn/nuxtjs/pwa-3.1.2
Bump @nuxtjs/pwa from 3.1.0 to 3.1.2
2020-10-08 09:31:06 +05:30
dependabot[bot]
bea8c89d9f Bump @nuxtjs/pwa from 3.1.0 to 3.1.2
Bumps [@nuxtjs/pwa](https://github.com/nuxt-community/pwa-module) from 3.1.0 to 3.1.2.
- [Release notes](https://github.com/nuxt-community/pwa-module/releases)
- [Changelog](https://github.com/nuxt-community/pwa-module/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/pwa-module/compare/v3.1.0...v3.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-08 00:06:41 +00:00
Qing Ye
fe1618d1d8 move test data to __fixtures__ 2020-10-08 00:17:58 +08:00
Qing Ye
30250e1afa add codegen for nodejs native and instrument snapshot tests 2020-10-08 00:10:02 +08:00
Liyas Thomas
16f02e2544 Updated contributors and sponsors list 2020-10-07 14:21:39 +05:30
Liyas Thomas
123f4a3931 Updated contributors and sponsors list 2020-10-07 14:10:45 +05:30
YE Qing
ce5fb78bcd Fix form post data parsing (#1248)
Co-authored-by: Qing Ye <ye.qing@go-jek.com>
2020-10-07 08:51:03 +05:30
Liyas Thomas
2ccd053b0a Disable workbox 2020-10-07 07:22:44 +05:30
Liyas Thomas
3422ccef15 Downgrade pwa dep 2020-10-07 06:47:21 +05:30
Liyas Thomas
630663ec83 Merge branch 'master' of https://github.com/hoppscotch/hoppscotch 2020-10-07 05:54:13 +05:30
Liyas Thomas
37c6bf3a3f Updated dependencies 2020-10-07 05:47:09 +05:30
YE Qing
1c2963a1e0 add codegen for python-requests (#1244)
Co-authored-by: Qing Ye <ye.qing@go-jek.com>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-06 22:56:10 +05:30
Liyas Thomas
50a5b0f7db Updated all dependencies 2020-10-06 06:20:06 +05:30
Yasio
df933bb928 Add PHP CURL codegenerator (#1238)
* add php-curl codegen

* fix codegen name casing
2020-10-05 23:03:44 +05:30
Léo Martin
aa85108f1a Use socket.io echo server (#1237) 2020-10-05 22:07:03 +05:30
Andrew Bastin
c778a28f2b Fixed improper page title definition 2020-10-04 22:29:07 -04:00
Liyas Thomas
48142721ac I18n (#1235)
Co-authored-by: CHALOPIN Clément <clement.chalopin@gmail.com>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
Co-authored-by: Fiki Maulana <fikimaul@gmail.com>
Co-authored-by: Shunjid Rahman Showrov <shunjid.rahman@gmail.com>
Co-authored-by: Adrian Skar <adrian@adrianskar.com>
Co-authored-by: Lucas Queiroz <me@lucasqueiroz.dev>
Co-authored-by: piraces <raul.piraces@gmail.com>
Co-authored-by: vachan-maker <65799568+vachan-maker@users.noreply.github.com>
2020-10-05 07:07:02 +05:30
Liyas Thomas
826e3ebc5e Added default Socket.io URL - fixed #1231 2020-10-05 05:31:48 +05:30
Léo Martin
0f31259c97 Allow multiple arguments with socket.io (#1234)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-05 05:11:56 +05:30
Raul Piraces Alastuey
881d7fa5ee Add: Powershell RestMethod codegen (#1233) 2020-10-05 04:50:50 +05:30
Raul Piraces Alastuey
d479425333 Add: JS jQuery codegen (#1232)
* Add: JS jQuery codegen

* Remove console.log
2020-10-04 20:49:07 +05:30
Yasio
cb944bcfb2 Add nodejs request codegenerator (#1227)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-10-04 18:11:07 +05:30
Yasio
745683f2a8 Fix: passing auth into codegen (#1226) 2020-10-04 18:04:21 +05:30
Liyas Thomas
e019935545 Merge pull request #1220 from LeoMartinDev/feat/filter-graphql-fields 2020-10-04 16:17:53 +05:30
Liyas Thomas
9601477bba Merge branch 'master' into feat/filter-graphql-fields 2020-10-04 16:04:40 +05:30
Liyas Thomas
0702ca3fc9 Highlight filtered field and type 2020-10-04 16:01:19 +05:30
leoGeorges
d9a9ae7e72 Merge branch 'feat/filter-graphql-fields' of https://github.com/LeoMartinDev/hoppscotch into feat/filter-graphql-fields 2020-10-04 11:46:35 +02:00
leoGeorges
76eba3d638 Graphql: highlight fields on search 2020-10-04 11:46:16 +02:00
Liyas Thomas
69a448b4c1 Merge pull request #1223 from tonybatty/bug/http-page-headers-suggestions-are-overlapped
Fix: http page headers suggestions overlapped issue
2020-10-04 05:41:19 +05:30
tonybatty
a8d9a64f79 http page headers suggestions overlapped issue fixes #1222 2020-10-03 19:42:07 +01:00
Andrew Bastin
1546727612 Merge branch 'master' into feat/filter-graphql-fields 2020-10-03 13:10:22 -04:00
Liyas Thomas
dfdcc05d3e Merge pull request #1221 from tonybatty/bug/ios-home-bar-covers-content 2020-10-03 21:37:11 +05:30
leoGeorges
12941a107d Use normalized text 2020-10-03 17:36:26 +02:00
tonybatty
a94d46e005 fixes #1003 ios home bar covers content 2020-10-03 16:34:40 +01:00
leoGeorges
acb777c236 Remove debug code 2020-10-03 17:33:03 +02:00
leoGeorges
7e87cc1923 Add search input & capability to graphql types and fields 2020-10-03 17:24:26 +02:00
Saswata Mukherjee
1ad23f4ed8 Fix: GET requests for Go codegen (#1216) 2020-10-03 12:36:55 +05:30
Saswata Mukherjee
1bcba17f76 Add native Golang codegen (#1215) 2020-10-03 12:20:20 +05:30
Liyas Thomas
cb871f6a88 Merge branch 'master' of https://github.com/liyasthomas/postwoman 2020-10-02 05:59:35 +05:30
Liyas Thomas
6d2c296a7d chore: updated dependencies 2020-10-02 05:59:16 +05:30
Liyas Thomas
3434a1f5a6 Create codeql-analysis.yml 2020-10-01 08:05:55 +05:30
CHALOPIN Clément
f9c45d95e1 Add: js axios codegen (#1199) 2020-10-01 06:32:22 +05:30
Liyas Thomas
0bd051a7a9 chore: updated dependencies 2020-10-01 06:26:13 +05:30
Liyas Thomas
d4789463ad Improving code quality as per LGTM review 2020-09-30 21:02:19 +05:30
Liyas Thomas
7190a25550 Added Hacktoberfest announcement 2020-09-30 14:21:22 +05:30
Liyas Thomas
04bc8308ac Fixed #1195 2020-09-29 23:46:31 +05:30
Liyas Thomas
68c67fbd32 Turn off GitHub Pages 2020-09-29 23:42:09 +05:30
Liyas Thomas
50fd8926d6 Fixed #1197 2020-09-29 23:40:33 +05:30
Liyas Thomas
7e1e61f8af chores: Lint + dependency update 2020-09-27 23:34:15 +05:30
Andrew Bastin
ee066d7859 Use the new separated codegen for code generation 2020-09-25 23:45:36 -04:00
Andrew Bastin
54a48f9493 Added codegen registrar 2020-09-25 23:45:18 -04:00
Andrew Bastin
70fc0789d6 Added separated code generator code for cURL, JS Fetch and JS XHR 2020-09-25 23:38:33 -04:00
Liyas Thomas
66cd75dce8 Moved utility functions from assets to helpers 2020-09-26 07:57:45 +05:30
Liyas Thomas
8b3b6a471b chore: dependency updates, minor UI fixes 2020-09-26 07:09:26 +05:30
Liyas Thomas
e707fcda05 chore: dependency updates, minor UI updates, better color schemes 2020-09-25 06:39:21 +05:30
Liyas Thomas
a3574acabd Fixed hidden overflow on inputs 2020-09-24 23:31:43 +05:30
Liyas Thomas
f36db182f1 Better color schemes, minor UI fixes 2020-09-24 22:52:17 +05:30
Liyas Thomas
59e492b2a6 Merge branch 'refactor' 2020-09-24 21:39:14 +05:30
Liyas Thomas
6380063978 Upstream 2020-09-24 21:37:27 +05:30
Liyas Thomas
2686873163 Cleanup stale files/codes 2020-09-24 21:31:56 +05:30
Moulik Aggarwal
b2ef26600c Fix Quality Issues using DeepSource (#1183)
Co-authored-by: DeepSource Bot <bot@deepsource.io>
2020-09-24 20:46:20 +05:30
Liyas Thomas
e94fdcad32 Color modes (#1181) 2020-09-24 08:22:54 +05:30
Liyas Thomas
2e423b101b Refactoring based on LGTM analysis 2020-09-23 17:54:52 +05:30
Liyas Thomas
91677a41d4 Update README.md 2020-09-23 13:34:02 +05:30
Liyas Thomas
b747d0273c Feat/tailwind (#1179) 2020-09-22 22:36:37 +05:30
Matthew Jarman
45fb612793 Add check for active suggestion (#1180) 2020-09-21 22:14:16 +05:30
Liyas Thomas
db081d3197 Updated all major dependencies 2020-09-21 21:04:55 +05:30
Liyas Thomas
423eaa5462 Fixed broken GraphQL mock query 2020-09-21 11:20:42 +05:30
Liyas Thomas
f38dde160e Updated all non-major dependencies to latest 2020-09-21 06:03:40 +05:30
Andrew Bastin
782eb29aac Fixed url-field bug with caret reset due to v-model update 2020-09-20 00:57:09 -04:00
Andrew Bastin
5810c5544a Fixed bug with restore from history not working in new URL field 2020-09-19 01:30:22 +00:00
Liyas Thomas
b6eb581192 Revert last commit 2020-09-18 16:05:50 +05:30
Liyas Thomas
54171e566a FIxed broken restore history on new URL bar 2020-09-18 16:01:58 +05:30
Liyas Thomas
b7a44ab11f Experiments (#1174)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2020-09-18 08:58:11 +05:30
Andrew Bastin
4ea60d3431 Added temporary fix for the trailing null character from extension
requests
2020-09-17 21:36:36 -04:00
Liyas Thomas
9840ebfe33 Updated all non-major dependencies 2020-09-18 06:18:54 +05:30
Liyas Thomas
af2b726a38 chore: dependency updates + fixed a minor bug with edit folder 2020-09-17 06:08:26 +05:30
Liyas Thomas
d425a1b2c0 Merge pull request #1169 from hoppscotch/snyk-fix-3786512203b7ce5e8de4cfb302b63900
[Snyk] Security upgrade firebase-admin from 9.1.1 to 9.2.0
2020-09-16 16:04:39 +05:30
snyk-bot
687b0ac3bd fix: functions/package.json & functions/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-NODEFORGE-598677
2020-09-16 08:47:23 +00:00
Liyas Thomas
3978dd9b07 Updated all non-major dependencies to latest 2020-09-15 06:49:03 +05:30
Liyas Thomas
d9f78b9d5c Fixed #1161 2020-09-14 15:09:44 +05:30
Liyas Thomas
81e638a7e0 chore: updated all non-major dependencies 2020-09-14 07:35:58 +05:30
Liyas Thomas
6dc906a604 🐛 Fixed #1160 2020-09-11 14:12:10 +05:30
Liyas Thomas
005db4d0e8 Fixed #1159 2020-09-11 15:07:57 +05:30
dependabot[bot]
41127da7d9 Bump node-fetch from 2.6.0 to 2.6.1 in /functions (#1158)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-11 05:52:51 +05:30
Liyas Thomas
3111b8d395 📦 Updated all non-major dependencies 2020-09-11 05:38:12 +05:30
Liyas Thomas
89f2647ead 📦 Updated all major dependencies 2020-09-10 19:07:49 +05:30
Liyas Thomas
084a432741 Merge branch 'master' of https://github.com/hoppscotch/hoppscotch into master 2020-09-10 05:46:13 +05:30
Liyas Thomas
1f071d1608 Updated all non-major dependencies 2020-09-10 05:45:56 +05:30
Liyas Thomas
3f75d0df56 Make use of GitHub sponsor API 2020-09-09 12:05:22 +05:30
Liyas Thomas
ba108fe23a Updated all non-major dependencies 2020-09-09 05:47:43 +05:30
Florin Mirosnicencu
ab736cf975 Handle postman subfolders when importing json (#1150) 2020-09-08 20:44:40 +05:30
Liyas Thomas
c36239772e Updated all non-major dependencies 2020-09-07 14:32:30 +05:30
Liyas Thomas
286090d58a Fixed #1146 2020-09-06 19:05:15 +05:30
Liyas Thomas
69fda864db Merge branch 'master' of https://github.com/hoppscotch/hoppscotch into master 2020-09-06 14:27:20 +05:30
Liyas Thomas
2709c5d332 Fixed typo, updated sponsors list 2020-09-06 14:26:54 +05:30
Scott Dutton
da5fdd54bd Import postman variables correctly (#1142)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2020-09-05 12:07:37 +05:30
Tiago Filipe Silva
92e7453c31 Lossless optimized images (#1141) 2020-09-05 12:01:42 +05:30
Liyas Thomas
a59e1a2015 Updated Sponsors list with GitHub sponsors 2020-09-04 16:51:27 +05:30
Liyas Thomas
838eb845d4 Merge branch 'master' of https://github.com/postwoman-io/postwoman into master 2020-09-04 06:35:50 +05:30
Liyas Thomas
f69fdc634a 📦 Updated all non-major dependencies 2020-09-04 06:35:22 +05:30
Liyas Thomas
9c0e7272ff I18n (#1138)
Co-authored-by: Gustavo Cavalieri <gugacavalieri@gmail.com>
Co-authored-by: Antonio Almeida <promatik@gmail.com>
Co-authored-by: Burak Sakallı <buraksakalli06@gmail.com>
Co-authored-by: Nisgrak <eneko.rodriguez99@gmail.com>
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
Co-authored-by: Junseo Park <wonderbotsupport@naver.com>
Co-authored-by: Jinseok Jeong <checkitup@hotmail.com>
2020-09-03 14:27:40 +05:30
Liyas Thomas
515afdc5ae Updated all non-major dependencies 2020-09-02 05:54:05 +05:30
Liyas Thomas
f32fa485e0 Merge branch 'master' of https://github.com/postwoman-io/postwoman into master 2020-09-01 06:05:09 +05:30
Liyas Thomas
a5187801b4 Updated all non-major dependencies 2020-09-01 06:04:43 +05:30
Liyas Thomas
6cb11d6a61 Added email address for contact 2020-08-31 13:23:39 +05:30
Liyas Thomas
1e91ca5eda Added default BASE_URL env variable 2020-08-30 11:21:42 +05:30
Liyas Thomas
210727ea0e Added default BASE_URL env variable 2020-08-30 11:10:19 +05:30
Liyas Thomas
f429585447 Removed @nuxtjs/dotenv (#1128)
* Removed `@nuxtjs/dotenv`

* Removed `@nuxtjs/dotenv`

* Fix broken tests

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2020-08-29 23:10:06 +05:30
Andrew Bastin
759a3f17cc Merge pull request #1126 from hoppscotch/tests
tests: improve test suite
2020-08-29 12:41:08 -04:00
jamesgeorge007
de7d1c2baa refactor: helper to compute suggestions 2020-08-29 22:09:12 +05:30
James George
8223011c3d chore: rename exported files (#1127)
* chore: rename to hoppscotch-collection.json

* chore: rename to hoppscotch-environment.json
2020-08-29 13:11:49 +05:30
Andrew Bastin
53684d6fbb Add test spec for components/ui/autocomplete.vue 2020-08-29 00:25:46 -04:00
Andrew Bastin
31cefd7dd0 Remove use of keyboardevent.which as its deprecated in autocomplete 2020-08-29 00:24:54 -04:00
Liyas Thomas
993a8f83ff 📦 Updated all non-major dependencies 2020-08-28 00:12:27 +05:30
Liyas Thomas
fd1e00986c 📦 Updated all non-major dependencies 2020-08-27 23:28:31 +05:30
Liyas Thomas
62028d1273 Reset file input value after operations - fixed #1124 2020-08-27 13:56:55 +05:30
Liyas Thomas
240ae25a90 📦 Updated all minor dependencies 2020-08-27 05:38:38 +05:30
Andrew Bastin
2bd242a164 jsonParse now validates for array as root JSONs
fixes #1097
2020-08-26 18:21:37 -04:00
Andrew Bastin
4b87684611 Updated debounce test spec to use jest timers 2020-08-26 12:14:47 -04:00
Andrew Bastin
3cd2a2a0f9 Added test spec for components/ui/pw-toggle.vue 2020-08-25 22:59:46 -04:00
Liyas Thomas
dc129eeb7d Fixed word wrap on 'Send' button - resolves #1116 2020-08-26 07:30:40 +05:30
Liyas Thomas
fb3f426688 📦 Updated all minor dependencies 2020-08-26 06:02:03 +05:30
Andrew Bastin
f33ebbcce0 Add test spec for components/ui/tabs.vue 2020-08-25 15:44:16 -04:00
Liyas Thomas
86922df61a Detect browser language and always redirect to locale - fixed #1108 2020-08-25 21:55:23 +05:30
Liyas Thomas
2916ab502e Announcement banner 2020-08-25 13:27:59 +05:30
Liyas Thomas
9c24fcec0d Merge pull request #1115 from HerbertChu/socket-io-path
support user to customize the path to socket-io server
2020-08-25 12:55:57 +05:30
herbertc
8258997f3e support user to customize the path to socket-io server 2020-08-25 15:16:38 +08:00
Liyas Thomas
ef714c3903 Updated Shortcuts wiki 2020-08-25 08:11:58 +05:30
Liyas Thomas
76c0874f0e 📦 Updated all non-major dependencies 2020-08-25 06:51:47 +05:30
Andrew Bastin
9166f734c9 Add test spec for components/ui/tab.vue 2020-08-24 15:42:48 -04:00
Andrew Bastin
368421a498 added jest-dom as dev dependency and added jest setup script 2020-08-24 15:41:59 -04:00
Andrew Bastin
c8d8c68b95 corrected typo for gql component test spec folder name 2020-08-24 14:23:15 -04:00
Khaleel Gibran
e68cd3c47f replace postwoman with hoppscotch values (#1111)
Replace Postwoman with Hoppscotch
2020-08-24 19:50:09 +05:30
Liyas Thomas
1732d7e707 Merge pull request #1109 from sebastianwd/patch-1 2020-08-24 10:53:27 +05:30
Sebastian Luque
da0d778b25 Typos in spanish translation 2020-08-23 23:52:45 -05:00
Andrew Bastin
b8bd0cc63a Add test spec for components/graphql/field.vue 2020-08-23 21:39:21 -04:00
Andrew Bastin
5150e13121 Removed unused fieldString computed property in gql/field.vue 2020-08-23 21:39:21 -04:00
Liyas Thomas
b2f784eeff Update README.md 2020-08-24 05:49:59 +05:30
Liyas Thomas
ad98284caf Add HOST 0.0.0.0 2020-08-24 05:45:35 +05:30
Liyas Thomas
6915ea70ca Add host 0.0.0.0 2020-08-24 05:18:02 +05:30
Liyas Thomas
26eb99d4a7 Redirect 2020-08-24 04:52:57 +05:30
Andrew Bastin
e1eb1be781 Add test spec for components/graphql/type.vue 2020-08-23 17:40:32 -04:00
Andrew Bastin
b536e191b1 Remove unused graphql/argument component 2020-08-23 15:48:05 -04:00
Andrew Bastin
f1b475b966 Add test spec for components/graphql/typelink.vue 2020-08-23 14:46:36 -04:00
Liyas Thomas
23a61d400d Add host: 0.0.0.0 to server property. #1099 2020-08-23 18:47:57 +05:30
Liyas Thomas
4ae8308663 Updated icon locations 2020-08-23 17:03:49 +05:30
Liyas Thomas
c3d6343edb Updated social share images 2020-08-23 16:47:48 +05:30
Liyas Thomas
1059d2ddbd Updated screenshots 2020-08-23 16:43:35 +05:30
Liyas Thomas
10ecc27bb5 Updated screenshots 2020-08-23 16:40:53 +05:30
Liyas Thomas
0c3c540051 Updated image assets 2020-08-23 16:21:49 +05:30
Liyas Thomas
c44dba5e92 Merge pull request #1107 from hoppscotch/a11y
♥ a11y
2020-08-23 13:38:22 +05:30
Liyas Thomas
82da7d0b24 Map DELETE method Alt + X 2020-08-23 11:16:25 +05:30
Liyas Thomas
4dd522eda4 Merge branch 'a11y' of https://github.com/postwoman-io/postwoman into a11y 2020-08-23 09:25:49 +05:30
Liyas Thomas
bbe300e847 Updated keyboard shortcuts 2020-08-23 09:23:34 +05:30
Andrew Bastin
dd667e5b4a Merge branch 'master' into a11y 2020-08-22 23:27:45 -04:00
Andrew Bastin
6f013de8e4 Added test spec for helpers/strategies/ExtensionStrategy.js 2020-08-22 23:25:35 -04:00
Andrew Bastin
96a6289911 Fixed a typo in decodeB64StringToArrayBuffer line for proxy-extension
request
2020-08-22 23:25:35 -04:00
Andrew Bastin
e0970fc69d Remove No-Proxy test stubs from AxiosStrategy-Proxy.spec.js 2020-08-22 23:25:35 -04:00
Liyas Thomas
5cafc7bb37 ❤️ ally 2020-08-23 08:41:04 +05:30
Liyas Thomas
2b755c0497 Updated all minor dependencies 2020-08-23 05:48:18 +05:30
Andrew Bastin
e3beae244d Add test spec for helpers/strategies/AxiosStrategy.js 2020-08-21 23:12:28 -04:00
Andrew Bastin
c2ec7be719 Modify AxiosStrategy to allow for some testing 2020-08-21 23:12:27 -04:00
Liyas Thomas
ed5d9132d3 📦 Updated all non-major dependencies 2020-08-21 06:05:50 +05:30
Liyas Thomas
8654db534b 📦 Updated all non-major dependencies 2020-08-21 06:00:11 +05:30
Andrew Bastin
e20299eb94 Added test spec for helpers/lenses/lenses.js 2020-08-20 20:08:45 -04:00
Andrew Bastin
1f65907f70 Export lenses array for helping testing 2020-08-20 20:07:59 -04:00
Liyas Thomas
6a55c09328 Fixed #1098 2020-08-20 19:54:12 +05:30
Andrew Bastin
9734e4d859 Add test spec for helpers/network.js 2020-08-19 16:16:49 -04:00
Andrew Bastin
4f93bc5721 Added test spec for helpers/platformutils.js 2020-08-19 01:37:38 -04:00
Andrew Bastin
73dc0b1cfa Added test spec for helpers/jsonParse.js 2020-08-19 01:08:02 -04:00
Andrew Bastin
e31979002f Add test spec for helpers/editorutils.js 2020-08-19 00:43:47 -04:00
Andrew Bastin
4be1e527fc Add application/vnd.api+json as a valid JSON mime to editorutils 2020-08-19 00:41:06 -04:00
Andrew Bastin
45332b3202 Added test spec for helpers/util/uri.js 2020-08-18 23:15:42 -04:00
Liyas Thomas
cb0258cb58 Merge pull request #1096 from hoppscotch/dependabot/npm_and_yarn/nuxt-i18n-6.13.10 2020-08-19 08:17:20 +05:30
dependabot[bot]
f3d80f28fd Bump nuxt-i18n from 6.13.9 to 6.13.10
Bumps [nuxt-i18n](https://github.com/nuxt-community/i18n-module) from 6.13.9 to 6.13.10.
- [Release notes](https://github.com/nuxt-community/i18n-module/releases)
- [Changelog](https://github.com/nuxt-community/i18n-module/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/i18n-module/compare/v6.13.9...v6.13.10)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-19 00:03:03 +00:00
Andrew Bastin
4b73ee3d41 Added test spec for helpers/utils/debounce.js 2020-08-18 14:34:46 -04:00
Andrew Bastin
e0e2e0c2fb Added test spec for helpers/util/contenttypes.js 2020-08-18 14:13:52 -04:00
Andrew Bastin
f2b977c941 isJSONContentType returns false for null/undefined 2020-08-18 14:13:20 -04:00
Andrew Bastin
9fe10fd9a2 Added test spec for helpers/utils/b64.js 2020-08-18 14:01:04 -04:00
Liyas Thomas
6b444d280a Merge pull request #1095 from thierrydev/patch-1 2020-08-18 13:01:54 +05:30
Thierry Meliot
10c2acf410 Update README.md
Updated the second Telegram URL
2020-08-18 08:53:21 +02:00
Liyas Thomas
6bb5908aba Merge pull request #1088 from hoppscotch/tests 2020-08-18 08:22:15 +05:30
Liyas Thomas
f54b24b4da Merge pull request #1093 from hoppscotch/dependabot/npm_and_yarn/nuxtjs/pwa-3.0.1 2020-08-18 06:41:51 +05:30
dependabot[bot]
ef3c548c98 Bump @nuxtjs/pwa from 3.0.0 to 3.0.1
Bumps [@nuxtjs/pwa](https://github.com/nuxt-community/pwa-module) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/nuxt-community/pwa-module/releases)
- [Changelog](https://github.com/nuxt-community/pwa-module/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/pwa-module/compare/v3.0.0...v3.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-18 00:02:15 +00:00
Liyas Thomas
820690a073 Lock package 2020-08-18 02:45:44 +05:30
Liyas Thomas
63cf4f0045 Merge branch 'master' into tests 2020-08-18 08:14:28 +05:30
Liyas Thomas
deecbdd124 Update dependencies 2020-08-18 02:41:45 +05:30
Andrew Bastin
07f790dda0 Added test spec for helper/utils/valid.js 2020-08-17 22:10:47 -04:00
Andrew Bastin
a91b1ecf1f Added babel-core dev dependency 2020-08-17 19:38:17 -04:00
Liyas Thomas
4f75dd30fe Fix broken mobile responsiveness and URL input type 2020-08-17 17:04:13 +05:30
Liyas Thomas
629ee3e33f Merge pull request #1091 from hoppscotch/svgicons 2020-08-17 21:56:34 +05:30
Liyas Thomas
54e06d26bc Merge pull request #1090 from hoppscotch/autoimport 2020-08-17 18:41:43 +05:30
Liyas Thomas
dda26b7a3f Auto import components 2020-08-17 18:37:54 +05:30
Liyas Thomas
e86a17980b Auto import components 2020-08-17 18:29:14 +05:30
Liyas Thomas
23068450ed Remove stale file 2020-08-17 16:15:37 +05:30
Liyas Thomas
3924370b3e Introduce component wrapper for svg icons 2020-08-17 16:07:36 +05:30
Liyas Thomas
f57ac978ae Tests 2020-08-17 09:22:02 +05:30
Liyas Thomas
e68ad7331f Added default value to BASE_URL 2020-08-17 08:19:23 +05:30
Liyas Thomas
9b5fb4bde1 Add BASE_URL to environment variables 2020-08-17 07:49:11 +05:30
Liyas Thomas
a8f1fb5148 Merge pull request #1087 from hoppscotch/v2 2020-08-17 07:43:43 +05:30
Liyas Thomas
65185ecb77 Merge branch 'master' into v2 2020-08-17 07:26:19 +05:30
Liyas Thomas
14a68f24e4 Updated instances of Postwoman 2020-08-17 07:24:25 +05:30
Liyas Thomas
16a93c2b7e Merge pull request #1086 from hoppscotch/v2 2020-08-17 06:57:21 +05:30
Liyas Thomas
925690fb00 Fix conflicts 2020-08-17 06:52:38 +05:30
Liyas Thomas
dc279bc335 Updated dependencies 2020-08-17 06:49:33 +05:30
Liyas Thomas
d1d759e86f Merge branch 'master' into v2 2020-08-17 06:45:10 +05:30
Liyas Thomas
82a30fbd25 Updated dependencies 2020-08-17 06:41:25 +05:30
Liyas Thomas
12fea29f48 Merge pull request #1085 from hoppscotch/dependabot/npm_and_yarn/eslint-7.7.0 2020-08-17 06:38:59 +05:30
dependabot[bot]
b602f7c03f Bump eslint from 7.6.0 to 7.7.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.6.0 to 7.7.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.6.0...v7.7.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-17 00:10:14 +00:00
Liyas Thomas
a20ab981a7 Merge pull request #1071 from hoppscotch/v2 2020-08-16 22:21:41 +05:30
Liyas Thomas
9ae08cecf2 Merge branch 'master' into v2 2020-08-16 22:17:35 +05:30
Joel Camus Bueno
c9cd7b4344 Issue/1080 (#1081) 2020-08-16 17:26:33 +05:30
Liyas Thomas
24b587762a Lint 2020-08-16 05:48:00 +05:30
Joel Camus Bueno
18a2fb38d1 Issue/1078 (#1079)
* Externalize property of deletion messages (Collection) #1078

* Externalize property of deletion messages (folder) #1078

* Externalize property of deletion messages (request) #1078

* Externalize property of deletion messages (environment) #1078

* Externalize property (fr-FR.json) of deletion messages (collection, folder, request, environment) #1078

* Externalize property (pt-PT.json) of deletion messages (collection, folder, request, environment) #1078

* Externalize property (pt-BR.json) of deletion messages (collection, folder, request, environment) #1078

* Updating the Spanish file  (es-ES)
2020-08-15 20:30:00 +05:30
Joel Camus Bueno
5f47cdb763 Issue/1076 (#1077) 2020-08-15 16:57:14 +05:30
Liyas Thomas
e5a18b6e4c Fixed typo 2020-08-14 18:16:46 +05:30
Liyas Thomas
519976eb0b Merge pull request #1072 from postwoman-io/dependabot/npm_and_yarn/firebase-7.18.0
Bump firebase from 7.17.2 to 7.18.0
2020-08-14 06:54:21 +05:30
dependabot[bot]
b630fbc641 Bump firebase from 7.17.2 to 7.18.0
Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 7.17.2 to 7.18.0.
- [Release notes](https://github.com/firebase/firebase-js-sdk/releases)
- [Commits](https://github.com/firebase/firebase-js-sdk/compare/firebase@7.17.2...firebase@7.18.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-14 00:05:57 +00:00
Liyas Thomas
2369a22d23 Typos 2020-08-13 16:54:27 +05:30
Liyas Thomas
124d9ff269 v2 2020-08-13 16:50:02 +05:30
Liyas Thomas
1c95ac7f07 Merge pull request #1066 from Reflex-Gravity/master
Fixed broken tick icons placement in Settings
2020-08-11 08:39:16 +05:30
Joel DSouza
54169fe22b Fixed broken tick icons placement in Settings 2020-08-11 06:34:36 +05:30
Liyas Thomas
4707b78c1f Merge pull request #1064 from postwoman-io/dependabot/npm_and_yarn/yargs-parser-19.0.1
Bump yargs-parser from 18.1.3 to 19.0.1
2020-08-10 06:15:02 +05:30
dependabot[bot]
95d91efe52 Bump yargs-parser from 18.1.3 to 19.0.1
Bumps [yargs-parser](https://github.com/yargs/yargs-parser) from 18.1.3 to 19.0.1.
- [Release notes](https://github.com/yargs/yargs-parser/releases)
- [Changelog](https://github.com/yargs/yargs-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/yargs-parser/compare/v18.1.3...v19.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-10 00:37:36 +00:00
Liyas Thomas
fc0c7f536b Merge pull request #1063 from postwoman-io/dependabot/npm_and_yarn/start-server-and-test-1.11.3 2020-08-10 06:05:52 +05:30
dependabot[bot]
80f1cfc72d Bump start-server-and-test from 1.11.2 to 1.11.3
Bumps [start-server-and-test](https://github.com/bahmutov/start-server-and-test) from 1.11.2 to 1.11.3.
- [Release notes](https://github.com/bahmutov/start-server-and-test/releases)
- [Commits](https://github.com/bahmutov/start-server-and-test/compare/v1.11.2...v1.11.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-10 00:04:52 +00:00
Liyas Thomas
628012fb49 Merge pull request #1054 from postwoman-io/dependabot/npm_and_yarn/graphql-language-service-interface-2.4.1
Bump graphql-language-service-interface from 2.4.0 to 2.4.1
2020-08-07 07:42:54 +05:30
dependabot[bot]
a29498e911 Bump graphql-language-service-interface from 2.4.0 to 2.4.1
Bumps [graphql-language-service-interface](https://github.com/graphql/graphiql) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/graphql/graphiql/releases)
- [Changelog](https://github.com/graphql/graphiql/blob/main/CHANGELOG.md)
- [Commits](https://github.com/graphql/graphiql/compare/graphql-language-service-interface@2.4.0...graphql-language-service-interface@2.4.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-07 02:09:28 +00:00
Liyas Thomas
3d460b5862 Merge pull request #1055 from postwoman-io/dependabot/npm_and_yarn/firebase-7.17.2
Bump firebase from 7.17.1 to 7.17.2
2020-08-07 07:37:03 +05:30
Liyas Thomas
4acb2a5d92 Merge branch 'master' into dependabot/npm_and_yarn/firebase-7.17.2 2020-08-07 07:32:07 +05:30
Liyas Thomas
47c800b78b Merge pull request #1053 from AndrewBastin/feat/extension-setting 2020-08-07 06:45:24 +05:30
dependabot[bot]
a20da8036f Bump firebase from 7.17.1 to 7.17.2
Bumps [firebase](https://github.com/firebase/firebase-js-sdk) from 7.17.1 to 7.17.2.
- [Release notes](https://github.com/firebase/firebase-js-sdk/releases)
- [Commits](https://github.com/firebase/firebase-js-sdk/compare/firebase@7.17.1...firebase@7.17.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-07 00:07:29 +00:00
Andrew Bastin
7897118fbd i10n for extension version display 2020-08-06 14:46:55 -04:00
Andrew Bastin
0246e09595 Extension Version is reported on the settings page 2020-08-06 14:40:41 -04:00
Liyas Thomas
72ad37456a Removed all instances of the term 'Postman' 2020-08-06 11:03:50 +05:30
Liyas Thomas
83ec0a6c76 Merge pull request #1050 from postwoman-io/dependabot/npm_and_yarn/sass-loader-9.0.3
Bump sass-loader from 9.0.2 to 9.0.3
2020-08-06 08:16:58 +05:30
dependabot[bot]
63f77b0dc5 Bump sass-loader from 9.0.2 to 9.0.3
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 9.0.2 to 9.0.3.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v9.0.2...v9.0.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-06 02:40:28 +00:00
Liyas Thomas
798d2e0e6b Merge pull request #1051 from postwoman-io/dependabot/npm_and_yarn/nuxtjs/axios-5.12.1
Bump @nuxtjs/axios from 5.12.0 to 5.12.1
2020-08-06 08:08:15 +05:30
dependabot[bot]
fd098b5440 Bump @nuxtjs/axios from 5.12.0 to 5.12.1
Bumps [@nuxtjs/axios](https://github.com/nuxt-community/axios-module) from 5.12.0 to 5.12.1.
- [Release notes](https://github.com/nuxt-community/axios-module/releases)
- [Changelog](https://github.com/nuxt-community/axios-module/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/axios-module/compare/v5.12.0...v5.12.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-06 00:08:12 +00:00
Liyas Thomas
11d30e409c 💄 Updated fonts to latest version 2020-08-04 09:39:52 +05:30
Andrew Bastin
6cb5ae246d Merge pull request #1044 from postwoman-io/dependabot/npm_and_yarn/cypress-4.12.0
Bump cypress from 4.10.0 to 4.12.0
2020-08-03 21:03:57 -04:00
Andrew Bastin
60c76ba854 Merge branch 'master' into dependabot/npm_and_yarn/cypress-4.12.0 2020-08-03 21:00:21 -04:00
Andrew Bastin
ebf7b32408 Fix Proxy error issue #1027 2020-08-03 20:47:44 -04:00
dependabot[bot]
f343459236 Bump cypress from 4.10.0 to 4.12.0
Bumps [cypress](https://github.com/cypress-io/cypress) from 4.10.0 to 4.12.0.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Commits](https://github.com/cypress-io/cypress/compare/v4.10.0...v4.12.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-04 00:04:52 +00:00
Andrew Bastin
d2a57864f0 Merge pull request #1043 from AndrewBastin/bug/1036
Test scripts get properly parsed JSON body
2020-08-03 14:29:47 -04:00
Andrew Bastin
8a07f281ff Test scripts get parsed JSON body 2020-08-03 13:42:33 -04:00
Andrew Bastin
b7b721abd5 Merge pull request #1040 from AndrewBastin/feat/js-linting
Basic linting for JS code
2020-08-02 13:20:40 -04:00
Andrew Bastin
45f1e386cc PreReq scripts and Test scripts now use JSEditor 2020-08-02 13:14:14 -04:00
Andrew Bastin
3c3e8a6c31 Move JS linting code to separate component 2020-08-02 11:47:16 -04:00
Andrew Bastin
ec99b48605 Added JS linting to Ace Editor 2020-08-02 00:26:16 -04:00
Andrew Bastin
da9e599e8f Added esprima as dependency 2020-08-02 00:26:16 -04:00
Andrew Bastin
b1d3fa3f42 Handled error handling to pre-request script execution 2020-08-02 00:26:16 -04:00
Andrew Bastin
7954672ef8 Updated README browser extension links 2020-07-31 23:50:49 -04:00
Liyas Thomas
20a6039bd0 Update README.md 2020-08-01 09:04:46 +05:30
Liyas Thomas
d7fe61bc3d Update README.md 2020-08-01 08:58:29 +05:30
Liyas Thomas
b10406896a Merge pull request #1039 from postwoman-io/dependabot/npm_and_yarn/eslint-7.6.0
Bump eslint from 7.5.0 to 7.6.0
2020-08-01 08:52:00 +05:30
dependabot[bot]
1f135e0721 Bump eslint from 7.5.0 to 7.6.0
Bumps [eslint](https://github.com/eslint/eslint) from 7.5.0 to 7.6.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.5.0...v7.6.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-01 03:18:02 +00:00
Liyas Thomas
e1bdc9ce33 Merge pull request #1038 from postwoman-io/dependabot/npm_and_yarn/nuxt-i18n-6.13.2 2020-08-01 08:38:19 +05:30
dependabot[bot]
e3654bb472 Bump nuxt-i18n from 6.13.1 to 6.13.2
Bumps [nuxt-i18n](https://github.com/nuxt-community/i18n-module) from 6.13.1 to 6.13.2.
- [Release notes](https://github.com/nuxt-community/i18n-module/releases)
- [Changelog](https://github.com/nuxt-community/i18n-module/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nuxt-community/i18n-module/compare/v6.13.1...v6.13.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-01 02:32:25 +00:00
Liyas Thomas
6418d6bcff Delete codeql-analysis.yml
codeql-analysis isn't available for this org. will be added once it is out of beta.
2020-08-01 07:53:50 +05:30
Liyas Thomas
9bf7c5013e Merge pull request #1037 from liyasthomas/dependabot/npm_and_yarn/elliptic-6.5.3
Bump elliptic from 6.5.2 to 6.5.3
2020-07-31 21:14:36 +05:30
dependabot[bot]
1797e78ebd Bump elliptic from 6.5.2 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-31 15:38:22 +00:00
347 changed files with 46048 additions and 26061 deletions

View File

@@ -15,43 +15,6 @@
"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",
@@ -61,82 +24,10 @@
"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",
"projectName": "hoppscotch",
"projectOwner": "hoppscotch",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true

View File

@@ -97,8 +97,8 @@ sw.*
# Vim swap files
*.swp
# Postwoman build data
.postwoman
# Build data
.hoppscotch
# File explorer
.directory

View File

@@ -1,11 +1,13 @@
# https://editorconfig.org
# editorconfig.org
root = true
[*]
indent_size = 2
indent_style = space
charset = utf-8
indent_size = 2
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

View File

@@ -1,14 +1,5 @@
{
"projects": {
"default": "postwoman-api"
},
"targets": {
"postwoman-api": {
"hosting": {
"postwoman": [
"postwoman"
]
}
}
}
}

4
.github/FUNDING.yml vendored
View File

@@ -1,4 +1,4 @@
github: postwoman-io
open_collective: postwoman
github: hoppscotch
open_collective: hoppscotch
patreon: liyasthomas
custom: https://www.paypal.me/liyascthomas

View File

@@ -3,7 +3,7 @@ updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
interval: weekly
time: '00:00'
open-pull-requests-limit: 10
reviewers:

View File

@@ -1,19 +1,33 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"
on:
push:
branches: [master, ]
branches: [main]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
branches: [main]
schedule:
- cron: '0 13 * * 0'
- cron: '0 0 * * 6'
jobs:
analyse:
name: Analyse
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['javascript']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@v2
@@ -30,9 +44,12 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)

4
.gitignore vendored
View File

@@ -93,8 +93,8 @@ sw.*
# Vim swap files
*.swp
# Postwoman build data
.postwoman
# Build data
.hoppscotch
# File explorer
.directory

1
.husky/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"
npm run pretty-quick

View File

@@ -1,7 +1,7 @@
.dependabot
.github
.nuxt
.postwoman
.hoppscotch
.vscode
package-lock.json
node_modules

View File

@@ -15,16 +15,11 @@ node_js:
os: linux
addons:
apt:
packages:
- libgconf-2-4 # cypress binary dependency
cache: npm
branches:
only:
- master
- main
install:
- npm ci

View File

@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at hello@postwoman.io. All
reported by contacting the project team at liyascthomas@gmail.com. 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.

View File

@@ -73,7 +73,7 @@ further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
reported by contacting the project team at liyascthomas@gmail.com. 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.

View File

@@ -6,16 +6,20 @@ LABEL maintainer="Liyas Thomas (liyascthomas@gmail.com)"
RUN apk add --update --no-cache \
git
# Create app directory
WORKDIR /app
COPY package*.json ./
RUN npm install
ADD . /app/
COPY . .
RUN npm run build
RUN npm run generate
ENV HOST 0.0.0.0
EXPOSE 3000
CMD ["npm", "run", "start"]
CMD ["npm", "run", "dev"]

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 Liyas Thomas
Copyright (c) 2020
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

670
README.md
View File

@@ -1,107 +1,140 @@
<div align="center">
<a href="https://postwoman.io"><img src="https://raw.githubusercontent.com/liyasthomas/postwoman/master/static/logo.png" alt="Postwoman.io logo" height="160"></a>
<a href="https://hoppscotch.io"><img src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/static/logo.png" alt="hoppscotch.io logo" height="128"></a>
<br>
<br>
<p>
<b>A free, fast and beautiful API request builder</b>
<b>Hoppscotch - Open source API development ecosystem</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>
<i>Helps you create requests faster, saving precious time on development - <a href="http://eepurl.com/g6n_P5">Subscribe</a></i>
</p>
<p>
[![Travis Build Status](https://img.shields.io/travis/com/liyasthomas/postwoman?logo=Travis)](https://travis-ci.com/liyasthomas/postwoman) [![GitHub release](https://img.shields.io/github/release/liyasthomas/postwoman/all?logo=GitHub)](https://github.com/liyasthomas/postwoman/releases/latest) [![Website](https://img.shields.io/website?url=https%3A%2F%2Fpostwoman.io&logo=Postwoman)](https://postwoman.io) [![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen)](CONTRIBUTING.md) [![Financial Contributors on Open Collective](https://img.shields.io/opencollective/all/postwoman?logo=Open-Collective&label=financial+contributors)](https://opencollective.com/postwoman) [![Donate on PayPal](https://img.shields.io/badge/support-PayPal-blue?logo=PayPal)](https://www.paypal.me/liyascthomas) [![Chat on Telegram](https://img.shields.io/badge/chat-Telegram-blueviolet?logo=Telegram)](https://t.me/postwoman_app) [![Chat on Discord](https://img.shields.io/badge/chat-Discord-violet?logo=discord)](https://discord.gg/GAMWxmR) [![Tweet](https://img.shields.io/twitter/url?url=https%3A%2F%2Fpostwoman.io%2F)](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)
[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen?logo=github)](CODE_OF_CONDUCT.md) [![Website](https://img.shields.io/website?url=https%3A%2F%2Fhoppscotch.io&logo=hoppscotch)](https://hoppscotch.io) [![Travis Build Status](https://img.shields.io/travis/com/hoppscotch/hoppscotch/main?logo=Travis)](https://travis-ci.com/hoppscotch/hoppscotch) [![Tweet](https://img.shields.io/twitter/url?url=https%3A%2F%2Fhoppscotch.io%2F)](https://twitter.com/intent/tweet?url=https%3A%2F%2Fhoppscotch.io&text=%F0%9F%91%BD%20Hoppscotch%20%E2%80%A2%20API%20request%20builder%20-%20Helps%20you%20create%20requests%20faster%2C%20saving%20precious%20time%20on%20development&original_referer=https%3A%2F%2Ftwitter.com%2Fshare%3Ftext%3D%25F0%259F%2591%25BD%2520Hoppscotch%2520%25E2%2580%25A2%2520API%2520request%2520builder%2520-%2520Helps%2520you%2520create%2520requests%2520faster%2C%2520saving%2520precious%2520time%2520on%2520development%26url%3Dhttps%3A%2F%2Fhoppscotch.io%26hashtags%3Dhoppscotch%26via%3Dliyasthomas&via=liyasthomas&hashtags=hoppscotch)
</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>
<a href="https://github.com/hoppscotch/hoppscotch/graphs/contributors">contributors</a>
</sub>
</p>
</div>
---
**Read: [Story behind Postwoman](https://dev.to/liyasthomas/i-created-postwoman-an-online-open-source-api-request-builder-41md)**
**Chat: [Telegram](https://t.me/postwoman_app), [Discord](https://discord.gg/GAMWxmR)**
**Donate: [GitHub Sponsors](https://github.com/sponsors/postwoman-io), [Open Collective](https://opencollective.com/postwoman), [Patreon](https://www.patreon.com/liyasthomas), [PayPal](https://www.paypal.me/liyascthomas)**
<p align="center">
<b>Sponsored by</b>
<br>
<br>
<a href="https://appwrite.io/?utm_source=hoppscotch&utm_medium=banner&utm_campaign=hello" title="Appwrite" target="_blank">
<img height="60px" src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/assets/images/appwrite-banner.svg" title="Appwrite">
</a>
</p>
<div align="center">
<a href="https://postwoman.io"><img src="https://raw.githubusercontent.com/liyasthomas/postwoman/master/static/images/screenshot1.png" alt="Screenshot1" width="100%"></a>
<a href="https://hoppscotch.io"><img src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/static/images/screenshots/banner_dark.png" alt="Screenshot" width="100%"></a>
</div>
<p align="center">
<b>Powered by</b>
<br>
<br>
<a href="https://oss.capital/?ref=hoppscotch" title="OSS Capital" target="_blank">
<img height="100px" src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/assets/images/ossc-banner.svg" title="OSS Capital">
</a>
</p>
#### **Contact**
[![Chat on Telegram](https://img.shields.io/badge/chat-Telegram-2CA5E0?logo=Telegram)](https://t.me/hoppscotch) [![Chat on Discord](https://img.shields.io/badge/chat-Discord-7289DA?logo=discord)](https://discord.gg/GAMWxmR)
#### **Support**
[![Sponsor on GitHub](https://img.shields.io/badge/sponsor-GitHub-181717?logo=github)](https://github.com/sponsors/hoppscotch) [![Contribute on Open Collective](https://img.shields.io/badge/contribute-Open%20Collective-7FADF2?logo=open-collective)](https://opencollective.com/hoppscotch) [![Join on Patreon](https://img.shields.io/badge/join-Patreon-F96854?logo=patreon)](https://www.patreon.com/liyasthomas) [![Donate on PayPal](https://img.shields.io/badge/donate-PayPal-00457C?logo=paypal)](https://www.paypal.me/liyascthomas)
<details>
<summary>Table of contents</summary>
<summary><i>Table of contents</i></summary>
---
- [Features](#features-)
- [Demo](#demo--)
- [Usage](#usage-)
- [Built with](#built-with-)
- [Developing](#developing-)
- [Features](#features)
- [Demo](#demo)
- [Usage](#usage)
- [Built with](#built-with)
- [Developing](#developing)
- [Browser based development environment](#browser-based-development-environment)
- [Local development environment](#local-development-environment)
- [Docker compose](#docker-compose)
- [Docker](#docker--)
- [Releasing](#releasing-)
- [Contributing](#contributing-)
- [Continuous Integration](#continuous-integration--)
- [Versioning](#versioning--)
- [Change log](#change-log-)
- [Authors](#authors-)
- [Docker](#docker)
- [Releasing](#releasing)
- [Contributing](#contributing)
- [Continuous Integration](#continuous-integration)
- [Changelog](#changelog)
- [Authors](#authors)
- [Lead Developers](#lead-developers)
- [Testing and Debugging](#testing-and-debugging)
- [Collaborators](#collaborators-)
- [Thanks](#thanks)
- [Financial Contributors](#financial-contributors)
- [Organizations](#organizations)
- [Individuals](#individuals)
- [GitHub Sponsors](#github-sponsors)
- [Open Collective](#open-collective)
- [Code Contributors](#code-contributors)
- [License](#license-)
- [Acknowledgements](#acknowledgements-)
- [Badges](#badges-)
- [License](#license)
---
</details>
### Features
### **Features**
❤️ **Lightweight**: Crafted with minimalistic UI design.
❤️ **Lightweight:** Crafted with minimalistic UI design.
⚡️ **Fast**: Send requests and get/copy responses in real-time.
⚡️ **Fast:** Send requests and get/copy responses in real-time.
**Methods:**
<details>
<summary><i>HTTP Methods</i></summary>
- `GET` - Retrieve information about the REST API resource
---
- `GET` - Requests retrieve resource information
- `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
- `POST` - The server creates a new entry in a database
- `PUT` - Updates an existing resource
- `DELETE` - Deletes 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
- `PATCH` - Very similar to `PUT` but makes a partial update on a resource
- `<custom>` - Some APIs use custom request methods such as `LIST`. Type in your custom methods.
🌈 **Make it yours**: Customizable combinations for background, foreground and accent colors.
---
**Theming:** [Customize now ✨](https://postwoman.io/settings)
</details>
- 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
🌈 **Make it yours:** Customizable combinations for background, foreground and accent colors. [Customize now ✨](https://hoppscotch.io/settings)
<details>
<summary><i>Theming</i></summary>
---
- Choose theme: System, Light, Dark (default) and Black
- Choose accent color: Blue, Green (default), Teal, Indigo, Purple, Orange, Pink, Red, and Yellow
- Toggle auto-scroll to response
<p>
<a href="https://hoppscotch.io"><img src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/static/images/screenshots/banner_light.png" alt="Screenshot" width="100%"></a>
</p>
---
</details>
_Customized themes are synced with local session storage_
🔥 **PWA**: Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
🔥 **PWA:** Install as a [PWA](https://developers.google.com/web/progressive-web-apps) on your device.
**Features:**
<details>
<summary><i>Features</i></summary>
---
- Instant loading with Service Workers
- Offline support
@@ -109,31 +142,44 @@ _Customized themes are synced with local session storage_
- Add to Home Screen
- Desktop PWA
🚀 **Request**: Retrieve response from endpoint instantly.
---
</details>
🚀 **Request:** Retrieve response from endpoint instantly.
- Choose `method`
- Enter `URL`
- Send
**Features:**
<details>
<summary><i>Features</i></summary>
---
- Copy/share public "Share URL"
- Generate/copy request code for `JavaScript XHR`, `Fetch` and `cURL`
- Generate/copy request code snippets for 10+ languages and frameworks
- Import `cURL`
- Label requests
🔌 **WebSocket**: Establish full-duplex communication channels over a single TCP connection.
---
- Send and receive data
- Basic and Bearer Token authentication
</details>
📡 **Server Sent Events**: Receive a stream of updates from a server over a HTTP connection without resorting to polling.
🔌 **WebSocket:** Establish full-duplex communication channels over a single TCP connection.
🌩 **Socket.IO**: Send and Receive data with SocketIO server.
📡 **Server Sent Events:** Receive a stream of updates from a server over a HTTP connection without resorting to polling.
🦟 **MQTT**: Subscribe and Publish to topics of a MQTT Broker.
🌩 **Socket.IO:** Send and Receive data with SocketIO server.
🔮 **GraphQL**: GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.
🦟 **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.
<details>
<summary><i>Features</i></summary>
---
- Set endpoint and get schemas
- Multi-column docs
@@ -141,9 +187,16 @@ _Customized themes are synced with local session storage_
- Query schema
- Get query response
🔐 **Authentication**: Allows to identify the end user.
---
**Types:**
</details>
🔐 **Authentication:** Allows to identify the end user.
<details>
<summary><i>Types</i></summary>
---
- None
- Basic
@@ -151,158 +204,247 @@ _Customized themes are synced with local session storage_
- OAuth 2.0
- OIDC Access Token/PKCE
📢 **Headers**: Describes the format the body of your request is being sent as.
---
📫 **Parameters**: Use request parameters to set varying parts in simulated requests.
</details>
📃 **Request Body**: Used to send and receive data via the REST API.
📢 **Headers:** Describes the format the body of your request is being sent as.
**Options:**
📫 **Parameters:** Use request parameters to set varying parts in simulated requests.
📃 **Request Body:** Used to send and receive data via the REST API.
<details>
<summary><i>Options</i></summary>
---
- 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.
---
</details>
👋 **Response:** Contains the status line, headers and the message/response body.
<details>
<summary><i>Features</i></summary>
---
- Copy response to clipboard
- Download response as a file
- View preview of HTML responses
- View response headers
- View raw and preview of HTML, image, JSON, XML responses
**History**: Request entries are synced with cloud / local session storage to restore with a single click.
---
📁 **Collections**: Keep your API requests organized with collections and folders. Reuse them with a single click.
</details>
**History:** Request entries are synced with cloud / local session storage to restore with a single click.
📁 **Collections:** Keep your API requests organized with collections and folders. Reuse them with a single click.
<details>
<summary><i>Features</i></summary>
---
- Unlimited collections, folders and requests
- Nested folders
- Export as / import from GitHub gist
---
</details>
_Collections are synced with cloud / local session storage_
🌐 **Proxy**: Enable Proxy Mode from Settings to access blocked APIs.
🌐 **Proxy:** Enable Proxy Mode from Settings to access blocked APIs.
**Features:**
<details>
<summary><i>Features</i></summary>
---
- 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.
</details>
**Use-cases:**
_Official proxy server is hosted by Hoppscotch - **[GitHub](https://github.com/hoppscotch/proxyscotch)** - **[Privacy Policy](https://github.com/hoppscotch/proxyscotch/wiki/Privacy-policy)**_
📜 **Pre-Request Scripts β:** Snippets of code associated with a request that are executed before the request is sent.
<details>
<summary><i>Use-cases</i></summary>
---
- Initialize environment variables
- Include timestamp in the request headers
- Send a random alphanumeric string in the URL parameters
📄 **API Documentation**: Create and share dynamic API documentation easily, quickly.
---
**Usage:**
</details>
📄 **API Documentation:** Create and share dynamic API documentation easily, quickly.
<details>
<summary><i>Usage</i></summary>
---
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:**
</details>
- Send/Cancel Request <kbd>Ctrl</kbd> + <kbd>G</kbd>
- Save to Collections <kbd>Ctrl</kbd> + <kbd>S</kbd>
- Copy Request Link <kbd>Ctrl</kbd> + <kbd>K</kbd>
- Reset Request <kbd>Ctrl</kbd> + <kbd>L</kbd>
⌨️ **Keyboard Shortcuts:** Optimized for efficiency.
🌎 **i18n β**: Experience the app in your own language.
> **[Shortcuts WIki](https://github.com/hoppscotch/hoppscotch/wiki/Shortcuts)**
🌎 **i18n:** Experience the app in your own language.
<details>
<summary><i>Usage</i></summary>
---
1. Scroll down to the footer
2. Click "Choose Language" icon button
3. Select your language from the menu
---
</details>
_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!**_
_**All `i18n` contributions are welcome to `i18n` [branch](https://github.com/hoppscotch/hoppscotch/tree/i18n) only!**_
📦 **Add-ons**: Official add-ons for Postwoman.
📦 **Add-ons:** Official add-ons for hoppscotch.
- **[Proxy](https://github.com/postwoman-io/proxywoman)** - A simple proxy server created for Postwoman
- **[CLI β](https://github.com/postwoman-io/postwoman-cli)** - A CLI solution for Postwoman
- **[Browser Extensions](https://github.com/AndrewBastin/postwoman-extension)** - Browser extensions that simplifies access to Postwoman
- **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch
- **[CLI β](https://github.com/hoppscotch/hopp-cli)** - A CLI solution for Hoppscotch
- **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that simplifies access to Hoppscotch
[![Firefox](https://raw.github.com/alrra/browser-logos/master/src/firefox/firefox_16x16.png) **Firefox**](https://addons.mozilla.org/en-US/firefox/addon/postwoman) &nbsp;|&nbsp; [![Chrome](https://raw.github.com/alrra/browser-logos/master/src/chrome/chrome_16x16.png) **Chrome**](https://chrome.google.com/webstore/detail/postwoman-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld)
[![Firefox](https://raw.github.com/alrra/browser-logos/master/src/firefox/firefox_16x16.png) **Firefox**](https://addons.mozilla.org/en-US/firefox/addon/hoppscotch) &nbsp;|&nbsp; [![Chrome](https://raw.github.com/alrra/browser-logos/master/src/chrome/chrome_16x16.png) **Chrome**](https://chrome.google.com/webstore/detail/hoppscotch-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld)
> **Extensions fixes `CORS` issues.**
_Add-ons are developed and maintained under **[Official Postwoman Organization](https://github.com/postwoman-io)**._
- **[Hopp-Doc-Gen](https://github.com/hoppscotch/hopp-doc-gen)** - An API doc generator CLI for Hoppscotch
☁️ **Auth + Sync**: Sign in and sync in real-time.
_Add-ons are developed and maintained under **[Official Hoppscotch Organization](https://github.com/hoppscotch)**._
**Sign in with:**
☁️ **Auth + Sync:** Sign in and sync in real-time.
**Sign in with**
- Google
- GitHub
**Sync:**
**Sync**
- History
- Collections
- Environments
- Notes
**Post-Request Tests β**: Write tests associated with a request that are executed after the request response.
**Post-Request Tests β:** Write tests associated with a request that are executed after the request response.
**Use-cases:**
<details>
<summary><i>Use-cases</i></summary>
---
- Check the status code as an integer
- Filter response headers
- Parse the response data
---
</details>
📝 **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:**
<details>
<summary><i>Features</i></summary>
---
- Unlimited environments and variables
- Initialize through pre-request script
- Export as / import from GitHub gist
---
</details>
<details>
<summary><i>Use-cases</i></summary>
---
- By storing a value in a variable, you can reference it throughout your request section
- If you need to update the value, you only have to change it in one place
- Using variables increases your ability to work efficiently and minimizes the likelihood of error
**To find out more, please check out [Postwoman Wiki](https://github.com/liyasthomas/postwoman/wiki).**
---
## Demo 🚀 [![Website](https://img.shields.io/website?url=https%3A%2F%2Fpostwoman.io&logo=Postwoman)](https://postwoman.io)
</details>
[postwoman.io](https://postwoman.io)
**To find out more, please check out [Hoppscotch Wiki](https://github.com/hoppscotch/hoppscotch/wiki).**
## Usage 💡
## **Demo**
[hoppscotch.io](https://hoppscotch.io)
## **Usage**
1. Choose `method`
2. Enter `URL`
3. Send request
4. Get response
## Built with 🔧
## **Built with**
- HTML - For the web framework
- CSS - For styling components
- JavaScript - For magic!
- [Vue](https://vuejs.org/)
- [Nuxt](https://nuxtjs.org/)
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)
- [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS), [SCSS](https://sass-lang.com), [Tailwind CSS](https://tailwindcss.com)
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
- [Vue](https://vuejs.org)
- [Nuxt](https://nuxtjs.org)
## Developing 👷
## **Developing**
0. Update [`.env.example`](https://github.com/liyasthomas/postwoman/blob/master/.env.example) file found in repository's root directory with your own keys and rename it to `.env`.
0. Update [`.env.example`](https://github.com/hoppscotch/hoppscotch/blob/main/.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)._
_Sample keys only works with the [production build](https://hoppscotch.io)._
#### Browser based development environment
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/liyasthomas/postwoman)
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/hoppscotch/hoppscotch)
#### 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`).
2. Install dependencies by running `npm install` within the directory that you cloned (probably `hoppscotch`).
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.
@@ -312,60 +454,74 @@ _Sample keys only works with the [production build](https://postwoman.io)._
2. Run `docker-compose up`
3. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
## Docker 🐳 [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/liyasthomas/postwoman?logo=Docker)](https://hub.docker.com/r/liyasthomas/postwoman)
## **Docker**
**Official container** &nbsp; [![hoppscotch/hoppscotch](https://img.shields.io/docker/pulls/hoppscotch/hoppscotch?style=social)](https://hub.docker.com/r/hoppscotch/hoppscotch)
```bash
#pull
docker pull hoppscotch/hoppscotch
#build
docker build -t hoppscotch/hoppscotch:latest .
#run
docker run -p 3000:3000 hoppscotch/hoppscotch:latest
```
**Legacy container** &nbsp; [![liyasthomas/postwoman](https://img.shields.io/docker/pulls/liyasthomas/postwoman?style=social)](https://hub.docker.com/r/liyasthomas/postwoman)
<details>
<summary><i>Legacy container</i></summary>
---
```bash
#pull
docker pull liyasthomas/postwoman
#build
docker build -t liyasthomas/postwoman:latest .
#run
docker run -p 3000:3000 liyasthomas/postwoman:latest
#build
docker build -t postwoman:latest .
```
## Releasing 🧞
---
</details>
## **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`.
2. Install dependencies by running `npm install` within the directory that you cloned (probably `hoppscotch`).
3. Build the release files with `npm run generate`.
4. Find the built project in `./dist`.
## Contributing 🍰
## **Contributing**
Please contribute using [GitHub Flow](https://guides.github.com/introduction/flow). Create a branch, add commits, and [open a pull request](https://github.com/hoppscotch/hoppscotch/compare).
Please read [`CONTRIBUTING`](CONTRIBUTING.md) for details on our [`CODE OF CONDUCT`](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
## Continuous Integration 💚 [![Travis Build Status](https://img.shields.io/travis/com/liyasthomas/postwoman?logo=Travis)](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.com/hoppscotch/hoppscotch).
## Versioning 🔖 [![GitHub release](https://img.shields.io/github/release/liyasthomas/postwoman/all?logo=GitHub)](https://github.com/liyasthomas/postwoman/releases/latest)
This project is developed by [Liyas Thomas](https://github.com/liyasthomas) using the [Semantic Versioning specification](https://semver.org). For the versions available, see the [releases on this repository](https://github.com/liyasthomas/postwoman/releases).
## Change log 📝
## **Changelog**
See the [`CHANGELOG`](CHANGELOG.md) file for details.
## Authors 🧙
## **Authors**
### Lead Developers
- **[Liyas Thomas](https://github.com/liyasthomas)** - _Author_
- **[John Harker](https://github.com/NBTX)** - _Lead developer_
- **[Andrew Bastin](https://github.com/andrewbastin)** - _Lead developer_
- **[James George](https://github.com/jamesgeorge007)** - _Lead maintainer_
- **[Caneco](https://twitter.com/caneco)** - _Logo and banner designer_
### Testing and Debugging
- [Contributors](https://github.com/liyasthomas/postwoman/graphs/contributors)
### Collaborators <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-14-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
@@ -373,22 +529,8 @@ See the [`CHANGELOG`](CHANGELOG.md) file for details.
<!-- 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>
<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/hoppscotch/commits?author=liyasthomas" title="Code">💻</a> <a href="#design-liyasthomas" title="Design">🎨</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/hoppscotch/commits?author=AndrewBastin" title="Code">💻</a></td>
</tr>
</table>
@@ -397,140 +539,124 @@ See the [`CHANGELOG`](CHANGELOG.md) file for details.
<!-- ALL-CONTRIBUTORS-LIST:END -->
See the list of [contributors](https://github.com/liyasthomas/postwoman/graphs/contributors) who participated in this project.
### Thanks
- [dev.to 👩‍💻👨‍💻](https://dev.to)
See the list of [contributors](https://github.com/hoppscotch/hoppscotch/graphs/contributors) who participated in this project.
### Financial Contributors
Become a financial contributor and help us sustain our community [[Contribute](https://opencollective.com/postwoman/contribute)].
Become a financial contributor and help us sustain our community [[Support](#support)].
#### Organizations
#### GitHub Sponsors
Support this project with your organization. Your logo will show up here with a link to your website [[Contribute](https://opencollective.com/postwoman/contribute)].
<p align="center">
<a href="https://github.com/eldadfux" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/eldadfux.png?size=64"
alt="Eldad A. Fux"
/>
</a>
<a href="https://github.com/aishwarydhare" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/aishwarydhare.png?size=64"
alt="Aishwary Dhare"
/>
</a>
<a href="https://github.com/rithish" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/rithish.png?size=64"
alt="Rithish"
/>
</a>
<a href="https://github.com/ankumar" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/ankumar.png?size=64"
alt="Anil Kumar"
/>
</a>
<a href="https://github.com/gpeal" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/gpeal.png?size=64"
alt="Gabriel Peal"
/>
</a>
<a href="https://github.com/donokuda" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/donokuda.png?size=64"
alt="Don Okuda"
/>
</a>
<a href="https://github.com/ebrescia" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/ebrescia.png?size=64"
alt="Erica Brescia"
/>
</a>
<a href="http://tom.preston-werner.com" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/mojombo.png?size=64"
alt="Tom Preston-Werner"
/>
</a>
<a href="https://github.com/mlynch" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/mlynch.png?size=64"
alt="Max Lynch"
/>
</a>
<a href="https://github.com/brianshaler" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/brianshaler.png?size=64"
alt="Brian Shaler"
/>
</a>
<a href="https://github.com/mxstbr" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/mxstbr.png?size=64"
alt="Max Stoiber"
/>
</a>
<a href="https://github.com/jjcaine" target="_blank" rel="noopener">
<img
width="64"
src="https://github.com/jjcaine.png?size=64"
alt="John Caine"
/>
</a>
</p>
<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>
#### Open Collective
#### Individuals
<p align="center">
<a href="https://paw.cloud/?utm_source=hoppscotch&utm_medium=github&utm_campaign=hoppscotch-sponsorship" target="_blank" rel="noopener">
<img
width="100"
src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/assets/images/Paw-Logo-for-Hoppscotch.png"
alt="Paw"
/>
</a>
</p>
<a href="https://opencollective.com/postwoman"><img src="https://opencollective.com/postwoman/individuals.svg"></a>
<p align="center">
<a href="https://opencollective.com/hoppscotch/organization/0/website"><img src="https://opencollective.com/hoppscotch/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/hoppscotch/organization/1/website"><img src="https://opencollective.com/hoppscotch/organization/1/avatar.svg"></a>
</p>
### 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>
<a href="https://github.com/hoppscotch/hoppscotch/graphs/contributors"><img src="https://opencollective.com/hoppscotch/contributors.svg?width=890&button=false" /></a>
## License 📄
## **License**
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [`LICENSE`](LICENSE) file for details.
## Acknowledgements 🙏
- Hat tip to anyone whose code was used
- Inspirations:
- [Dribbble](https://dribbble.com)
## Badges 📛
<table>
<tr>
<th>Preview</th>
<th>Markdown code</th>
</tr>
<tbody>
<tr>
<td align="center" width="200px">
<a href="https://postwoman.io">
<br/>
<img src="https://img.shields.io/badge/Tested_on-Postwoman-202124?logo=Postwoman"/>
</a>
<br/>
<sub>
Default
</sub>
</td>
<td>
<code>[![Postwoman](https://img.shields.io/badge/Tested_on-Postwoman-202124?logo=Postwoman)](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>[![Postwoman](https://img.shields.io/badge/Tested_on-Postwoman-success?logo=Postwoman)](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>[![Postwoman](https://img.shields.io/badge/Tested_on-Postwoman-critical?logo=Postwoman)](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>[![Postwoman](https://img.shields.io/badge/Tested_on-Postwoman-blueviolet?logo=Postwoman)](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>[![Postwoman](https://img.shields.io/badge/your_text-Postwoman-hex_color_code?logo=Postwoman)](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>

View File

@@ -4,11 +4,11 @@ Thanks for your interest in helping translating the software!
## Starting a translation
Before you start working on a translation, look through the [open pull requests](https://github.com/liyasthomas/postwoman/pulls) to see if anyone else is already working on one for your language.
Before you start working on a translation, look through the [open pull requests](https://github.com/hoppscotch/hoppscotch/pulls) to see if anyone else is already working on one for your language.
If there's not, then today is your day to lead this effort! Here's how to start:
1. [Fork this repository](https://github.com/liyasthomas/postwoman/fork)
1. [Fork this repository](https://github.com/hoppscotch/hoppscotch/fork)
2. Create a new branch for your translation work e.g. `es`.
3. Copy `lang/en-US.json` to your target language file e.g. `lang/es-ES.json` and translate all the strings.
4. Add your language entry to `nuxt.config.js`.
@@ -28,7 +28,7 @@ If there's not, then today is your day to lead this effort! Here's how to start:
code: 'es',
name: 'Español',
iso: 'es-ES',
file: 'es-ES.js'
file: 'es-ES.json'
}
]
}
@@ -37,7 +37,7 @@ If there's not, then today is your day to lead this effort! Here's how to start:
5. Save & commit changes.
6. Send a pull request. (You may send a pull request before all steps above are complete: e.g., you may want to ask for help with translations, or getting tests to pass. However your pull request will not be merged until all steps above are complete.)
Completing an initial translation of the whole site is a fairly large task. One way to break that task up is to work with other translators through pull requests on your fork. You can also [add collaborators to your fork](https://help.github.com/en/github/setting-up-and-managing-your-github-user-account/inviting-collaborators-to-a-personal-repository) if you'd like to inivte other translators to commit directly to your fork and share responsibility for merging pull requests.
Completing an initial translation of the whole site is a fairly large task. One way to break that task up is to work with other translators through pull requests on your fork. You can also [add collaborators to your fork](https://help.github.com/en/github/setting-up-and-managing-your-github-user-account/inviting-collaborators-to-a-personal-repository) if you'd like to invite other translators to commit directly to your fork and share responsibility for merging pull requests.
## Updating a translation

1
__mocks__/svgMock.js Normal file
View File

@@ -0,0 +1 @@
export default {}

View File

@@ -1,7 +1,5 @@
# 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).

213
assets/css/fonts.css Normal file
View File

@@ -0,0 +1,213 @@
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Material_Icons-400-fallback1.woff2') format('woff2');
}
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Poppins-400-devanagari2.woff2') format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Poppins-400-latin-ext3.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Poppins-400-latin4.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('~assets/fonts/Poppins-500-devanagari5.woff2') format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('~assets/fonts/Poppins-500-latin-ext6.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('~assets/fonts/Poppins-500-latin7.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('~assets/fonts/Poppins-600-devanagari8.woff2') format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('~assets/fonts/Poppins-600-latin-ext9.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('~assets/fonts/Poppins-600-latin10.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('~assets/fonts/Poppins-700-devanagari11.woff2') format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('~assets/fonts/Poppins-700-latin-ext12.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('~assets/fonts/Poppins-700-latin13.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url('~assets/fonts/Poppins-800-devanagari14.woff2') format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url('~assets/fonts/Poppins-800-latin-ext15.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url('~assets/fonts/Poppins-800-latin16.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Roboto_Mono-400-cyrillic-ext17.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Roboto_Mono-400-cyrillic18.woff2') format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Roboto_Mono-400-greek19.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Roboto_Mono-400-vietnamese20.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Roboto_Mono-400-latin-ext21.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('~assets/fonts/Roboto_Mono-400-latin22.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
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;
}

View File

@@ -1,66 +0,0 @@
// @import url("https://fonts.googleapis.com/css?family=Poppins:500,700|Roboto+Mono:400&display=swap");
// @import url("https://fonts.googleapis.com/icon?family=Material+Icons&display=swap");
/* Material Design Icons */
@font-face {
font-family: "Material Icons";
font-style: normal;
font-weight: 400;
// Do not use font-display: swap for the icon font - it looks really bad when the page
// loads.
font-display: block;
src: url("~static/fonts/material-icons-v52.woff2") format("woff2");
}
.material-icons {
font-family: "Material Icons";
font-weight: normal;
font-style: normal;
height: 24px;
width: 24px;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: "liga";
-webkit-font-smoothing: antialiased;
font-feature-settings: "liga";
border-radius: 50%;
}
/* poppins-500 - latin */
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 500;
font-display: swap;
src: local("Poppins Medium"), local("Poppins-Medium"),
url("~static/fonts/poppins-v9-latin-500.woff2") format("woff2"),
url("~static/fonts/poppins-v9-latin-500.woff") format("woff");
}
/* poppins-700 - latin */
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 700;
font-display: swap;
src: local("Poppins Bold"), local("Poppins-Bold"),
url("~static/fonts/poppins-v9-latin-700.woff2") format("woff2"),
url("~static/fonts/poppins-v9-latin-700.woff") format("woff");
}
/* roboto-mono-regular - latin */
@font-face {
font-family: "Roboto Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: local("Roboto Mono"), local("RobotoMono-Regular"),
url("~static/fonts/roboto-mono-v7-latin-regular.woff2") format("woff2"),
url("~static/fonts/roboto-mono-v7-latin-regular.woff") format("woff");
}

View File

@@ -1,784 +0,0 @@
$responsiveWidth: 768px;
::selection {
background-color: var(--ac-color);
color: var(--act-color);
}
::-webkit-scrollbar {
width: 4px;
height: 4px;
border-radius: 4px;
background-color: var(--bg-dark-color);
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: var(--fg-light-color);
&:hover {
background-color: var(--fg-color);
}
}
::placeholder {
color: var(--fg-light-color);
opacity: 0.3;
}
* {
box-sizing: border-box;
outline: 0;
border: 0;
}
html,
body {
background-color: var(--bg-color);
color: var(--fg-color);
font-weight: 500;
font-size: 14px;
font-family: "Poppins", "Roboto", "Noto", sans-serif;
line-height: 1.5;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
user-select: none;
padding: 0;
margin: 0;
scroll-behavior: smooth;
transition: all 0.2s ease-in-out;
}
body.afterLoad {
transition: background-color 0.2s ease-in-out;
}
body.sticky-footer footer {
opacity: 0.25;
}
a {
display: inline-flex;
color: inherit;
text-decoration: none;
transition: all 0.2s ease-in-out;
&.link {
color: var(--ac-color);
}
}
header,
footer {
& > div {
display: flex;
padding: 16px 8px;
width: 100%;
align-items: center;
justify-content: space-between;
}
}
.page-enter-active,
.page-leave-active,
.layout-enter-active,
.layout-leave-active {
transition: opacity 0.2s;
}
.page-enter,
.page-leave-active,
.layout-enter,
.layout-leave-active {
opacity: 0;
}
.wrapper {
min-height: 100vh;
display: flex;
flex-flow: column nowrap;
}
.wrapper .page {
min-height: calc(100vh - 153px);
}
.header,
.content,
.columns,
.footer {
display: flex;
flex: 1;
}
.nav-first,
.sticky-inner {
display: flex;
order: 1;
flex-flow: column;
position: sticky;
top: 0;
align-self: flex-start;
}
.main {
display: flex;
flex-flow: column;
flex: 1;
order: 2;
position: relative;
padding: 0 16px;
}
h1,
h2,
h3,
h4 {
display: flex;
align-items: center;
margin: 0;
font-weight: 700;
}
h3.title {
margin: 4px;
}
p {
transition: all 0.2s ease-in-out;
}
hr {
border-bottom: 1px dashed var(--brd-color);
margin: 16px 0;
}
.tooltip {
$bgcolor: var(--tt-color);
$fgcolor: var(--fg-color);
display: block !important;
z-index: 10000;
transition: all 0.2s ease-in-out;
.tooltip-inner {
background: $bgcolor;
color: $fgcolor;
border-radius: 8px;
padding: 8px 16px;
font-size: 12px;
font-weight: 500;
box-shadow: 0 4px 24px rgba(black, 0.1);
}
.tooltip-arrow {
width: 0;
height: 0;
border-style: solid;
position: absolute;
margin: 5px;
border-color: $bgcolor;
z-index: 1;
}
&[x-placement^="top"] {
margin-bottom: 5px;
.tooltip-arrow {
border-width: 5px 5px 0 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
bottom: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="bottom"] {
margin-top: 5px;
.tooltip-arrow {
border-width: 0 5px 5px 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-top-color: transparent !important;
top: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="right"] {
margin-left: 5px;
.tooltip-arrow {
border-width: 5px 5px 5px 0;
border-left-color: transparent !important;
border-top-color: transparent !important;
border-bottom-color: transparent !important;
left: -5px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&[x-placement^="left"] {
margin-right: 5px;
.tooltip-arrow {
border-width: 5px 0 5px 5px;
border-top-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
right: -5px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&.popover {
.wrapper {
min-height: auto;
}
.popover-inner {
background: $bgcolor;
color: $fgcolor;
padding: 4px;
border-radius: 8px;
box-shadow: 0 5px 30px rgba(black, 0.1);
max-height: 256px;
overflow: auto;
div {
display: flex;
align-items: stretch;
flex-direction: column;
}
button {
justify-content: start;
}
}
.popover-arrow {
border-color: $bgcolor;
}
}
&[aria-hidden="true"] {
visibility: hidden;
opacity: 0;
transition: opacity 0.15s, visibility 0.15s;
}
&[aria-hidden="false"] {
visibility: visible;
opacity: 1;
transition: opacity 0.15s;
}
}
.info:not(.toasted) {
margin-left: 4px;
color: var(--fg-light-color);
.material-icons {
vertical-align: middle;
margin-right: 8px;
}
}
.bg-color {
background-color: transparent;
}
button {
display: inline-flex;
align-items: center;
justify-content: center;
margin: 4px;
padding: 6px 16px;
border-radius: 8px;
background-color: var(--ac-color);
color: var(--act-color);
font-size: 16px;
font-family: "Poppins", "Roboto", "Noto", sans-serif;
font-weight: 700;
transition: all 0.2s ease-in-out;
fill: var(--act-color);
cursor: pointer;
span {
display: inline-flex;
margin-left: 8px;
text-align: left;
}
&:not([disabled]):hover,
&:not([disabled]):active,
&:not([disabled]):focus {
color: var(--act-color);
fill: var(--act-color);
box-shadow: inset 0 0 0 2px var(--fg-color);
}
&.icon {
background-color: transparent;
color: var(--fg-light-color);
fill: var(--fg-light-color);
border-radius: 8px;
&:not([disabled]):hover,
&:not([disabled]):active,
&:not([disabled]):focus {
color: var(--fg-color);
fill: var(--fg-color);
box-shadow: none;
}
}
&.primary {
color: var(--ac-color);
&:not([disabled]):hover,
&:not([disabled]):active,
&:not([disabled]):focus {
background-color: var(--ac-color);
color: var(--act-color);
}
}
}
@keyframes beat {
30% {
transform: scale(1.1);
}
50% {
transform: scale(0.9);
}
100% {
transform: scale(1);
}
}
.material-icons:active {
animation: beat 0.5s forwards 1;
}
fieldset:target,
section:target {
animation: highlight 2s ease;
}
@keyframes highlight {
50% {
box-shadow: 0 0 0 2px var(--ac-color);
}
}
input[type="file"],
input[type="radio"],
.hide-on-large-screen,
#installPWA,
.hidden {
display: none;
}
.method,
kbd,
select,
input,
textarea,
pre,
code {
display: inline-flex;
margin: 4px;
padding: 8px;
border-radius: 8px;
background-color: var(--bg-dark-color);
color: var(--fg-color);
font-size: 16px;
font-family: "Roboto Mono", monospace;
font-weight: 400;
line-height: 1;
transition: all 0.2s ease-in-out;
user-select: text;
width: calc(100% - 8px);
resize: vertical;
text-overflow: ellipsis;
&:not([readonly]):not(.ace_editor):hover,
&:not([readonly]):not(.ace_editor):active,
&:not([readonly]):not(.ace_editor):focus {
box-shadow: inset 0 0 0 2px var(--fg-light-color);
}
}
.method {
cursor: pointer;
&:hover,
&:active,
&:focus {
box-shadow: inset 0 0 0 2px var(--fg-light-color);
}
}
pre {
display: grid;
}
pre.ace_editor {
font-family: "Roboto Mono", monospace;
font-weight: 400;
z-index: 0;
}
kbd,
code,
pre {
width: auto;
}
.select-wrapper {
position: relative;
input {
text-transform: uppercase;
min-width: 128px;
}
.trigger {
width: 100%;
}
&:after {
display: inline-block;
position: absolute;
pointer-events: none;
content: "\e313";
font-family: "Material Icons";
top: 14px;
right: 14px;
}
}
select {
height: 37px;
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
&::-ms-expand {
display: none;
}
}
option {
background-color: var(--bg-color);
}
input[type="checkbox"] {
display: none;
&,
& + label {
vertical-align: middle;
cursor: pointer;
&:before {
content: "\2714";
border: 1px solid var(--fg-color);
border-radius: 8px;
display: inline-flex;
height: 16px;
width: 16px;
align-items: center;
justify-content: center;
margin: 8px 8px 8px 0;
color: transparent;
transition: all 0.2s ease-in-out;
}
}
&:checked + label:before {
background-color: var(--ac-color);
border-color: var(--ac-color);
color: var(--act-color);
}
}
.error:not(input),
.disabled:not(input),
[disabled] {
background-color: var(--err-color);
color: var(--fg-light-color);
fill: var(--fg-light-color);
cursor: not-allowed;
&.icon {
color: var(--err-color);
fill: var(--err-color);
}
}
label {
padding: 4px;
color: var(--fg-light-color);
transition: all 0.2s ease-in-out;
}
ul,
ol {
display: flex;
margin: 4px 0 4px;
padding: 0;
list-style-type: none;
ul,
ol {
margin: 0;
}
}
ul li,
ol li {
display: inline-flex;
flex-flow: column nowrap;
flex: 1;
justify-content: center;
&.shrink {
flex-grow: 0;
}
}
.flex-wrap {
display: flex;
align-items: center;
justify-content: space-between;
flex-grow: 1;
flex-direction: row;
* {
display: inline-flex;
flex-flow: row nowrap;
align-items: center;
justify-content: center;
}
}
.show-on-small-screen {
display: flex;
}
.show-on-large-screen {
display: flex;
flex: 1;
}
.info-response {
color: #ffeb3b;
}
.success-response {
color: #4bb543;
}
.redir-response {
color: #ff5722;
}
.cl-error-response {
color: #a63232;
}
.sv-error-response {
color: #b71c1c;
}
.missing-data-response {
background-color: var(--err-color);
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.mono {
font-family: "Roboto Mono", monospace;
font-weight: 400;
}
#response-details-wrapper {
position: relative;
overflow: hidden;
border-radius: 8px;
textarea {
margin: 0;
width: 100%;
line-height: 1;
}
.covers-response {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: white;
height: 100%;
width: 100%;
}
}
#send {
&.show {
display: flex;
position: fixed;
bottom: 86px;
left: 50%;
z-index: 10001;
transform: translateX(-50%);
box-shadow: 0 4px 24px rgba(black, 0.2);
transition: all 0.2s ease-in-out;
}
}
section {
display: flex;
flex-wrap: wrap;
border-radius: 8px;
}
.toasted-container .toasted {
justify-content: flex-start !important;
}
.toasted.info {
background-color: var(--ac-color) !important;
color: var(--act-color) !important;
font-weight: 700 !important;
}
.toasted.bubble .action {
color: inherit !important;
}
.toasted .action {
margin-left: auto !important;
}
.page-columns {
display: flex;
flex: 1;
flex-flow: column;
}
.inner-left {
display: flex;
order: 1;
}
.inner-right {
display: flex;
width: 30%;
order: 2;
margin-left: 16px;
}
@media (max-width: $responsiveWidth) {
.content,
.columns {
flex-flow: column;
}
.main {
padding: 0 8px 68px;
}
ul,
ol {
flex-flow: column nowrap;
}
ul li,
ol li {
display: flex;
}
.hide-on-small-screen {
display: none;
}
.hide-on-large-screen,
.show-on-small-screen {
display: inline-flex;
}
.sticky-inner {
position: relative;
width: 100%;
}
.inner-left {
order: 0;
}
.inner-right {
margin-left: 0;
}
.toasted-container {
margin-bottom: 68px;
}
}
.toasted-ad {
background-color: #fefefe;
color: #121212;
padding: 16px !important;
font-weight: 700;
font-size: 16px;
border-radius: 8px;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
.action {
text-transform: none !important;
background-color: #121212;
color: #fefefe;
padding: 12px 16px !important;
font-weight: 500 !important;
font-size: 16px !important;
border-radius: 8px;
margin: 0 !important;
margin-left: 8px !important;
}
}
.virtual-list {
overflow: auto;
}
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
overflow-wrap: break-word;
word-break: break-all;
}

5
assets/css/tailwind.css Normal file
View File

@@ -0,0 +1,5 @@
/* purgecss start ignore */
@tailwind base;
@tailwind components;
/* purgecss end ignore */
@tailwind utilities;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 3.5 MiB

View File

@@ -0,0 +1,8 @@
<svg width="198" height="172" xmlns="http://www.w3.org/2000/svg">
<path fill="none" d="M-1-1h200v174H-1z"/>
<g>
<path fill="#f02e65" d="M197.289 88c0 35.899-29.101 65-65 65-16.675 0-31.883-6.279-43.389-16.601 18.433-8.298 31.266-26.827 31.266-48.353 0-21.553-12.865-40.101-31.335-48.384C100.346 29.304 115.581 23 132.289 23c35.899 0 65 29.101 65 65z"/>
<path fill="#f02e65" d="M106.306 36.116a65 65 0 10-78.307 103.769 65 65 0 1078.307-103.77v.001zm-7.229 9.578a53 53 0 01-63.85 84.612 53 53 0 0163.85-84.612z"/>
<path fill="#f02e65" d="M68.894 65.511c-.078.192-1.072 4.095-2.142 8.725-1.111 4.63-2.871 11.938-3.864 16.262-1.915 7.92-3.063 13.124-3.063 13.813 0 .19 1.187.345 2.64.345h2.641l1.185-5.282c.69-2.869 2.221-9.451 3.445-14.616 1.225-5.166 2.716-11.441 3.291-13.967.573-2.526 1.147-4.82 1.263-5.088.115-.344-.537-.458-2.526-.458-1.493 0-2.795.114-2.87.266zM48.345 82.079l-3.52 3.827 1.034 1.224c.572.688 2.143 2.411 3.482 3.827l2.449 2.601h6.964l-3.29-3.559c-1.8-1.911-3.292-3.749-3.292-3.978 0-.268 1.378-1.989 3.062-3.826 1.683-1.874 3.061-3.483 3.061-3.674 0-.153-1.454-.268-3.214-.268h-3.176l-3.56 3.826zm26.785-3.597c0 .116.651.842 1.453 1.646 2.987 2.984 5.091 5.511 4.976 6.007-.076.269-1.531 2.066-3.29 3.941l-3.175 3.482h3.557l3.559-.038 3.251-3.558c1.8-1.988 3.253-3.751 3.253-3.98 0-.19-1.53-1.989-3.444-4.016l-3.442-3.713h-3.331c-1.875 0-3.367.115-3.367.229z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 3.6 MiB

29
assets/js/regex.worker.js Normal file
View File

@@ -0,0 +1,29 @@
function generateREForProtocol(protocol) {
return [
new RegExp(
`${protocol}(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`
),
new RegExp(
`${protocol}(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9/])$`
),
]
}
const ws = generateREForProtocol("^(wss?:\\/\\/)?")
const sse = generateREForProtocol("^(https?:\\/\\/)?")
const socketio = generateREForProtocol("^((wss?:\\/\\/)|(https?:\\/\\/))?")
const regex = { ws, sse, socketio }
// type = ws/sse/socketio
async function validator(type, url) {
console.time(`validator ${url}`)
const [res1, res2] = await Promise.all([regex[type][0].test(url), regex[type][1].test(url)])
console.timeEnd(`validator ${url}`)
return res1 || res2
}
onmessage = async ({ data }) => {
const { type, url } = data
const result = await validator(type, url)
postMessage({ type, url, result })
}

221
assets/md/docs.md Normal file
View File

@@ -0,0 +1,221 @@
{{#collections}}
# {{name}}
## {{#folders}}
## Folder: {{name}}
{{#requests}}
### {{name}}
**Method**: {{method}}
**Request URL**: `{{{url}}}{{{path}}}`
{{#isHeaders}}
**Headers**:
<table>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
{{#headers}}
<tr>
<td>{{{key}}}</td>
<td>`{{{value}}}`</td>
</tr>
{{/headers}}
</table>
{{/isHeaders}}
{{#isParams}}
**Parameters**:
<table>
<tr>
<th>type</th>
<th>Key</th>
<th>Value</th>
</tr>
{{#params}}
<tr>
<td>{{type}}</td>
<td>{{{key}}}</td>
<td>{{{value}}}</td>
</tr>
{{/params}}
</table>
{{/isParams}}
{{#isAuth}}
**Authentication Type**: {{{auth}}}
{{/isAuth}}
{{#bearerToken}}
**Bearer Token**: `{{{.}}}`
{{/bearerToken}}
{{#isAuthBasic}}
Username: `{{{httpUser}}}`
Password: `{{{httpPassword}}}`
{{/isAuthBasic}}
{{#isRawParams}}
**RawParams**:
```json
{{{rawParams}}}
```
{{/isRawParams}}
{{#contentType}}
**ContentType**: `{{{contentType}}}`
{{/contentType}}
{{#preRequestScript}}
**Pre Request Script**:
```js
{
{
{
preRequestScript
}
}
}
```
{{/preRequestScript}}
{{#testScript}}
**Test Script**:
```js
{
{
{
testScript
}
}
}
```
{{/testScript}}
{{/requests}}
---
{{/folders}}
{{#requests}}
## {{name}}
**Method**: {{method}}
**Request URL**: `{{{url}}}{{{path}}}`
{{#isHeaders}}
**Headers**:
<table>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
{{#headers}}
<tr>
<td>{{{key}}}</td>
<td>`{{{value}}}`</td>
</tr>
{{/headers}}
</table>
{{/isHeaders}}
{{#isParams}}
**Parameters**:
<table>
<tr>
<th>type</th>
<th>Key</th>
<th>Value</th>
</tr>
{{#params}}
<tr>
<td>{{type}}</td>
<td>{{{key}}}</td>
<td>{{{value}}}</td>
</tr>
{{/params}}
</table>
{{/isParams}}
{{#isAuth}}
**Authentication Type**: {{{auth}}}
{{/isAuth}}
{{#bearerToken}}
**Bearer Token**: `{{{.}}}`
{{/bearerToken}}
{{#isAuthBasic}}
Username: `{{{httpUser}}}`
Password: `{{{httpPassword}}}`
{{/isAuthBasic}}
{{#isRawParams}}
**Raw Parameters**:
```json
{{{rawParams}}}
```
{{/isRawParams}}
{{#contentType}}
**Content Type**: `{{{contentType}}}`
{{/contentType}}
{{#preRequestScript}}
**Pre Request Script**:
```js
{
{
{
preRequestScript
}
}
}
```
{{/preRequestScript}}
{{#testScript}}
**Test Script**:
```js
{
{
{
testScript
}
}
}
```
{{/testScript}}
{{/requests}}
{{/collections}}
---
Made with [Hoppscotch](https://github.com/hoppscotch/hoppscotch)

813
assets/scss/styles.scss Normal file
View File

@@ -0,0 +1,813 @@
$responsiveWidth: 768px;
*,
*::before,
*::after {
backface-visibility: hidden;
}
:root {
@apply antialiased;
font-variant-ligatures: common-ligatures;
}
::selection {
@apply bg-acColor;
@apply text-actColor;
}
::-webkit-scrollbar {
@apply h-1;
@apply w-2;
&:hover {
@apply bg-bgDarkColor;
}
}
::-webkit-scrollbar-thumb {
@apply bg-fgLightColor;
&:hover {
@apply bg-fgColor;
}
}
::placeholder {
@apply text-fgLightColor;
@apply opacity-25;
}
html {
scroll-behavior: smooth;
}
body {
@apply bg-bgColor;
@apply text-fgColor;
@apply text-base;
@apply font-medium;
@apply select-none;
@apply transition;
@apply ease-in-out;
@apply duration-150;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
}
body.afterLoad {
@apply transition-colors;
@apply ease-in-out;
@apply duration-150;
}
body.sticky-footer footer {
@apply opacity-25;
}
.page-enter-active,
.page-leave-active,
.layout-enter-active,
.layout-leave-active {
@apply transition-opacity;
@apply duration-150;
}
.page-enter,
.page-leave-active,
.layout-enter,
.layout-leave-active {
@apply opacity-0;
}
a {
@apply inline-flex;
@apply text-current;
@apply no-underline;
@apply outline-none;
&.link {
@apply text-acColor;
}
}
header,
footer {
& > div {
@apply flex;
@apply py-2;
@apply px-2;
@apply w-full;
@apply items-center;
@apply justify-between;
}
}
.wrapper {
@apply min-h-screen;
@apply flex;
@apply flex-col;
@apply flex-nowrap;
}
.wrapper .page {
min-height: calc(100vh - 153px);
}
.header,
.content,
.columns,
.footer {
@apply flex;
@apply flex-1;
}
.nav-first,
.sticky-inner {
@apply flex;
@apply order-1;
@apply flex-col;
@apply sticky;
@apply top-0;
@apply items-start;
@apply items-stretch;
@apply h-full;
}
main {
@apply flex;
@apply flex-col;
@apply flex-1;
@apply order-2;
@apply relative;
@apply px-4;
}
h1,
h2,
h3,
h4 {
@apply flex;
@apply items-center;
@apply m-0;
@apply font-bold;
}
h3.title {
@apply m-2;
}
p {
@apply text-sm;
@apply transition;
@apply ease-in-out;
@apply duration-150;
}
hr {
@apply border-b;
@apply border-dashed;
@apply border-brdColor;
@apply my-4;
}
.tooltip {
@apply z-50;
@apply outline-none;
.tooltip-inner {
@apply rounded-lg;
@apply px-4;
@apply py-2;
@apply text-xs;
@apply font-medium;
@apply shadow-lg;
@apply transition;
@apply ease-in-out;
@apply duration-150;
@apply bg-ttColor;
@apply text-fgColor;
}
.tooltip-arrow {
@apply h-0;
@apply w-0;
@apply border-solid;
@apply absolute;
@apply m-2;
@apply border-ttColor;
@apply z-30;
@apply transition;
@apply ease-in-out;
@apply duration-150;
}
&[x-placement^="top"] {
@apply mb-0;
.tooltip-arrow {
@apply mt-0;
@apply mb-0;
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);
}
}
&[x-placement^="bottom"] {
@apply mt-0;
.tooltip-arrow {
@apply mt-0;
@apply mb-0;
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);
}
}
&[x-placement^="right"] {
@apply ml-0;
.tooltip-arrow {
@apply ml-0;
@apply mr-0;
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);
}
}
&[x-placement^="left"] {
@apply mr-0;
.tooltip-arrow {
@apply ml-0;
@apply mr-0;
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);
}
}
&.popover {
.wrapper {
min-height: auto;
}
.popover-inner {
@apply bg-ttColor;
@apply text-fgColor;
@apply text-base;
@apply p-2;
@apply rounded-lg;
@apply overflow-auto;
@apply shadow-lg;
max-height: 256px;
min-width: 128px;
button {
@apply flex-1;
@apply m-0;
@apply p-2;
@apply justify-start;
@apply text-left;
}
div {
@apply flex;
@apply items-stretch;
@apply flex-col;
}
}
.popover-arrow {
@apply border-ttColor;
}
}
&[aria-hidden="true"] {
@apply invisible;
@apply opacity-0;
@apply transition-opacity;
@apply ease-in-out;
@apply duration-150;
}
&[aria-hidden="false"] {
@apply visible;
@apply opacity-100;
@apply transition-opacity;
@apply ease-in-out;
@apply duration-150;
}
}
.info:not(.toasted) {
@apply m-4;
@apply text-fgLightColor;
.material-icons {
@apply align-middle;
@apply mr-2;
}
}
button {
@apply inline-flex;
@apply items-center;
@apply justify-center;
@apply p-4;
@apply bg-acColor;
@apply text-actColor;
@apply font-bold;
@apply transition;
@apply ease-in-out;
@apply duration-150;
@apply fill-current;
@apply cursor-pointer;
@apply outline-none;
@apply border-none;
span {
@apply inline-flex;
@apply ml-4;
@apply text-left;
}
&:not([disabled]):hover,
&:not([disabled]):active,
&:not([disabled]):focus {
@apply text-actColor;
@apply fill-current;
@apply outline-none;
box-shadow: inset 0 0 0 2px var(--fg-color);
}
&.icon {
@apply bg-transparent;
@apply text-fgLightColor;
@apply fill-current;
@apply outline-none;
@apply border-none;
@apply rounded-lg;
&:not([disabled]):hover,
&:not([disabled]):active,
&:not([disabled]):focus {
@apply text-fgColor;
@apply fill-current;
@apply shadow-none;
}
}
&.primary {
@apply text-acColor;
@apply px-6;
&:not([disabled]):hover,
&:not([disabled]):active,
&:not([disabled]):focus {
@apply bg-acColor;
@apply text-actColor;
}
}
}
@keyframes beat {
30% {
@apply transform;
@apply scale-90;
}
50% {
@apply transform;
@apply scale-110;
}
100% {
@apply transform;
@apply scale-100;
}
}
.material-icons {
&:active {
animation: beat 0.5s forwards 1;
}
}
fieldset:target,
section:target {
animation: highlight 2s ease;
}
@keyframes highlight {
50% {
box-shadow: 0 0 0 2px var(--ac-color);
}
}
input {
@apply truncate;
}
input[type="file"],
input[type="radio"],
#installPWA {
@apply hidden;
}
.show-on-large-screen {
@apply flex;
@apply flex-1;
}
.method,
.url-field,
kbd,
select,
input,
textarea,
pre,
code {
@apply flex;
@apply p-4;
@apply bg-bgDarkColor;
@apply text-fgColor;
@apply font-mono;
@apply font-normal;
@apply transition;
@apply ease-in-out;
@apply duration-150;
@apply select-text;
@apply resize-y;
@apply outline-none;
@apply w-full;
&: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 {
@apply cursor-pointer;
@apply uppercase;
@apply rounded-none;
min-width: 128px;
&:hover,
&:active,
&:focus {
box-shadow: inset 0 0 0 2px var(--fg-light-color);
}
}
pre {
@apply grid;
}
pre.ace_editor {
@apply font-mono;
@apply font-normal;
@apply z-0;
@apply resize-none;
}
kbd,
code,
pre {
@apply w-auto;
}
.select-wrapper {
@apply relative;
@apply w-full;
pre,
input {
@apply cursor-pointer;
}
.trigger {
@apply w-full;
}
&::after {
@apply inline-block;
@apply absolute;
@apply pointer-events-none;
@apply font-icon;
@apply text-fgLightColor;
content: "\e313";
top: 16px;
right: 16px;
}
}
select {
@apply cursor-pointer;
@apply appearance-none;
// height: 40px;
&::-ms-expand {
@apply hidden;
}
}
option {
@apply bg-bgColor;
}
input[type="checkbox"] {
@apply hidden;
&,
& + label {
@apply align-middle;
@apply cursor-pointer;
&::before {
@apply border;
@apply border-fgColor;
@apply rounded-lg;
@apply inline-flex;
@apply items-center;
@apply justify-center;
@apply text-transparent;
@apply transition;
@apply ease-in-out;
@apply duration-150;
content: "\2714";
height: 16px;
width: 16px;
margin: 8px 8px 8px 0;
}
}
&:checked + label::before {
@apply bg-acColor;
@apply border-acColor;
@apply text-actColor;
}
}
.error:not(input),
.disabled:not(input),
[disabled] {
@apply bg-errColor;
@apply text-fgLightColor;
@apply fill-current;
@apply cursor-not-allowed;
&.icon {
@apply text-errColor;
@apply fill-current;
}
}
label {
@apply p-4;
@apply text-fgLightColor;
@apply text-sm;
@apply transition;
@apply ease-in-out;
@apply duration-150;
}
ul,
ol {
@apply flex;
}
ul li,
ol li {
@apply inline-flex;
@apply flex-col;
@apply flex-nowrap;
@apply flex-1;
@apply justify-center;
&.shrink {
@apply flex-grow-0;
}
}
.row-wrapper {
@apply flex;
@apply items-center;
@apply justify-between;
@apply flex-1;
@apply flex-row;
span,
div {
@apply inline-flex;
@apply flex-nowrap;
@apply items-center;
@apply justify-center;
}
}
.info-response {
@apply text-yellow-400;
}
.success-response {
@apply text-green-400;
}
.redir-response {
@apply text-pink-400;
}
.cl-error-response {
@apply text-red-400;
}
.sv-error-response {
@apply text-red-600;
}
.missing-data-response {
@apply text-fgLightColor;
}
#response-details-wrapper {
@apply relative;
textarea {
@apply m-0;
@apply w-full;
line-height: 1;
}
.covers-response {
@apply absolute;
@apply inset-0;
@apply bg-white;
@apply h-full;
@apply w-full;
}
}
#send {
@apply whitespace-nowrap;
@apply outline-none;
@apply border-none;
&.show {
@apply flex;
@apply fixed;
@apply transition;
@apply ease-in-out;
@apply duration-150;
@apply shadow-lg;
@apply rounded-lg;
bottom: 86px;
left: 50%;
z-index: 10001;
transform: translateX(-50%);
}
}
section {
@apply flex;
@apply rounded-lg;
@apply w-full;
}
.toasted-container .toasted {
justify-content: space-between !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 {
@apply flex;
@apply flex-1;
@apply flex-col;
}
.inner-left {
@apply flex;
@apply order-1;
}
.inner-right {
@apply flex;
@apply order-2;
@apply ml-4;
width: 33%;
}
@media (max-width: $responsiveWidth) {
.content,
.columns {
@apply flex-col;
}
main {
padding: 0 8px 68px;
margin-bottom: env(safe-area-inset-bottom);
}
ul,
ol {
@apply flex-col;
@apply flex-nowrap;
}
ul li,
ol li {
@apply flex;
}
.hide-on-small-screen {
@apply hidden;
}
.sticky-inner {
@apply relative;
@apply w-full;
}
.inner-left {
order: 0;
}
.inner-right {
@apply ml-0;
}
.toasted-container {
margin-bottom: 68px;
}
}
.toasted-ad {
@apply bg-gray-50;
@apply text-gray-900;
@apply font-bold;
@apply text-sm;
@apply rounded-lg;
@apply shadow-lg;
padding: 16px !important;
.action {
@apply bg-gray-50;
@apply text-gray-900;
@apply rounded-lg;
@apply font-bold;
text-transform: none !important;
padding: 12px 16px !important;
font-size: 16px !important;
margin: 0 !important;
margin-left: 8px !important;
}
}
.virtual-list {
@apply overflow-auto;
}

View File

@@ -1,12 +1,9 @@
/**
Main Themes:
- dark (default)
- light
- black
- auto
*/
@mixin baseTheme {
--font-sans: "Poppins", "sans-serif";
--font-mono: "Roboto Mono", "monospace";
--font-icon: "Material Icons";
}
// Dark is the default theme variant.
@mixin darkTheme {
// Background color
--bg-color: rgba(32, 33, 36, 1);
@@ -22,14 +19,12 @@
--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);
// Tooltip color
--tt-color: rgba(48, 48, 48, 1);
// Editor theme
--editor-theme: "twilight";
// 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 {
@@ -47,14 +42,12 @@
--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);
// Tooltip color
--tt-color: rgba(255, 255, 255, 1);
// Editor theme
--editor-theme: "iplastic";
// 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 {
@@ -72,36 +65,101 @@
--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);
// Tooltip color
--tt-color: rgba(32, 32, 32, 1);
// Editor theme
--editor-theme: "vibrant_ink";
// 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);
}
@mixin blueTheme {
// Acent color
--ac-color: theme("colors.blue.400");
}
@mixin greenTheme {
// Acent color
--ac-color: theme("colors.green.400");
}
@mixin tealTheme {
// Acent color
--ac-color: theme("colors.teal.400");
}
@mixin indigoTheme {
// Acent color
--ac-color: theme("colors.indigo.400");
}
@mixin purpleTheme {
// Acent color
--ac-color: theme("colors.purple.400");
}
@mixin orangeTheme {
// Acent color
--ac-color: theme("colors.orange.400");
}
@mixin pinkTheme {
// Acent color
--ac-color: theme("colors.pink.400");
}
@mixin redTheme {
// Acent color
--ac-color: theme("colors.red.400");
}
@mixin yellowTheme {
// Acent color
--ac-color: theme("colors.yellow.400");
}
:root {
@include baseTheme;
@include darkTheme;
@include greenTheme;
}
:root.light {
@include lightTheme;
}
:root.dark {
@include darkTheme;
}
:root.black {
@include blackTheme;
}
@media (prefers-color-scheme: dark) {
:root.auto {
@include darkTheme;
}
:root[data-accent="blue"] {
@include blueTheme;
}
@media (prefers-color-scheme: light) {
:root.auto {
@include lightTheme;
}
:root[data-accent="green"] {
@include greenTheme;
}
:root[data-accent="teal"] {
@include tealTheme;
}
:root[data-accent="indigo"] {
@include indigoTheme;
}
:root[data-accent="purple"] {
@include purpleTheme;
}
:root[data-accent="orange"] {
@include orangeTheme;
}
:root[data-accent="pink"] {
@include pinkTheme;
}
:root[data-accent="red"] {
@include redTheme;
}
:root[data-accent="yellow"] {
@include yellowTheme;
}

22
babel.config.js Normal file
View File

@@ -0,0 +1,22 @@
function isBabelLoader(caller) {
return caller && caller.name === "babel-loader"
}
module.exports = function (api) {
if (api.env("test") && !api.caller(isBabelLoader)) {
return {
plugins: ["@babel/plugin-proposal-class-properties"],
presets: [
[
"@babel/preset-env",
{
targets: {
node: "current",
},
},
],
],
}
}
return {}
}

View File

@@ -1,56 +0,0 @@
const axios = require("axios")
const fs = require("fs")
const { spawnSync } = require("child_process")
const runCommand = (command, args) => spawnSync(command, args).stdout.toString().replace(/\n/g, "")
const FAIL_ON_ERROR = false
const PW_BUILD_DATA_DIR = "./.postwoman"
// const IS_DEV_MODE = process.argv.includes("--dev")
try {
;(async () => {
// Create the build data directory if it does not exist.
if (!fs.existsSync(PW_BUILD_DATA_DIR)) {
fs.mkdirSync(PW_BUILD_DATA_DIR)
}
let version = {}
// Get the current version name as the tag from Git.
version.name =
process.env.TRAVIS_TAG || runCommand("git", ["tag --sort=committerdate | tail -1"])
// FALLBACK: If version.name was unset, let's grab it from GitHub.
if (!version.name) {
version.name = (
await axios
.get("https://api.github.com/repos/liyasthomas/postwoman/releases")
// If we can't get it from GitHub, we'll resort to getting it from package.json
.catch((ex) => ({
data: [
{
tag_name: require("./package.json").version,
},
],
}))
).data[0]["tag_name"]
}
// Get the current version hash as the short hash from Git.
// version.hash = runCommand("git", ["rev-parse", "--short", "HEAD"])
// Get the 'variant' name as the branch, if it's not master.
// version.variant =
// process.env.TRAVIS_BRANCH ||
// runCommand("git", ["branch"])
// .split("* ")[1]
// .split(" ")[0] + (IS_DEV_MODE ? " - DEV MODE" : "")
// if (["", "master"].includes(version.variant)) {
// delete version.variant
// }
// Write version data into a file
fs.writeFileSync(`${PW_BUILD_DATA_DIR}/version.json`, JSON.stringify(version))
})()
} catch (ex) {
console.error(ex)
process.exit(FAIL_ON_ERROR ? 1 : 0)
}

View File

@@ -1,7 +1,5 @@
# 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._

View File

@@ -0,0 +1,169 @@
<template>
<div>
<p class="info">
{{ $t("donate_info1") }}
</p>
<p class="info">
{{ $t("donate_info2") }}
</p>
<div class="px-2 row-wrapper">
<span>
<a
href="https://github.com/sponsors/hoppscotch"
target="_blank"
rel="noopener"
v-tooltip.right="$t('recurring')"
>
<button class="icon">
<i class="material-icons">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
class="material-icons"
>
<path
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
/>
</svg>
</i>
<span>GitHub Sponsors</span>
</button>
</a>
</span>
</div>
<div class="px-2 row-wrapper">
<span>
<a
href="https://opencollective.com/hoppscotch"
target="_blank"
rel="noopener"
v-tooltip.right="$t('one_time_recurring')"
>
<button class="icon">
<i class="material-icons">donut_large</i>
<span>{{ $t("open_collective") }}</span>
</button>
</a>
</span>
</div>
<div class="px-2 row-wrapper">
<span>
<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>
</span>
</div>
<div class="px-2 row-wrapper">
<span>
<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>
</span>
</div>
<hr />
<div class="p-2">
<h3 class="title">Financial Contributors</h3>
<div class="contributors">
<a href="https://oss.capital/?ref=hoppscotch" target="_blank" rel="noopener">
<img
style="height: 100%; width: 300px"
src="~assets/images/ossc-banner.svg"
alt="OSS Capital"
/>
</a>
</div>
<div class="contributors">
<a
href="https://appwrite.io/?utm_source=hoppscotch&utm_medium=banner&utm_campaign=hello"
target="_blank"
rel="noopener"
>
<img
style="height: 100%; width: 300px; background: #fff"
src="~assets/images/appwrite-banner.svg"
alt="Appwrite"
/>
</a>
</div>
<div class="contributors">
<a
href="https://paw.cloud/?utm_source=hoppscotch&utm_medium=website&utm_campaign=hoppscotch-sponsorship"
target="_blank"
rel="noopener"
>
<img
style="max-width: 100px"
src="~assets/images/Paw-Logo-for-Hoppscotch.png"
alt="Paw"
/>
</a>
</div>
<div class="contributors">
<a href="https://tyk.io?ref=hoppscotch" target="_blank" rel="noopener">
<img
style="max-width: 320px"
src="~assets/images/Tyk-side-project-logo-tagline-blk.png"
alt="Tyk Banner"
/>
</a>
</div>
<div class="contributors">
<a
target="_blank"
rel="noopener"
href="https://opencollective.com/hoppscotch/organization/0/website"
>
<img src="https://opencollective.com/hoppscotch/organization/0/avatar.svg" />
</a>
<a
target="_blank"
rel="noopener"
href="https://opencollective.com/hoppscotch/organization/1/website"
>
<img src="https://opencollective.com/hoppscotch/organization/1/avatar.svg" />
</a>
</div>
</div>
<hr />
<p class="info">
This project exists thanks to all the
<a
target="_blank"
rel="noopener"
href="https://github.com/hoppscotch/hoppscotch/graphs/contributors"
class="link"
>
people who contribute
</a>
.
</p>
</div>
</template>
<style scoped lang="scss">
.contributors {
@apply flex;
@apply items-center;
@apply flex-nowrap;
@apply overflow-auto;
@apply m-2;
}
</style>

View File

@@ -0,0 +1,88 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("extensions") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<p class="info">
{{ $t("extensions_info1") }}
</p>
<div class="px-2">
<a
href="https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
target="_blank"
rel="noopener"
>
<button class="icon">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<path
d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm8.003 8.657c-1.276-3.321-4.46-4.605-5.534-4.537 3.529 1.376 4.373 6.059 4.06 7.441-.307-1.621-1.286-3.017-1.872-3.385 3.417 8.005-4.835 10.465-7.353 7.687.649.168 1.931.085 2.891-.557.898-.602.983-.638 1.56-.683.686-.053-.041-1.406-1.539-1.177-.616.094-1.632.819-2.88.341-1.508-.576-1.46-2.634.096-2.015.337-.437.088-1.263.088-1.263.452-.414 1.022-.706 1.37-.911.228-.135.829-.507.795-1.23-.123-.096-.32-.219-.766-.193-1.736.11-1.852-.518-1.967-.808.078-.668.524-1.534 1.361-1.931-1.257-.193-2.28.397-2.789 1.154-.809-.174-1.305-.183-2.118-.031-.316-.24-.666-.67-.878-1.181C6.36 3.312 9.027 2 12 2c5.912 0 8.263 4.283 8.003 6.657z"
/>
</svg>
<span>Firefox</span>
<span class="icon" v-if="hasFirefoxExtInstalled" v-tooltip="$t('installed')">
<i class="material-icons">done</i>
</span>
</button>
</a>
</div>
<div class="px-2">
<a
href="https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
target="_blank"
rel="noopener"
>
<button class="icon">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<path
d="M2.897 4.181A11.87 11.87 0 0111.969 0c4.288 0 8.535 2.273 10.717 6.554h-9.293c-1.674.001-2.755-.037-3.926.579-1.376.724-2.415 2.067-2.777 3.644L2.897 4.181zM8.007 12c0 2.2 1.789 3.99 3.988 3.99s3.988-1.79 3.988-3.99-1.789-3.991-3.988-3.991S8.007 9.8 8.007 12zm5.536 5.223c-2.238.666-4.858-.073-6.293-2.549-1.095-1.891-3.989-6.933-5.305-9.225A11.856 11.856 0 000 11.956c0 5.448 3.726 10.65 9.673 11.818l3.87-6.551zm2.158-9.214a5.463 5.463 0 011.007 6.719 1815.43 1815.43 0 01-5.46 9.248C18.437 24.419 24 18.616 24 12.004c0-1.313-.22-2.66-.69-3.995h-7.609z"
/>
</svg>
<span>Chrome</span>
<span class="icon" v-if="hasChromeExtInstalled" v-tooltip="$t('installed')">
<i class="material-icons">done</i>
</span>
</button>
</a>
</div>
</div>
<div slot="footer"></div>
</SmartModal>
</template>
<script>
import {
hasChromeExtensionInstalled,
hasFirefoxExtensionInstalled,
} from "~/helpers/strategies/ExtensionStrategy"
export default {
props: {
show: Boolean,
},
watch: {
show() {
this.hasChromeExtInstalled = hasChromeExtensionInstalled()
this.hasFirefoxExtInstalled = hasFirefoxExtensionInstalled()
},
},
data() {
return {
hasChromeExtInstalled: hasChromeExtensionInstalled(),
hasFirefoxExtInstalled: hasFirefoxExtensionInstalled(),
}
},
methods: {
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

88
components/app/Footer.vue Normal file
View File

@@ -0,0 +1,88 @@
<template>
<footer class="footer">
<div class="row-wrapper">
<span class="flex flex-col font-mono md:flex-row" style="align-items: start">
<a class="footer-link" href="https://www.netlify.com" target="_blank" rel="noopener">
Powered by Netlify
</a>
<span class="footer-link"> Sponsored by </span>
<span>
<a
class="footer-link"
href="https://oss.capital/?ref=hoppscotch"
target="_blank"
rel="noopener"
>
OSS Capital
</a>
</span>
<span class="footer-link"> & </span>
<span>
<a
class="footer-link"
href="https://paw.cloud/?utm_source=hoppscotch&utm_medium=website&utm_campaign=hoppscotch-sponsorship"
target="_blank"
rel="noopener"
>
Paw
</a>
</span>
<iframe
src="https://ghbtns.com/github-btn.html?user=hoppscotch&type=sponsor"
frameborder="0"
scrolling="0"
width="150"
height="20"
title="GitHub"
class="footer-link"
loading="lazy"
></iframe>
</span>
<span class="flex flex-col font-mono md:flex-row" style="align-items: start">
<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-link {
@apply inline-flex;
@apply flex-shrink-0;
@apply my-2;
@apply mx-4;
@apply text-fgLightColor;
&:hover {
@apply text-fgColor;
}
}
</style>
<script>
export default {
computed: {
availableLocales() {
return this.$i18n.locales.filter(({ code }) => code !== this.$i18n.locale)
},
},
}
</script>

319
components/app/Header.vue Normal file
View File

@@ -0,0 +1,319 @@
<template>
<header class="header">
<div class="row-wrapper">
<span class="slide-in">
<nuxt-link :to="localePath('index')">
<h1 class="hide-on-small-screen logo">Hoppscotch</h1>
<h1 class="show-on-small-screen logo">Hs</h1>
</nuxt-link>
<iframe
src="https://ghbtns.com/github-btn.html?user=hoppscotch&repo=hoppscotch&type=star&count=true"
frameborder="0"
scrolling="0"
width="150"
height="20"
title="GitHub"
class="ml-8 hide-on-small-screen"
loading="lazy"
></iframe>
</span>
<span>
<a
href="https://appwrite.io/?utm_source=hoppscotch&utm_medium=banner&utm_campaign=hello"
target="_blank"
rel="noopener"
class="inline-flex items-center px-4 py-2 mx-4 font-mono text-sm rounded-md bg-bgDarkColor hide-on-small-screen"
>
Appwrite - Open-Source Backend as a Service
<img class="w-8 ml-2" src="~assets/images/appwrite-icon.svg" alt="Appwrite" />
</a>
<button
class="icon"
id="installPWA"
@click.prevent="showInstallPrompt()"
v-tooltip="$t('install_pwa')"
>
<i class="material-icons">offline_bolt</i>
</button>
<a
href="https://github.com/hoppscotch/hoppscotch"
target="_blank"
aria-label="GitHub"
rel="noopener"
>
<button class="icon" aria-label="GitHub" v-tooltip="'GitHub'">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="material-icons">
<path
d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"
/>
</svg>
</button>
</a>
<v-popover v-if="fb.currentUser === null">
<button class="icon" v-tooltip="$t('login_with')">
<i class="material-icons">login</i>
</button>
<template slot="popover">
<FirebaseLogin />
</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="w-6 h-6 rounded-full 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>
<FirebaseLogout />
</div>
</template>
</v-popover>
<v-popover>
<button class="icon" v-tooltip="$t('more')">
<i class="material-icons">drag_indicator</i>
</button>
<template slot="popover">
<button class="icon" @click="showExtensions = true" v-close-popover>
<i class="material-icons">extension</i>
<span>{{ $t("extensions") }}</span>
</button>
<button class="icon" @click="showShortcuts = true" v-close-popover>
<i class="material-icons">keyboard</i>
<span>{{ $t("shortcuts") }}</span>
</button>
<button class="icon" @click="showSupport = true" v-close-popover>
<i class="material-icons">favorite</i>
<span>{{ $t("support_us") }}</span>
</button>
<button
class="icon"
onClick="window.open('https://twitter.com/share?text=👽 Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.&url=https://hoppscotch.io&hashtags=hoppscotch&via=liyasthomas');"
v-close-popover
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<path
d="M24 4.557a9.83 9.83 0 01-2.828.775 4.932 4.932 0 002.165-2.724 9.864 9.864 0 01-3.127 1.195 4.916 4.916 0 00-3.594-1.555c-3.179 0-5.515 2.966-4.797 6.045A13.978 13.978 0 011.671 3.149a4.93 4.93 0 001.523 6.574 4.903 4.903 0 01-2.229-.616c-.054 2.281 1.581 4.415 3.949 4.89a4.935 4.935 0 01-2.224.084 4.928 4.928 0 004.6 3.419A9.9 9.9 0 010 19.54a13.94 13.94 0 007.548 2.212c9.142 0 14.307-7.721 13.995-14.646A10.025 10.025 0 0024 4.557z"
/>
</svg>
<span>Tweet</span>
</button>
<button
v-if="navigatorShare"
class="icon"
@click="nativeShare"
v-close-popover
v-tooltip="$t('more')"
>
<i class="material-icons">share</i>
<span>Share</span>
</button>
</template>
</v-popover>
</span>
</div>
<AppExtensions :show="showExtensions" @hide-modal="showExtensions = false" />
<AppShortcuts :show="showShortcuts" @hide-modal="showShortcuts = false" />
<AppSupport :show="showSupport" @hide-modal="showSupport = false" />
</header>
</template>
<style scoped lang="scss">
$responsiveWidth: 768px;
.logo {
@apply text-xl;
@apply transition-colors;
@apply ease-in-out;
@apply duration-150;
&:hover {
@apply text-acColor;
}
}
@keyframes slideIn {
0% {
@apply opacity-0;
@apply -left-4;
}
100% {
@apply opacity-100;
@apply left-0;
}
}
.slide-in {
@apply relative;
animation: slideIn 0.2s forwards ease-in-out;
}
.show-on-small-screen {
@apply hidden;
}
@media (max-width: $responsiveWidth) {
.show-on-small-screen {
@apply inline-flex;
}
}
</style>
<script>
import intializePwa from "~/helpers/pwa"
import { fb } from "~/helpers/fb"
// import { hasExtensionInstalled } from "~/helpers/strategies/ExtensionStrategy"
export default {
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,
showShortcuts: false,
showSupport: false,
navigatorShare: navigator.share,
fb,
}
},
async mounted() {
this._keyListener = function (e) {
if (e.key === "Escape") {
e.preventDefault()
this.showExtensions = this.showShortcuts = this.showSupport = false
}
}
document.addEventListener("keydown", this._keyListener.bind(this))
// Initializes the PWA code - checks if the app is installed,
// etc.
this.showInstallPrompt = await intializePwa()
let cookiesAllowed = localStorage.getItem("cookiesAllowed") === "yes"
if (!cookiesAllowed) {
this.$toast.show(this.$t("we_use_cookies"), {
icon: "info",
duration: 5000,
theme: "toasted-primary",
action: [
{
text: this.$t("dismiss"),
onClick: (e, toastObject) => {
localStorage.setItem("cookiesAllowed", "yes")
toastObject.goAway(0)
},
},
],
})
}
// let showAd = localStorage.getItem("showAd") === "no"
// if (!showAd) {
// setTimeout(() => {
// this.$toast.clear()
// this.$toast.show(
// "<span><a href='https://github.com/sponsors/hoppscotch' target='_blank' rel='noopener'>Sponsor us to support Hoppscotch open source project 💖</a><br><sub>Whoosh this away to dismiss.</sub></span>",
// {
// icon: "",
// duration: 0,
// theme: "toasted-ad",
// action: [
// {
// text: "Sponsor",
// icon: "chevron_right",
// onClick: (e, toastObject) => {
// localStorage.setItem("showAd", "no")
// toastObject.goAway(0)
// window.open("https://github.com/sponsors/hoppscotch")
// },
// },
// ],
// onComplete() {
// localStorage.setItem("showAd", "no")
// },
// }
// )
// }, 8000)
// }
// let showExtensionsToast = localStorage.getItem("showExtensionsToast") === "yes"
// if (!showExtensionsToast) {
// setTimeout(() => {
// if (!hasExtensionInstalled()) {
// this.$toast.show(this.$t("extensions_info2"), {
// icon: "extension",
// duration: 5000,
// theme: "toasted-primary",
// action: [
// {
// text: this.$t("yes"),
// onClick: (e, toastObject) => {
// this.showExtensions = true
// localStorage.setItem("showExtensionsToast", "yes")
// toastObject.goAway(0)
// },
// },
// {
// text: this.$t("no"),
// onClick: (e, toastObject) => {
// this.$store.commit("setMiscState", {
// value: false,
// attribute: "showExtensionsToast",
// })
// localStorage.setItem("showExtensionsToast", "no")
// toastObject.goAway(0)
// },
// },
// ],
// })
// }
// }, 5000)
// }
},
methods: {
nativeShare() {
if (navigator.share) {
navigator
.share({
title: "Hoppscotch",
text:
"Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.",
url: "https://hoppscotch.io",
})
.then(() => {})
.catch(console.error)
} else {
// fallback
}
},
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
}
</script>

View File

@@ -0,0 +1,67 @@
<template>
<fieldset :id="label.toLowerCase()">
<legend v-if="!noLegend" @click.prevent="collapse">
<span>{{ label }}</span>
<i class="ml-2 align-middle 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 {
@apply my-4;
@apply rounded-lg;
@apply bg-bgDarkColor;
@apply transition;
@apply ease-in-out;
@apply duration-150;
@apply w-full;
legend {
@apply px-4;
@apply text-fgColor;
@apply font-bold;
@apply cursor-pointer;
@apply transition;
@apply ease-in-out;
@apply duration-150;
}
}
</style>
<script lang="ts">
import Vue from "vue"
export default Vue.extend({
computed: {
sectionString(): string {
return `${this.$route.path.replace(/\/+$/, "")}/${this.label}`
},
},
props: {
label: {
type: String,
default: "Section",
},
noLegend: {
type: Boolean,
default: false,
},
},
methods: {
collapse() {
// Save collapsed section into the collapsedSections array
this.$store.commit("setCollapsedSection", this.sectionString)
},
isCollapsed(_label: string) {
return this.$store.state.theme.collapsedSections.includes(this.sectionString) || false
},
},
})
</script>

View File

@@ -0,0 +1,102 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("shortcuts") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<div class="p-2">
<div>
<kbd>{{ getSpecialKey() }}</kbd>
+
<kbd>G</kbd>
<label>{{ $t("send_request") }}</label>
</div>
<div>
<kbd>{{ getSpecialKey() }}</kbd
>+<kbd>S</kbd>
<label>{{ $t("save_to_collections") }}</label>
</div>
<div>
<kbd>{{ getSpecialKey() }}</kbd
>+<kbd>K</kbd>
<label>{{ $t("copy_request_link") }}</label>
</div>
<div>
<kbd>{{ getSpecialKey() }}</kbd
>+<kbd>I</kbd>
<label>{{ $t("reset_request") }}</label>
</div>
</div>
<hr />
<div class="p-2">
<div>
<kbd>Alt</kbd>+<kbd></kbd>
<label>{{ $t("select_next_method") }}</label>
</div>
<div>
<kbd>Alt</kbd>+<kbd></kbd>
<label>{{ $t("select_previous_method") }}</label>
</div>
</div>
<hr />
<div class="p-2">
<div>
<kbd>Alt</kbd>+<kbd>G</kbd>
<label>{{ $t("select_get_method") }}</label>
</div>
<div>
<kbd>Alt</kbd>+<kbd>H</kbd>
<label>{{ $t("select_head_method") }}</label>
</div>
<div>
<kbd>Alt</kbd>+<kbd>P</kbd>
<label>{{ $t("select_post_method") }}</label>
</div>
<div>
<kbd>Alt</kbd>+<kbd>U</kbd>
<label>{{ $t("select_put_method") }}</label>
</div>
<div>
<kbd>Alt</kbd>+<kbd>X</kbd>
<label>{{ $t("select_delete_method") }}</label>
</div>
</div>
</div>
<div slot="footer"></div>
</SmartModal>
</template>
<style scoped lang="scss">
kbd {
@apply inline-flex;
@apply resize-none;
@apply m-2;
@apply rounded-lg;
@apply py-2;
@apply px-4;
@apply text-sm;
}
</style>
<script>
import { getPlatformSpecialKey } from "~/helpers/platformutils"
export default {
props: {
show: Boolean,
},
methods: {
getSpecialKey: getPlatformSpecialKey,
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

307
components/app/Sidenav.vue Normal file
View File

@@ -0,0 +1,307 @@
<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')"
>
<AppLogo alt class="material-icons" style="height: 24px" />
</nuxt-link>
<nuxt-link
:to="localePath('realtime')"
:class="linkActive('/realtime')"
v-tooltip.right="$t('realtime')"
>
<i class="material-icons">language</i>
</nuxt-link>
<nuxt-link
:to="localePath('graphql')"
:class="linkActive('/graphql')"
v-tooltip.right="$t('graphql')"
:aria-label="$t('graphql')"
>
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 29.999 30">
<path d="M4.08 22.864l-1.1-.636L15.248.98l1.1.636z" />
<path d="M2.727 20.53h24.538v1.272H2.727z" />
<path
d="M15.486 28.332L3.213 21.246l.636-1.1 12.273 7.086zm10.662-18.47L13.874 2.777l.636-1.1 12.273 7.086z"
/>
<path d="M3.852 9.858l-.636-1.1L15.5 1.67l.636 1.1z" />
<path
d="M25.922 22.864l-12.27-21.25 1.1-.636 12.27 21.25zM3.7 7.914h1.272v14.172H3.7zm21.328 0H26.3v14.172h-1.272z"
/>
<path d="M15.27 27.793l-.555-.962 10.675-6.163.555.962z" />
<path
d="M27.985 22.5a2.68 2.68 0 01-3.654.981 2.68 2.68 0 01-.981-3.654 2.68 2.68 0 013.654-.981 2.665 2.665 0 01.98 3.654M6.642 10.174a2.68 2.68 0 01-3.654.981A2.68 2.68 0 012.007 7.5a2.68 2.68 0 013.654-.981 2.68 2.68 0 01.981 3.654M2.015 22.5a2.68 2.68 0 01.981-3.654 2.68 2.68 0 013.654.981 2.68 2.68 0 01-.981 3.654c-1.287.735-2.92.3-3.654-.98m21.343-12.326a2.68 2.68 0 01.981-3.654 2.68 2.68 0 013.654.981 2.68 2.68 0 01-.981 3.654 2.68 2.68 0 01-3.654-.981M15 30a2.674 2.674 0 112.674-2.673A2.68 2.68 0 0115 30m0-24.652a2.67 2.67 0 01-2.674-2.674 2.67 2.67 0 115.347 0A2.67 2.67 0 0115 5.347"
/>
</svg>
</nuxt-link>
<nuxt-link
:to="localePath('doc')"
:class="linkActive('/doc')"
v-tooltip.right="$t('documentation')"
:aria-label="$t('documentation')"
>
<i class="material-icons">topic</i>
</nuxt-link>
<nuxt-link
:to="localePath('settings')"
:class="linkActive('/settings')"
v-tooltip.right="$t('settings')"
:aria-label="$t('settings')"
>
<i class="material-icons">settings</i>
</nuxt-link>
</nav>
<nav v-if="$route.path == '/'" class="secondary-nav">
<a href="#request" v-tooltip.right="$t('request')">
<i class="material-icons">cloud_upload</i>
</a>
<a href="#options" v-tooltip.right="$t('options')">
<i class="material-icons">toc</i>
</a>
<a href="#response" v-tooltip.right="$t('response')">
<i class="material-icons">cloud_download</i>
</a>
</nav>
<nav v-else-if="$route.path.includes('/realtime')" class="secondary-nav">
<a href="#request" v-tooltip.right="$t('request')">
<i class="material-icons">cloud_upload</i>
</a>
<a href="#response" v-tooltip.right="$t('communication')">
<i class="material-icons">cloud_download</i>
</a>
</nav>
<nav v-else-if="$route.path.includes('/graphql')" class="secondary-nav">
<a href="#endpoint" v-tooltip.right="$t('endpoint')">
<i class="material-icons">cloud</i>
</a>
<a href="#schema" v-tooltip.right="$t('schema')">
<i class="material-icons">assignment_returned</i>
</a>
<a href="#query" v-tooltip.right="$t('query')">
<i class="material-icons">cloud_upload</i>
</a>
<a href="#response" v-tooltip.right="$t('response')">
<i class="material-icons">cloud_download</i>
</a>
</nav>
<nav v-else-if="$route.path.includes('/doc')" class="secondary-nav">
<a href="#import" v-tooltip.right="$t('import')">
<i class="material-icons">folder</i>
</a>
<a href="#documentation" v-tooltip.right="$t('documentation')">
<i class="material-icons">insert_drive_file</i>
</a>
</nav>
<nav v-else-if="$route.path.includes('/settings')" class="secondary-nav">
<a href="#account" v-tooltip.right="$t('account')">
<i class="material-icons">person</i>
</a>
<a href="#theme" v-tooltip.right="$t('theme')">
<i class="material-icons">brush</i>
</a>
<a href="#extensions" v-tooltip.right="$t('extensions')">
<i class="material-icons">extension</i>
</a>
<a href="#proxy" v-tooltip.right="$t('proxy')">
<i class="material-icons">public</i>
</a>
</nav>
</aside>
</template>
<style scoped lang="scss">
$responsiveWidth: 768px;
.nav-first {
@apply z-10;
@apply h-screen;
@apply p-2;
@apply bg-bgDarkColor;
@apply transition;
@apply ease-in-out;
@apply duration-150;
@apply space-y-2;
}
nav.primary-nav {
@apply flex;
@apply flex-col;
@apply flex-nowrap;
@apply items-center;
@apply justify-center;
@apply space-y-2;
@apply w-full;
svg {
@apply fill-current;
}
a {
@apply inline-flex;
@apply items-center;
@apply justify-center;
@apply flex-shrink-0;
@apply p-4;
@apply rounded-full;
@apply bg-bgLightColor;
@apply text-fgLightColor;
@apply fill-current;
@apply outline-none;
@apply transition;
@apply ease-in-out;
@apply duration-150;
&:hover {
@apply text-fgColor;
@apply fill-current;
svg {
@apply fill-current;
}
}
&.nuxt-link-exact-active {
@apply bg-acColor;
@apply text-actColor;
@apply fill-current;
border-radius: 16px;
svg {
@apply fill-current;
}
}
}
}
nav.primary-nav::-webkit-scrollbar,
.nav-first::-webkit-scrollbar {
@apply hidden;
}
nav.secondary-nav {
@apply flex;
@apply flex-col;
@apply flex-nowrap;
@apply items-center;
@apply justify-center;
@apply border-t-2;
@apply border-dashed;
@apply border-brdColor;
@apply pt-2;
@apply space-y-2;
a {
@apply inline-flex;
@apply items-center;
@apply justify-center;
@apply flex-shrink-0;
@apply p-4;
@apply rounded-full;
@apply bg-bgDarkColor;
@apply text-fgLightColor;
@apply fill-current;
@apply outline-none;
@apply transition;
@apply ease-in-out;
@apply duration-150;
&:hover {
@apply text-fgColor;
@apply fill-current;
}
&.current {
@apply text-acColor;
@apply fill-current;
}
}
}
@media (max-width: $responsiveWidth) {
.nav-first {
@apply fixed;
@apply top-auto;
@apply bottom-0;
@apply h-auto;
@apply p-0;
@apply w-full;
@apply bg-bgColor;
@apply shadow-2xl;
}
nav.primary-nav {
@apply flex-row;
@apply flex-nowrap;
@apply overflow-auto;
@apply bg-bgDarkColor;
@apply space-y-0;
padding-bottom: env(safe-area-inset-bottom);
a {
@apply bg-transparent;
@apply my-2;
@apply flex-1;
&.nuxt-link-exact-active {
@apply bg-transparent;
@apply text-acColor;
@apply fill-current;
svg {
@apply fill-current;
}
}
}
}
nav.secondary-nav {
@apply hidden;
}
}
</style>
<script>
export default {
methods: {
linkActive(path) {
return {
"nuxt-link-exact-active": this.$route.path === path,
"nuxt-link-active": this.$route.path === path,
}
},
},
mounted() {
window.addEventListener("scroll", (event) => {
let mainNavLinks = document.querySelectorAll("nav ul li a")
let fromTop = window.scrollY
mainNavLinks.forEach(({ hash, classList }) => {
let section = document.querySelector(hash)
if (
section &&
section.offsetTop <= fromTop &&
section.offsetTop + section.offsetHeight > fromTop
) {
classList.add("current")
} else {
classList.remove("current")
}
})
})
},
// watch: {
// $route() {
// // this.$toast.clear();
// },
// },
}
</script>

View File

@@ -0,0 +1,31 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("support_us") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<AppContributors />
</div>
<div slot="footer"></div>
</SmartModal>
</template>
<script>
export default {
props: {
show: Boolean,
},
methods: {
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -0,0 +1,84 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("new_collection") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="$t('my_new_collection')"
@keyup.enter="addNewCollection"
/>
</div>
<div slot="footer">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="addNewCollection">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
show: Boolean,
},
data() {
return {
name: undefined,
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
addNewCollection() {
if (!this.$data.name) {
this.$toast.info(this.$t("invalid_collection_name"))
return
}
this.$store.commit("postwoman/addNewCollection", {
name: this.$data.name,
flag: "rest",
})
this.$emit("hide-modal")
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
this.$data.name = undefined
},
},
}
</script>

View File

@@ -0,0 +1,65 @@
<template>
<SmartModal v-if="show" @close="show = false">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("new_folder") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="$t('my_new_folder')"
@keyup.enter="addFolder"
/>
</div>
<div slot="footer">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="addFolder">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
export default {
props: {
show: Boolean,
folder: Object,
folderPath: String,
collectionIndex: Number,
},
data() {
return {
name: undefined,
}
},
methods: {
addFolder() {
this.$emit("add-folder", {
name: this.name,
folder: this.folder,
path: this.folderPath || `${this.collectionIndex}`,
})
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -0,0 +1,182 @@
<template>
<div>
<div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
@dragover.prevent
@drop.prevent="dropEvent"
@dragover="dragging = true"
@drop="dragging = false"
@dragleave="dragging = false"
@dragend="dragging = false"
>
<button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i>
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i>
<i class="material-icons">folder</i>
<span>{{ collection.name }}</span>
</button>
<div>
<button
v-if="doc"
class="icon"
@click="$emit('select-collection')"
v-tooltip.left="$t('import')"
>
<i class="material-icons">topic</i>
</button>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button
class="icon"
@click="$emit('add-folder', { folder: collection, path: `${collectionIndex}` })"
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="confirmRemove = true" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
</div>
<div v-show="showChildren || isFiltered">
<ul class="flex-col">
<li
v-for="(folder, index) in collection.folders"
:key="folder.name"
class="ml-8 border-l border-brdColor"
>
<CollectionsFolder
:folder="folder"
:folder-index="index"
:folder-path="`${collectionIndex}/${index}`"
:collection-index="collectionIndex"
:doc="doc"
:isFiltered="isFiltered"
@add-folder="$emit('add-folder', $event)"
@edit-folder="$emit('edit-folder', $event)"
@edit-request="$emit('edit-request', $event)"
/>
</li>
</ul>
<ul class="flex-col">
<li
v-for="(request, index) in collection.requests"
:key="index"
class="ml-8 border-l border-brdColor"
>
<CollectionsRequest
:request="request"
:collection-index="collectionIndex"
:folder-index="-1"
:folder-name="collection.name"
:request-index="index"
:doc="doc"
@edit-request="$emit('edit-request', $event)"
/>
</li>
</ul>
<ul>
<li
v-if="collection.folders.length === 0 && collection.requests.length === 0"
class="flex ml-8 border-l border-brdColor"
>
<p class="info">
<i class="material-icons">not_interested</i> {{ $t("collection_empty") }}
</p>
</li>
</ul>
</div>
<SmartConfirmModal
:show="confirmRemove"
:title="$t('are_you_sure_remove_collection')"
@hide-modal="confirmRemove = false"
@resolve="removeCollection"
/>
</div>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
collectionIndex: Number,
collection: Object,
doc: Boolean,
isFiltered: Boolean,
},
data() {
return {
showChildren: false,
dragging: false,
selectedFolder: {},
confirmRemove: false,
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
toggleShowChildren() {
this.showChildren = !this.showChildren
},
removeCollection() {
this.$store.commit("postwoman/removeCollection", {
collectionIndex: this.collectionIndex,
flag: "rest",
})
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
this.syncCollections()
},
dropEvent({ dataTransfer }) {
this.dragging = !this.dragging
const oldCollectionIndex = dataTransfer.getData("oldCollectionIndex")
const oldFolderIndex = dataTransfer.getData("oldFolderIndex")
const oldFolderName = dataTransfer.getData("oldFolderName")
const requestIndex = dataTransfer.getData("requestIndex")
const flag = "rest"
this.$store.commit("postwoman/moveRequest", {
oldCollectionIndex,
newCollectionIndex: this.$props.collectionIndex,
newFolderIndex: -1,
newFolderName: this.$props.collection.name,
oldFolderIndex,
oldFolderName,
requestIndex,
flag,
})
this.syncCollections()
},
},
}
</script>

View File

@@ -0,0 +1,90 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("edit_collection") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="editingCollection.name"
@keyup.enter="saveCollection"
/>
</div>
<div slot="footer">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="saveCollection">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
show: Boolean,
editingCollection: Object,
editingCollectionIndex: Number,
},
data() {
return {
name: undefined,
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
saveCollection() {
if (!this.$data.name) {
this.$toast.info(this.$t("invalid_collection_name"))
return
}
const collectionUpdated = {
...this.$props.editingCollection,
name: this.$data.name,
}
this.$store.commit("postwoman/editCollection", {
collection: collectionUpdated,
collectionIndex: this.$props.editingCollectionIndex,
flag: "rest",
})
this.$emit("hide-modal")
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -0,0 +1,85 @@
<template>
<SmartModal v-if="show" @close="show = false">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("edit_folder") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="folder.name"
@keyup.enter="editFolder"
/>
</div>
<div slot="footer">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="editFolder">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
show: Boolean,
collectionIndex: Number,
folder: Object,
folderIndex: Number,
},
data() {
return {
name: undefined,
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
editFolder() {
this.$store.commit("postwoman/editFolder", {
collectionIndex: this.$props.collectionIndex,
folder: { ...this.$props.folder, name: this.$data.name },
folderIndex: this.$props.folderIndex,
folderName: this.$props.folder.name,
flag: "rest",
})
this.hideModal()
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -0,0 +1,96 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("edit_request") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="requestUpdateData.name"
@keyup.enter="saveRequest"
:placeholder="request.name"
/>
</div>
<div slot="footer">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="saveRequest">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
show: Boolean,
collectionIndex: Number,
folderIndex: Number,
folderName: String,
request: Object,
requestIndex: Number,
},
data() {
return {
requestUpdateData: {
name: undefined,
},
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
saveRequest() {
const requestUpdated = {
...this.$props.request,
name: this.$data.requestUpdateData.name || this.$props.request.name,
}
this.$store.commit("postwoman/editRequest", {
requestCollectionIndex: this.$props.collectionIndex,
requestFolderName: this.$props.folderName,
requestFolderIndex: this.$props.folderIndex,
requestNew: requestUpdated,
requestIndex: this.$props.requestIndex,
flag: "rest",
})
this.hideModal()
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -0,0 +1,184 @@
<template>
<div>
<div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
@dragover.prevent
@drop.prevent="dropEvent"
@dragover="dragging = true"
@drop="dragging = false"
@dragleave="dragging = false"
@dragend="dragging = false"
>
<div>
<button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i>
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i>
<i class="material-icons">folder_open</i>
<span>{{ folder.name }}</span>
</button>
</div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button
class="icon"
@click="$emit('add-folder', { folder, path: folderPath })"
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-folder', { folder, folderIndex, collectionIndex })"
v-close-popover
>
<i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span>
</button>
</div>
<div>
<button class="icon" @click="confirmRemove = true" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<div v-show="showChildren || isFiltered">
<ul v-if="folder.folders && folder.folders.length" class="flex-col">
<li
v-for="(subFolder, subFolderIndex) in folder.folders"
:key="subFolder.name"
class="ml-8 border-l border-brdColor"
>
<CollectionsFolder
:folder="subFolder"
:folder-index="subFolderIndex"
:collection-index="collectionIndex"
:doc="doc"
:folder-path="`${folderPath}/${subFolderIndex}`"
@add-folder="$emit('add-folder', $event)"
@edit-folder="$emit('edit-folder', $event)"
@edit-request="$emit('edit-request', $event)"
/>
</li>
</ul>
<ul class="flex-col">
<li
v-for="(request, index) in folder.requests"
:key="index"
class="flex ml-8 border-l border-brdColor"
>
<CollectionsRequest
:request="request"
:collection-index="collectionIndex"
:folder-index="folderIndex"
:folder-name="folder.name"
:request-index="index"
:doc="doc"
@edit-request="$emit('edit-request', $event)"
/>
</li>
</ul>
<ul
v-if="
folder.folders &&
folder.folders.length === 0 &&
folder.requests &&
folder.requests.length === 0
"
>
<li class="flex ml-8 border-l border-brdColor">
<p class="info"><i class="material-icons">not_interested</i> {{ $t("folder_empty") }}</p>
</li>
</ul>
</div>
<SmartConfirmModal
:show="confirmRemove"
:title="$t('are_you_sure_remove_folder')"
@hide-modal="confirmRemove = false"
@resolve="removeFolder"
/>
</div>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
name: "folder",
props: {
folder: Object,
folderIndex: Number,
collectionIndex: Number,
folderPath: String,
doc: Boolean,
isFiltered: Boolean,
},
data() {
return {
showChildren: false,
dragging: false,
confirmRemove: false,
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
toggleShowChildren() {
this.showChildren = !this.showChildren
},
removeFolder() {
this.$store.commit("postwoman/removeFolder", {
collectionIndex: this.$props.collectionIndex,
folderName: this.$props.folder.name,
folderIndex: this.$props.folderIndex,
flag: "rest",
})
this.syncCollections()
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
},
dropEvent({ dataTransfer }) {
this.dragging = !this.dragging
const oldCollectionIndex = dataTransfer.getData("oldCollectionIndex")
const oldFolderIndex = dataTransfer.getData("oldFolderIndex")
const oldFolderName = dataTransfer.getData("oldFolderName")
const requestIndex = dataTransfer.getData("requestIndex")
const flag = "rest"
this.$store.commit("postwoman/moveRequest", {
oldCollectionIndex,
newCollectionIndex: this.$props.collectionIndex,
newFolderIndex: this.$props.folderIndex,
newFolderName: this.$props.folder.name,
oldFolderIndex,
oldFolderName,
requestIndex,
flag,
})
this.syncCollections()
},
},
}
</script>

View File

@@ -0,0 +1,395 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("import_export") }} {{ $t("collections") }}</h3>
<div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="readCollectionGist" v-close-popover>
<i class="material-icons">assignment_returned</i>
<span>{{ $t("import_from_gist") }}</span>
</button>
</div>
<div
v-tooltip.bottom="{
content: !fb.currentUser
? $t('login_with_github_to') + $t('create_secret_gist')
: fb.currentUser.provider !== 'github.com'
? $t('login_with_github_to') + $t('create_secret_gist')
: null,
}"
>
<button
:disabled="
!fb.currentUser ? true : fb.currentUser.provider !== 'github.com' ? true : false
"
class="icon"
@click="createCollectionGist"
v-close-popover
>
<i class="material-icons">assignment_turned_in</i>
<span>{{ $t("create_secret_gist") }}</span>
</button>
</div>
</template>
</v-popover>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<div class="flex flex-col items-start p-2">
<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>
<div v-if="showJsonCode" class="row-wrapper">
<textarea v-model="collectionJson" rows="8" readonly></textarea>
</div>
</div>
<div slot="footer">
<div class="row-wrapper">
<span>
<SmartToggle :on="showJsonCode" @change="showJsonCode = $event">
{{ $t("show_code") }}
</SmartToggle>
</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>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
data() {
return {
fb,
showJsonCode: false,
}
},
subscriptions() {
SYNC_COLLECTIONS: getSettingSubject("syncCollections")
},
props: {
show: Boolean,
},
computed: {
collectionJson() {
return JSON.stringify(this.$store.state.postwoman.collections, null, 2)
},
},
methods: {
async createCollectionGist() {
await this.$axios
.$post(
"https://api.github.com/gists",
{
files: {
"hoppscotch-collections.json": {
content: this.collectionJson,
},
},
},
{
headers: {
Authorization: `token ${fb.currentUser.accessToken}`,
Accept: "application/vnd.github.v3+json",
},
}
)
.then(({ html_url }) => {
this.$toast.success(this.$t("gist_created"), {
icon: "done",
})
window.open(html_url)
})
.catch((error) => {
this.$toast.error(this.$t("something_went_wrong"), {
icon: "error",
})
console.log(error)
})
},
async readCollectionGist() {
let gist = prompt(this.$t("enter_gist_url"))
if (!gist) return
await this.$axios
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
headers: {
Accept: "application/vnd.github.v3+json",
},
})
.then(({ files }) => {
let collections = JSON.parse(Object.values(files)[0].content)
this.$store.commit("postwoman/replaceCollections", { data: collections, flag: "rest" })
this.fileImported()
this.syncToFBCollections()
})
.catch((error) => {
this.failedImport()
console.log(error)
})
},
hideModal() {
this.$emit("hide-modal")
},
openDialogChooseFileToReplaceWith() {
this.$refs.inputChooseFileToReplaceWith.click()
},
openDialogChooseFileToImportFrom() {
this.$refs.inputChooseFileToImportFrom.click()
},
replaceWithJSON() {
let reader = new FileReader()
reader.onload = ({ target }) => {
let content = 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 {
this.failedImport()
return
}
this.$store.commit("postwoman/replaceCollections", { data: collections, flag: "rest" })
this.fileImported()
this.syncToFBCollections()
}
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
this.$refs.inputChooseFileToReplaceWith.value = ""
},
importFromJSON() {
let reader = new FileReader()
reader.onload = ({ target }) => {
let content = 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")) {
//replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
collections = JSON.parse(content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>"))
collections = [this.parsePostmanCollection(collections)]
} else {
this.failedImport()
return
}
this.$store.commit("postwoman/importCollections", { data: collections, flag: "rest" })
this.fileImported()
this.syncToFBCollections()
}
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
this.$refs.inputChooseFileToImportFrom.value = ""
},
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 = "hoppscotch-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", {
data: fb.currentCollections,
flag: "rest",
})
this.fileImported()
},
syncToFBCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
fileImported() {
this.$toast.info(this.$t("file_imported"), {
icon: "folder_shared",
})
},
failedImport() {
this.$toast.error(this.$t("import_failed"), {
icon: "error",
})
},
parsePostmanCollection({ info, name, item }) {
let postwomanCollection = {
name: "",
folders: [],
requests: [],
}
postwomanCollection.name = info ? info.name : name
if (item && item.length > 0) {
for (let collectionItem of item) {
if (collectionItem.request) {
if (postwomanCollection.hasOwnProperty("folders")) {
postwomanCollection.name = info ? info.name : name
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
} else {
postwomanCollection.name = name ? name : ""
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
}
} else if (this.hasFolder(collectionItem)) {
postwomanCollection.folders.push(this.parsePostmanCollection(collectionItem))
} else {
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
}
}
}
return postwomanCollection
},
parsePostmanRequest({ name, request }) {
let pwRequest = {
url: "",
path: "",
method: "",
auth: "",
httpUser: "",
httpPassword: "",
passwordFieldType: "password",
bearerToken: "",
headers: [],
params: [],
bodyParams: [],
rawParams: "",
rawInput: false,
contentType: "",
requestType: "",
name: "",
}
pwRequest.name = name
let requestObjectUrl = request.url.raw.match(/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/)
if (requestObjectUrl) {
pwRequest.url = requestObjectUrl[1]
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
}
pwRequest.method = request.method
let itemAuth = request.auth ? request.auth : ""
let authType = itemAuth ? itemAuth.type : ""
if (authType === "basic") {
pwRequest.auth = "Basic Auth"
pwRequest.httpUser =
itemAuth.basic[0].key === "username" ? itemAuth.basic[0].value : itemAuth.basic[1].value
pwRequest.httpPassword =
itemAuth.basic[0].key === "password" ? itemAuth.basic[0].value : itemAuth.basic[1].value
} else if (authType === "oauth2") {
pwRequest.auth = "OAuth 2.0"
pwRequest.bearerToken =
itemAuth.oauth2[0].key === "accessToken"
? itemAuth.oauth2[0].value
: itemAuth.oauth2[1].value
} else if (authType === "bearer") {
pwRequest.auth = "Bearer Token"
pwRequest.bearerToken = itemAuth.bearer[0].value
}
let requestObjectHeaders = request.header
if (requestObjectHeaders) {
pwRequest.headers = requestObjectHeaders
for (let header of pwRequest.headers) {
delete header.name
delete header.type
}
}
let requestObjectParams = request.url.query
if (requestObjectParams) {
pwRequest.params = requestObjectParams
for (let param of pwRequest.params) {
delete param.disabled
}
}
if (request.body) {
if (request.body.mode === "urlencoded") {
let params = request.body.urlencoded
pwRequest.bodyParams = params ? params : []
for (let param of pwRequest.bodyParams) {
delete param.type
}
} else if (request.body.mode === "raw") {
pwRequest.rawInput = true
pwRequest.rawParams = request.body.raw
}
}
return pwRequest
},
hasFolder(item) {
return item.hasOwnProperty("item")
},
},
}
</script>

View File

@@ -0,0 +1,130 @@
<template>
<div>
<div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
draggable="true"
@dragstart="dragStart"
@dragover.stop
@dragleave="dragging = false"
@dragend="dragging = false"
>
<div>
<button
class="icon"
@click="!doc ? selectRequest() : {}"
v-tooltip="!doc ? $t('use_request') : ''"
>
<span :class="getRequestLabelColor(request.method)">{{ request.method }}</span>
<span>{{ request.name }}</span>
</button>
</div>
<v-popover>
<button class="tooltip-target icon" v-tooltip="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button
class="icon"
@click="
$emit('edit-request', {
collectionIndex,
folderIndex,
folderName,
request,
requestIndex,
})
"
v-close-popover
>
<i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span>
</button>
</div>
<div>
<button class="icon" @click="confirmRemove = true" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<SmartConfirmModal
:show="confirmRemove"
:title="$t('are_you_sure_remove_request')"
@hide-modal="confirmRemove = false"
@resolve="removeRequest"
/>
</div>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
request: Object,
collectionIndex: Number,
folderIndex: Number,
folderName: String,
requestIndex: Number,
doc: Boolean,
},
data() {
return {
dragging: false,
requestMethodLabels: {
get: "text-green-400",
post: "text-yellow-400",
put: "text-blue-400",
delete: "text-red-400",
default: "text-gray-400",
},
confirmRemove: false,
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
selectRequest() {
this.$store.commit("postwoman/selectRequest", { request: this.request })
},
dragStart({ dataTransfer }) {
this.dragging = !this.dragging
dataTransfer.setData("oldCollectionIndex", this.$props.collectionIndex)
dataTransfer.setData("oldFolderIndex", this.$props.folderIndex)
dataTransfer.setData("oldFolderName", this.$props.folderName)
dataTransfer.setData("requestIndex", this.$props.requestIndex)
},
removeRequest() {
this.$store.commit("postwoman/removeRequest", {
collectionIndex: this.$props.collectionIndex,
folderName: this.$props.folderName,
requestIndex: this.$props.requestIndex,
flag: "rest",
})
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
this.confirmRemove = false
this.syncCollections()
},
getRequestLabelColor(method) {
return this.requestMethodLabels[method.toLowerCase()] || this.requestMethodLabels.default
},
},
}
</script>

View File

@@ -0,0 +1,229 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("save_request_as") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("token_req_name") }}</label>
<input type="text" id="selectLabel" v-model="requestData.name" @keyup.enter="saveRequestAs" />
<ul>
<li>
<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>
</li>
</ul>
<label>{{ $t("folder") }}</label>
<SmartAutoComplete
:placeholder="$t('search')"
:source="folders"
:spellcheck="false"
v-model="requestData.folderName"
/>
<ul>
<li>
<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="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="saveRequestAs">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
show: Boolean,
editingRequest: Object,
},
data() {
return {
defaultRequestName: "Untitled Request",
requestData: {
name: undefined,
collectionIndex: undefined,
folderName: undefined,
requestIndex: undefined,
},
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
watch: {
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
// if user has chosen some folder, than selected other collection, which doesn't have any folders
// than `requestUpdateData.folderName` won't be reseted
this.$data.requestData.folderName = undefined
this.$data.requestData.requestIndex = undefined
},
"requestData.folderName": function resetRequestIndex() {
this.$data.requestData.requestIndex = undefined
},
editingRequest({ name }) {
this.$data.requestData.name = name || this.$data.defaultRequestName
},
},
computed: {
folders() {
const collections = this.$store.state.postwoman.collections
const collectionIndex = this.$data.requestData.collectionIndex
const userSelectedAnyCollection = collectionIndex !== undefined
if (!userSelectedAnyCollection) return []
const noCollectionAvailable = collections[collectionIndex] !== undefined
if (!noCollectionAvailable) return []
return getFolderNames(collections[collectionIndex].folders, [])
},
requests() {
const collections = this.$store.state.postwoman.collections
const collectionIndex = this.$data.requestData.collectionIndex
const folderName = this.$data.requestData.folderName
const userSelectedAnyCollection = collectionIndex !== undefined
if (!userSelectedAnyCollection) {
return []
}
const userSelectedAnyFolder = folderName !== undefined && folderName !== ""
if (userSelectedAnyFolder) {
const collection = collections[collectionIndex]
const folder = findFolder(folderName, collection)
return folder.requests
} else {
const collection = collections[collectionIndex]
const noCollectionAvailable = collection !== undefined
if (!noCollectionAvailable) {
return []
}
return collection.requests
}
},
},
methods: {
syncCollections() {
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
saveRequestAs() {
const userDidntSpecifyCollection = this.$data.requestData.collectionIndex === undefined
if (userDidntSpecifyCollection) {
this.$toast.error(this.$t("select_collection"), {
icon: "error",
})
return
}
if (this.$data.requestData.name.length === 0) {
this.$toast.error(this.$t("empty_req_name"), {
icon: "error",
})
return
}
const requestUpdated = {
...this.$props.editingRequest,
name: this.$data.requestData.name,
collection: this.$data.requestData.collectionIndex,
}
this.$store.commit("postwoman/saveRequestAs", {
request: requestUpdated,
collectionIndex: this.$data.requestData.collectionIndex,
folderName: this.$data.requestData.folderName,
requestIndex: this.$data.requestData.requestIndex,
flag: "rest",
})
this.hideModal()
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
},
},
}
function getFolderNames(folders, namesList) {
if (folders.length) {
folders.forEach((folder) => {
namesList.push(folder.name)
if (folder.folders && folder.folders.length) {
getFolderNames(folder.folders, namesList)
}
})
}
return namesList
}
function findFolder(folderName, currentFolder) {
let selectedFolder
let result
if (folderName === currentFolder.name) {
return currentFolder
}
for (let i = 0; i < currentFolder.folders.length; i++) {
selectedFolder = currentFolder.folders[i]
result = findFolder(folderName, selectedFolder)
if (result !== false) {
return result
}
}
return false
}
</script>

View File

@@ -1,83 +0,0 @@
<template>
<modal v-if="show" @close="show = false">
<div slot="header">
<ul>
<li>
<div class="flex-wrap">
<h3 class="title">{{ $t("new_folder") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</li>
</ul>
</div>
<div slot="body">
<ul>
<li>
<input
type="text"
v-model="name"
:placeholder="$t('my_new_folder')"
@keyup.enter="addNewFolder"
/>
</li>
</ul>
</div>
<div slot="footer">
<div class="flex-wrap">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="addNewFolder">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</modal>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
show: Boolean,
collection: Object,
collectionIndex: Number,
},
components: {
modal: () => import("~/components/ui/modal"),
},
data() {
return {
name: undefined,
}
},
methods: {
addNewFolder() {
this.$store.commit("postwoman/addNewFolder", {
folder: { name: this.$data.name },
collectionIndex: this.$props.collectionIndex,
})
this.hideModal()
this.syncCollections()
},
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -1,141 +0,0 @@
<template>
<div>
<div class="flex-wrap">
<button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren">arrow_right</i>
<i class="material-icons" v-show="showChildren">arrow_drop_down</i>
<i class="material-icons">folder</i>
<span>{{ collection.name }}</span>
</button>
<div>
<button
v-if="doc"
class="icon"
@click="$emit('select-collection')"
v-tooltip.left="$t('import')"
>
<i class="material-icons">topic</i>
</button>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="$emit('add-folder')" v-close-popover>
<i class="material-icons">create_new_folder</i>
<span>{{ $t("new_folder") }}</span>
</button>
</div>
<div>
<button class="icon" @click="$emit('edit-collection')" v-close-popover>
<i class="material-icons">create</i>
<span>{{ $t("edit") }}</span>
</button>
</div>
<div>
<button class="icon" @click="removeCollection" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
</div>
<div v-show="showChildren">
<ul>
<li v-for="(folder, index) in collection.folders" :key="folder.name">
<folder
:folder="folder"
:folderIndex="index"
:collection-index="collectionIndex"
:doc="doc"
@edit-folder="editFolder(collectionIndex, folder, index)"
@edit-request="$emit('edit-request', $event)"
/>
</li>
<li v-if="collection.folders.length === 0 && collection.requests.length === 0">
<label>{{ $t("collection_empty") }}</label>
</li>
</ul>
<ul>
<li v-for="(request, index) in collection.requests" :key="index">
<request
:request="request"
:collection-index="collectionIndex"
:folder-index="-1"
:request-index="index"
:doc="doc"
@edit-request="
$emit('edit-request', {
request,
collectionIndex,
folderIndex: undefined,
requestIndex: index,
})
"
/>
</li>
</ul>
</div>
</div>
</template>
<style scoped lang="scss">
ul {
display: flex;
flex-direction: column;
}
ul li {
display: flex;
margin-left: 32px;
border-left: 1px solid var(--brd-color);
}
</style>
<script>
import { fb } from "~/helpers/fb"
export default {
components: {
folder: () => import("./folder"),
request: () => import("./request"),
},
props: {
collectionIndex: Number,
collection: Object,
doc: Boolean,
},
data() {
return {
showChildren: false,
selectedFolder: {},
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
toggleShowChildren() {
this.showChildren = !this.showChildren
},
removeCollection() {
if (!confirm("Are you sure you want to remove this Collection?")) return
this.$store.commit("postwoman/removeCollection", {
collectionIndex: this.collectionIndex,
})
this.syncCollections()
},
editFolder(collectionIndex, folder, folderIndex) {
this.$emit("edit-folder", { collectionIndex, folder, folderIndex })
},
},
}
</script>

View File

@@ -1,149 +0,0 @@
<template>
<modal v-if="show" @close="hideModal">
<div slot="header">
<ul>
<li>
<div class="flex-wrap">
<h3 class="title">{{ $t("edit_request") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</li>
</ul>
</div>
<div slot="body">
<ul>
<li>
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="requestUpdateData.name"
@keyup.enter="saveRequest"
:placeholder="request.name"
/>
<label for="selectCollection">{{ $t("collection") }}</label>
<span class="select-wrapper">
<select type="text" id="selectCollection" v-model="requestUpdateData.collectionIndex">
<option :key="undefined" :value="undefined" hidden disabled selected>{{
$t("current_collection")
}}</option>
<option
v-for="(collection, index) in $store.state.postwoman.collections"
:key="index"
:value="index"
>
{{ collection.name }}
</option>
</select>
</span>
<label for="selectFolder">{{ $t("folder") }}</label>
<span class="select-wrapper">
<select type="text" id="selectFolder" v-model="requestUpdateData.folderIndex">
<option :key="undefined" :value="undefined">/</option>
<option v-for="(folder, index) in folders" :key="index" :value="index">
{{ folder.name }}
</option>
</select>
</span>
</li>
</ul>
</div>
<div slot="footer">
<div class="flex-wrap">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="saveRequest">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</modal>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
show: Boolean,
collectionIndex: Number,
folderIndex: Number,
request: Object,
requestIndex: Number,
},
components: {
modal: () => import("~/components/ui/modal"),
},
data() {
return {
requestUpdateData: {
name: undefined,
collectionIndex: undefined,
folderIndex: undefined,
},
}
},
watch: {
"requestUpdateData.collectionIndex": function resetFolderIndex() {
// if user choosen some folder, than selected other collection, which doesn't have any folders
// than `requestUpdateData.folderIndex` won't be reseted
this.$data.requestUpdateData.folderIndex = undefined
},
},
computed: {
folders() {
const userSelectedAnyCollection = this.$data.requestUpdateData.collectionIndex !== undefined
if (!userSelectedAnyCollection) return []
return this.$store.state.postwoman.collections[this.$data.requestUpdateData.collectionIndex]
.folders
},
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
saveRequest() {
const userSelectedAnyCollection = this.$data.requestUpdateData.collectionIndex !== undefined
const requestUpdated = {
...this.$props.request,
name: this.$data.requestUpdateData.name || this.$props.request.name,
collection: userSelectedAnyCollection
? this.$data.requestUpdateData.collectionIndex
: this.$props.collectionIndex,
folder: this.$data.requestUpdateData.folderIndex,
}
// pass data separately to don't depend on request's collection, folder fields
// probably, they should be deprecated because they don't describe request itself
this.$store.commit("postwoman/editRequest", {
requestOldCollectionIndex: this.$props.collectionIndex,
requestOldFolderIndex: this.$props.folderIndex,
requestOldIndex: this.$props.requestIndex,
requestNew: requestUpdated,
requestNewCollectionIndex: requestUpdated.collection,
requestNewFolderIndex: requestUpdated.folder,
})
this.hideModal()
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -1,118 +0,0 @@
<template>
<div>
<div class="flex-wrap">
<div>
<button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren">arrow_right</i>
<i class="material-icons" v-show="showChildren">arrow_drop_down</i>
<i class="material-icons">folder_open</i>
<span>{{ folder.name }}</span>
</button>
</div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="editFolder" v-close-popover>
<i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span>
</button>
</div>
<div>
<button class="icon" @click="removeFolder" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<div v-show="showChildren">
<ul>
<li v-for="(request, index) in folder.requests" :key="index">
<request
:request="request"
:collection-index="collectionIndex"
:folder-index="folderIndex"
:request-index="index"
:doc="doc"
@edit-request="
$emit('edit-request', {
request,
collectionIndex,
folderIndex,
requestIndex: index,
})
"
/>
</li>
<li v-if="folder.requests.length === 0">
<label>{{ $t("folder_empty") }}</label>
</li>
</ul>
</div>
</div>
</template>
<style scoped lang="scss">
ul {
display: flex;
flex-direction: column;
}
ul li {
display: flex;
margin-left: 32px;
border-left: 1px solid var(--brd-color);
}
</style>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
folder: Object,
collectionIndex: Number,
folderIndex: Number,
doc: Boolean,
},
components: {
request: () => import("./request"),
},
data() {
return {
showChildren: false,
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
toggleShowChildren() {
this.showChildren = !this.showChildren
},
selectRequest(request) {
this.$store.commit("postwoman/selectRequest", { request })
},
removeFolder() {
if (!confirm("Are you sure you want to remove this folder?")) return
this.$store.commit("postwoman/removeFolder", {
collectionIndex: this.collectionIndex,
folderIndex: this.folderIndex,
})
this.syncCollections()
},
editFolder() {
this.$emit("edit-folder")
},
},
}
</script>

View File

@@ -1,33 +1,27 @@
<template>
<modal v-if="show" @close="hideModal">
<SmartModal 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 class="row-wrapper">
<h3 class="title">{{ $t("new_collection") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body">
<ul>
<li>
<input
type="text"
v-model="name"
:placeholder="$t('my_new_collection')"
@keyup.enter="addNewCollection"
/>
</li>
</ul>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="$t('my_new_collection')"
@keyup.enter="addNewCollection"
/>
</div>
<div slot="footer">
<div class="flex-wrap">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
@@ -39,7 +33,7 @@
</span>
</div>
</div>
</modal>
</SmartModal>
</template>
<script>
@@ -49,9 +43,6 @@ export default {
props: {
show: Boolean,
},
components: {
modal: () => import("~/components/ui/modal"),
},
data() {
return {
name: undefined,
@@ -59,9 +50,12 @@ export default {
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
@@ -72,6 +66,7 @@ export default {
}
this.$store.commit("postwoman/addNewCollection", {
name: this.$data.name,
flag: "graphql",
})
this.$emit("hide-modal")
this.syncCollections()

View File

@@ -0,0 +1,65 @@
<template>
<SmartModal v-if="show" @close="show = false">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("new_folder") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="$t('my_new_folder')"
@keyup.enter="addFolder"
/>
</div>
<div slot="footer">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="addFolder">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
export default {
props: {
show: Boolean,
folder: Object,
folderPath: String,
collectionIndex: Number,
},
data() {
return {
name: undefined,
}
},
methods: {
addFolder() {
this.$emit("add-folder", {
name: this.name,
folder: this.folder,
path: this.folderPath || `${this.collectionIndex}`,
})
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -0,0 +1,178 @@
<template>
<div>
<div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
@dragover.prevent
@drop.prevent="dropEvent"
@dragover="dragging = true"
@drop="dragging = false"
@dragleave="dragging = false"
@dragend="dragging = false"
>
<button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i>
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i>
<i class="material-icons">folder</i>
<span>{{ collection.name }}</span>
</button>
<div>
<button
v-if="doc"
class="icon"
@click="$emit('select-collection')"
v-tooltip.left="$t('import')"
>
<i class="material-icons">topic</i>
</button>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button
class="icon"
@click="$emit('add-folder', { folder: collection, path: `${collectionIndex}` })"
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="confirmRemove = true" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
</div>
<div v-show="showChildren || isFiltered">
<ul class="flex-col">
<li
v-for="(folder, index) in collection.folders"
:key="folder.name"
class="ml-8 border-l border-brdColor"
>
<CollectionsGraphqlFolder
:folder="folder"
:folder-index="index"
:folder-path="`${collectionIndex}/${index}`"
:collection-index="collectionIndex"
:doc="doc"
:isFiltered="isFiltered"
@add-folder="$emit('add-folder', $event)"
@edit-folder="$emit('edit-folder', $event)"
@edit-request="$emit('edit-request', $event)"
/>
</li>
</ul>
<ul class="flex-col">
<li
v-for="(request, index) in collection.requests"
:key="index"
class="ml-8 border-l border-brdColor"
>
<CollectionsGraphqlRequest
:request="request"
:collection-index="collectionIndex"
:folder-index="-1"
:folder-name="collection.name"
:request-index="index"
:doc="doc"
@edit-request="$emit('edit-request', $event)"
/>
</li>
</ul>
<ul>
<li
v-if="collection.folders.length === 0 && collection.requests.length === 0"
class="flex ml-8 border-l border-brdColor"
>
<p class="info">
<i class="material-icons">not_interested</i> {{ $t("collection_empty") }}
</p>
</li>
</ul>
</div>
<SmartConfirmModal
:show="confirmRemove"
:title="$t('are_you_sure_remove_collection')"
@hide-modal="confirmRemove = false"
@resolve="removeCollection"
/>
</div>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
collectionIndex: Number,
collection: Object,
doc: Boolean,
isFiltered: Boolean,
},
data() {
return {
showChildren: false,
dragging: false,
selectedFolder: {},
confirmRemove: false,
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
toggleShowChildren() {
this.showChildren = !this.showChildren
},
removeCollection() {
this.$store.commit("postwoman/removeCollection", {
collectionIndex: this.collectionIndex,
flag: "graphql",
})
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
this.syncCollections()
},
dropEvent({ dataTransfer }) {
this.dragging = !this.dragging
const oldCollectionIndex = dataTransfer.getData("oldCollectionIndex")
const oldFolderIndex = dataTransfer.getData("oldFolderIndex")
const oldFolderName = dataTransfer.getData("oldFolderName")
const requestIndex = dataTransfer.getData("requestIndex")
const flag = "graphql"
this.$store.commit("postwoman/moveRequest", {
oldCollectionIndex,
newCollectionIndex: this.$props.collectionIndex,
newFolderIndex: -1,
newFolderName: this.$props.collection.name,
oldFolderIndex,
oldFolderName,
requestIndex,
flag,
})
this.syncCollections()
},
},
}
</script>

View File

@@ -1,33 +1,27 @@
<template>
<modal v-if="show" @close="hideModal">
<SmartModal 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 class="row-wrapper">
<h3 class="title">{{ $t("edit_collection") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body">
<ul>
<li>
<input
type="text"
v-model="name"
:placeholder="editingCollection.name"
@keyup.enter="saveCollection"
/>
</li>
</ul>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="editingCollection.name"
@keyup.enter="saveCollection"
/>
</div>
<div slot="footer">
<div class="flex-wrap">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
@@ -39,7 +33,7 @@
</span>
</div>
</div>
</modal>
</SmartModal>
</template>
<script>
@@ -51,9 +45,6 @@ export default {
editingCollection: Object,
editingCollectionIndex: Number,
},
components: {
modal: () => import("~/components/ui/modal"),
},
data() {
return {
name: undefined,
@@ -61,9 +52,12 @@ export default {
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
@@ -79,6 +73,7 @@ export default {
this.$store.commit("postwoman/editCollection", {
collection: collectionUpdated,
collectionIndex: this.$props.editingCollectionIndex,
flag: "graphql",
})
this.$emit("hide-modal")
this.syncCollections()

View File

@@ -1,28 +1,27 @@
<template>
<modal v-if="show" @close="show = false">
<SmartModal 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 class="row-wrapper">
<h3 class="title">{{ $t("edit_folder") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body">
<ul>
<li>
<input type="text" v-model="name" :placeholder="folder.name" @keyup.enter="editFolder" />
</li>
</ul>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="folder.name"
@keyup.enter="editFolder"
/>
</div>
<div slot="footer">
<div class="flex-wrap">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
@@ -34,7 +33,7 @@
</span>
</div>
</div>
</modal>
</SmartModal>
</template>
<script>
@@ -43,14 +42,10 @@ import { fb } from "~/helpers/fb"
export default {
props: {
show: Boolean,
collection: Object,
collectionIndex: Number,
folder: Object,
folderIndex: Number,
},
components: {
modal: () => import("~/components/ui/modal"),
},
data() {
return {
name: undefined,
@@ -58,9 +53,12 @@ export default {
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
@@ -69,6 +67,8 @@ export default {
collectionIndex: this.$props.collectionIndex,
folder: { ...this.$props.folder, name: this.$data.name },
folderIndex: this.$props.folderIndex,
folderName: this.$props.folder.name,
flag: "graphql",
})
this.hideModal()
this.syncCollections()

View File

@@ -0,0 +1,92 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("edit_request") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="requestUpdateData.name"
@keyup.enter="saveRequest"
:placeholder="request.name"
/>
</div>
<div slot="footer">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="saveRequest">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
show: Boolean,
collectionIndex: Number,
folderIndex: Number,
folderName: String,
request: Object,
requestIndex: Number,
},
data() {
return {
requestUpdateData: {
name: undefined,
},
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
saveRequest() {
const requestUpdated = {
...this.$props.request,
name: this.$data.requestUpdateData.name || this.$props.request.name,
}
this.$store.commit("postwoman/editRequest", {
requestCollectionIndex: this.$props.collectionIndex,
requestFolderName: this.$props.folderName,
requestFolderIndex: this.$props.folderIndex,
requestNew: requestUpdated,
requestIndex: this.$props.requestIndex,
flag: "graphql",
})
this.hideModal()
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -0,0 +1,180 @@
<template>
<div>
<div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
@dragover.prevent
@drop.prevent="dropEvent"
@dragover="dragging = true"
@drop="dragging = false"
@dragleave="dragging = false"
@dragend="dragging = false"
>
<div>
<button class="icon" @click="toggleShowChildren">
<i class="material-icons" v-show="!showChildren && !isFiltered">arrow_right</i>
<i class="material-icons" v-show="showChildren || isFiltered">arrow_drop_down</i>
<i class="material-icons">folder_open</i>
<span>{{ folder.name }}</span>
</button>
</div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button
class="icon"
@click="$emit('add-folder', { folder, path: folderPath })"
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-folder', { folder, folderIndex, collectionIndex })"
v-close-popover
>
<i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span>
</button>
</div>
<div>
<button class="icon" @click="confirmRemove = true" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<div v-show="showChildren || isFiltered">
<ul v-if="folder.folders && folder.folders.length" class="flex-col">
<li
v-for="(subFolder, subFolderIndex) in folder.folders"
:key="subFolder.name"
class="ml-8 border-l border-brdColor"
>
<CollectionsGraphqlFolder
:folder="subFolder"
:folder-index="subFolderIndex"
:collection-index="collectionIndex"
:doc="doc"
:folder-path="`${folderPath}/${subFolderIndex}`"
@add-folder="$emit('add-folder', $event)"
@edit-folder="$emit('edit-folder', $event)"
@edit-request="$emit('edit-request', $event)"
/>
</li>
</ul>
<ul class="flex-col">
<li
v-for="(request, index) in folder.requests"
:key="index"
class="flex ml-8 border-l border-brdColor"
>
<CollectionsGraphqlRequest
:request="request"
:collection-index="collectionIndex"
:folder-index="folderIndex"
:folder-name="folder.name"
:request-index="index"
:doc="doc"
@edit-request="$emit('edit-request', $event)"
/>
</li>
</ul>
<ul
v-if="
folder.folders &&
folder.folders.length === 0 &&
folder.requests &&
folder.requests.length === 0
"
>
<li class="flex ml-8 border-l border-brdColor">
<p class="info"><i class="material-icons">not_interested</i> {{ $t("folder_empty") }}</p>
</li>
</ul>
</div>
<SmartConfirmModal
:show="confirmRemove"
:title="$t('are_you_sure_remove_folder')"
@hide-modal="confirmRemove = false"
@resolve="removeFolder"
/>
</div>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
name: "folder",
props: {
folder: Object,
folderIndex: Number,
collectionIndex: Number,
folderPath: String,
doc: Boolean,
isFiltered: Boolean,
},
data() {
return {
showChildren: false,
dragging: false,
confirmRemove: false,
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
toggleShowChildren() {
this.showChildren = !this.showChildren
},
removeFolder() {
this.$store.commit("postwoman/removeFolder", {
collectionIndex: this.$props.collectionIndex,
folderName: this.$props.folder.name,
folderIndex: this.$props.folderIndex,
flag: "graphql",
})
this.syncCollections()
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
},
dropEvent({ dataTransfer }) {
this.dragging = !this.dragging
const oldCollectionIndex = dataTransfer.getData("oldCollectionIndex")
const oldFolderIndex = dataTransfer.getData("oldFolderIndex")
const oldFolderName = dataTransfer.getData("oldFolderName")
const requestIndex = dataTransfer.getData("requestIndex")
const flag = "graphql"
this.$store.commit("postwoman/moveRequest", {
oldCollectionIndex,
newCollectionIndex: this.$props.collectionIndex,
newFolderIndex: this.$props.folderIndex,
newFolderName: this.$props.folder.name,
oldFolderIndex,
oldFolderName,
requestIndex,
flag,
})
this.syncCollections()
},
},
}
</script>

View File

@@ -0,0 +1,393 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("import_export") }} {{ $t("collections") }}</h3>
<div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="readCollectionGist" v-close-popover>
<i class="material-icons">assignment_returned</i>
<span>{{ $t("import_from_gist") }}</span>
</button>
</div>
<div
v-tooltip.bottom="{
content: !fb.currentUser
? $t('login_with_github_to') + $t('create_secret_gist')
: fb.currentUser.provider !== 'github.com'
? $t('login_with_github_to') + $t('create_secret_gist')
: null,
}"
>
<button
:disabled="
!fb.currentUser ? true : fb.currentUser.provider !== 'github.com' ? true : false
"
class="icon"
@click="createCollectionGist"
v-close-popover
>
<i class="material-icons">assignment_turned_in</i>
<span>{{ $t("create_secret_gist") }}</span>
</button>
</div>
</template>
</v-popover>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<div class="flex flex-col items-start p-2">
<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>
<div v-if="showJsonCode" class="row-wrapper">
<textarea v-model="collectionJson" rows="8" readonly></textarea>
</div>
</div>
<div slot="footer">
<div class="row-wrapper">
<span>
<SmartToggle :on="showJsonCode" @change="showJsonCode = $event">
{{ $t("show_code") }}
</SmartToggle>
</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>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
data() {
return {
fb,
showJsonCode: false,
}
},
props: {
show: Boolean,
},
computed: {
collectionJson() {
return JSON.stringify(this.$store.state.postwoman.collectionsGraphql, null, 2)
},
},
methods: {
async createCollectionGist() {
await this.$axios
.$post(
"https://api.github.com/gists",
{
files: {
"hoppscotch-collections.json": {
content: this.collectionJson,
},
},
},
{
headers: {
Authorization: `token ${fb.currentUser.accessToken}`,
Accept: "application/vnd.github.v3+json",
},
}
)
.then(({ html_url }) => {
this.$toast.success(this.$t("gist_created"), {
icon: "done",
})
window.open(html_url)
})
.catch((error) => {
this.$toast.error(this.$t("something_went_wrong"), {
icon: "error",
})
console.log(error)
})
},
async readCollectionGist() {
let gist = prompt(this.$t("enter_gist_url"))
if (!gist) return
await this.$axios
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
headers: {
Accept: "application/vnd.github.v3+json",
},
})
.then(({ files }) => {
let collections = JSON.parse(Object.values(files)[0].content)
this.$store.commit("postwoman/replaceCollections", { data: collections, flag: "graphql" })
this.fileImported()
this.syncToFBCollections()
})
.catch((error) => {
this.failedImport()
console.log(error)
})
},
hideModal() {
this.$emit("hide-modal")
},
openDialogChooseFileToReplaceWith() {
this.$refs.inputChooseFileToReplaceWith.click()
},
openDialogChooseFileToImportFrom() {
this.$refs.inputChooseFileToImportFrom.click()
},
replaceWithJSON() {
let reader = new FileReader()
reader.onload = ({ target }) => {
let content = 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 {
this.failedImport()
return
}
this.$store.commit("postwoman/replaceCollections", { data: collections, flag: "graphql" })
this.fileImported()
this.syncToFBCollections()
}
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
this.$refs.inputChooseFileToReplaceWith.value = ""
},
importFromJSON() {
let reader = new FileReader()
reader.onload = ({ target }) => {
let content = 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")) {
//replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
collections = JSON.parse(content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>"))
collections = [this.parsePostmanCollection(collections)]
} else {
this.failedImport()
return
}
this.$store.commit("postwoman/importCollections", { data: collections, flag: "graphql" })
this.fileImported()
this.syncToFBCollections()
}
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
this.$refs.inputChooseFileToImportFrom.value = ""
},
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 = "hoppscotch-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", {
data: fb.currentGraphqlCollections,
flag: "graphql",
})
this.fileImported()
},
syncToFBCollections() {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
fileImported() {
this.$toast.info(this.$t("file_imported"), {
icon: "folder_shared",
})
},
failedImport() {
this.$toast.error(this.$t("import_failed"), {
icon: "error",
})
},
parsePostmanCollection({ info, name, item }) {
let postwomanCollection = {
name: "",
folders: [],
requests: [],
}
postwomanCollection.name = info ? info.name : name
if (item && item.length > 0) {
for (let collectionItem of item) {
if (collectionItem.request) {
if (postwomanCollection.hasOwnProperty("folders")) {
postwomanCollection.name = info ? info.name : name
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
} else {
postwomanCollection.name = name ? name : ""
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
}
} else if (this.hasFolder(collectionItem)) {
postwomanCollection.folders.push(this.parsePostmanCollection(collectionItem))
} else {
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
}
}
}
return postwomanCollection
},
parsePostmanRequest({ name, request }) {
let pwRequest = {
url: "",
path: "",
method: "",
auth: "",
httpUser: "",
httpPassword: "",
passwordFieldType: "password",
bearerToken: "",
headers: [],
params: [],
bodyParams: [],
rawParams: "",
rawInput: false,
contentType: "",
requestType: "",
name: "",
}
pwRequest.name = name
let requestObjectUrl = request.url.raw.match(/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/)
if (requestObjectUrl) {
pwRequest.url = requestObjectUrl[1]
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
}
pwRequest.method = request.method
let itemAuth = request.auth ? request.auth : ""
let authType = itemAuth ? itemAuth.type : ""
if (authType === "basic") {
pwRequest.auth = "Basic Auth"
pwRequest.httpUser =
itemAuth.basic[0].key === "username" ? itemAuth.basic[0].value : itemAuth.basic[1].value
pwRequest.httpPassword =
itemAuth.basic[0].key === "password" ? itemAuth.basic[0].value : itemAuth.basic[1].value
} else if (authType === "oauth2") {
pwRequest.auth = "OAuth 2.0"
pwRequest.bearerToken =
itemAuth.oauth2[0].key === "accessToken"
? itemAuth.oauth2[0].value
: itemAuth.oauth2[1].value
} else if (authType === "bearer") {
pwRequest.auth = "Bearer Token"
pwRequest.bearerToken = itemAuth.bearer[0].value
}
let requestObjectHeaders = request.header
if (requestObjectHeaders) {
pwRequest.headers = requestObjectHeaders
for (let header of pwRequest.headers) {
delete header.name
delete header.type
}
}
let requestObjectParams = request.url.query
if (requestObjectParams) {
pwRequest.params = requestObjectParams
for (let param of pwRequest.params) {
delete param.disabled
}
}
if (request.body) {
if (request.body.mode === "urlencoded") {
let params = request.body.urlencoded
pwRequest.bodyParams = params ? params : []
for (let param of pwRequest.bodyParams) {
delete param.type
}
} else if (request.body.mode === "raw") {
pwRequest.rawInput = true
pwRequest.rawParams = request.body.raw
}
}
return pwRequest
},
hasFolder(item) {
return item.hasOwnProperty("item")
},
},
}
</script>

View File

@@ -0,0 +1,115 @@
<template>
<div>
<div
:class="['row-wrapper transition duration-150 ease-in-out', { 'bg-bgDarkColor': dragging }]"
draggable="true"
@dragstart="dragStart"
@dragover.stop
@dragleave="dragging = false"
@dragend="dragging = false"
>
<div>
<button
class="icon"
@click="!doc ? selectRequest() : {}"
v-tooltip="!doc ? $t('use_request') : ''"
>
<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', {
collectionIndex,
folderIndex,
folderName,
request,
requestIndex,
})
"
v-close-popover
>
<i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span>
</button>
</div>
<div>
<button class="icon" @click="confirmRemove = true" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<SmartConfirmModal
:show="confirmRemove"
:title="$t('are_you_sure_remove_request')"
@hide-modal="confirmRemove = false"
@resolve="removeRequest"
/>
</div>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
request: Object,
collectionIndex: Number,
folderIndex: Number,
folderName: String,
requestIndex: Number,
doc: Boolean,
},
data() {
return {
dragging: false,
confirmRemove: false,
}
},
methods: {
syncCollections() {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
selectRequest() {
this.$store.commit("postwoman/selectGraphqlRequest", { request: this.request })
},
dragStart({ dataTransfer }) {
this.dragging = !this.dragging
dataTransfer.setData("oldCollectionIndex", this.$props.collectionIndex)
dataTransfer.setData("oldFolderIndex", this.$props.folderIndex)
dataTransfer.setData("oldFolderName", this.$props.folderName)
dataTransfer.setData("requestIndex", this.$props.requestIndex)
},
removeRequest() {
this.$store.commit("postwoman/removeRequest", {
collectionIndex: this.$props.collectionIndex,
folderName: this.$props.folderName,
requestIndex: this.$props.requestIndex,
flag: "graphql",
})
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
this.confirmRemove = false
this.syncCollections()
},
},
}
</script>

View File

@@ -0,0 +1,225 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("save_request_as") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("token_req_name") }}</label>
<input type="text" id="selectLabel" v-model="requestData.name" @keyup.enter="saveRequestAs" />
<ul>
<li>
<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.collectionsGraphql"
:key="index"
:value="index"
>
{{ collection.name }}
</option>
</select>
</span>
</li>
</ul>
<label>{{ $t("folder") }}</label>
<SmartAutoComplete
:placeholder="$t('search')"
:source="folders"
:spellcheck="false"
v-model="requestData.folderName"
/>
<ul>
<li>
<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="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="saveRequestAs">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
show: Boolean,
editingRequest: Object,
},
data() {
return {
defaultRequestName: "Untitled Request",
requestData: {
name: undefined,
collectionIndex: undefined,
folderName: undefined,
requestIndex: undefined,
},
}
},
watch: {
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
// if user has chosen some folder, than selected other collection, which doesn't have any folders
// than `requestUpdateData.folderName` won't be reseted
this.$data.requestData.folderName = undefined
this.$data.requestData.requestIndex = undefined
},
"requestData.folderName": function resetRequestIndex() {
this.$data.requestData.requestIndex = undefined
},
editingRequest({ name }) {
this.$data.requestData.name = name || this.$data.defaultRequestName
},
},
computed: {
folders() {
const collections = this.$store.state.postwoman.collectionsGraphql
const collectionIndex = this.$data.requestData.collectionIndex
const userSelectedAnyCollection = collectionIndex !== undefined
if (!userSelectedAnyCollection) return []
const noCollectionAvailable = collections[collectionIndex] !== undefined
if (!noCollectionAvailable) return []
return getFolderNames(collections[collectionIndex].folders, [])
},
requests() {
const collections = this.$store.state.postwoman.collectionsGraphql
const collectionIndex = this.$data.requestData.collectionIndex
const folderName = this.$data.requestData.folderName
const userSelectedAnyCollection = collectionIndex !== undefined
if (!userSelectedAnyCollection) {
return []
}
const userSelectedAnyFolder = folderName !== undefined && folderName !== ""
if (userSelectedAnyFolder) {
const collection = collections[collectionIndex]
const folder = findFolder(folderName, collection)
return folder.requests
} else {
const collection = collections[collectionIndex]
const noCollectionAvailable = collection !== undefined
if (!noCollectionAvailable) {
return []
}
return collection.requests
}
},
},
methods: {
syncCollections() {
if (fb.currentUser !== null && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
saveRequestAs() {
const userDidntSpecifyCollection = this.$data.requestData.collectionIndex === undefined
if (userDidntSpecifyCollection) {
this.$toast.error(this.$t("select_collection"), {
icon: "error",
})
return
}
if (this.$data.requestData.name.length === 0) {
this.$toast.error(this.$t("empty_req_name"), {
icon: "error",
})
return
}
const requestUpdated = {
...this.$props.editingRequest,
name: this.$data.requestData.name,
collection: this.$data.requestData.collectionIndex,
}
this.$store.commit("postwoman/saveRequestAs", {
request: requestUpdated,
collectionIndex: this.$data.requestData.collectionIndex,
folderName: this.$data.requestData.folderName,
requestIndex: this.$data.requestData.requestIndex,
flag: "graphql",
})
this.hideModal()
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
},
},
}
function getFolderNames(folders, namesList) {
if (folders.length) {
folders.forEach((folder) => {
namesList.push(folder.name)
if (folder.folders && folder.folders.length) {
getFolderNames(folder.folders, namesList)
}
})
}
return namesList
}
function findFolder(folderName, currentFolder) {
let selectedFolder
let result
if (folderName === currentFolder.name) {
return currentFolder
}
for (let i = 0; i < currentFolder.folders.length; i++) {
selectedFolder = currentFolder.folders[i]
result = findFolder(folderName, selectedFolder)
if (result !== false) {
return result
}
}
return false
}
</script>

View File

@@ -0,0 +1,261 @@
<template>
<AppSection class="yellow" :label="$t('collections')" ref="collections" no-legend>
<div class="show-on-large-screen">
<input
aria-label="Search"
type="search"
:placeholder="$t('search')"
v-model="filterText"
class="rounded-t-lg"
/>
</div>
<CollectionsGraphqlAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
<CollectionsGraphqlEdit
:show="showModalEdit"
:editing-collection="editingCollection"
:editing-collection-index="editingCollectionIndex"
@hide-modal="displayModalEdit(false)"
/>
<CollectionsGraphqlAddFolder
:show="showModalAddFolder"
:folder="editingFolder"
:folder-path="editingFolderPath"
@add-folder="onAddFolder($event)"
@hide-modal="displayModalAddFolder(false)"
/>
<CollectionsGraphqlEditFolder
:show="showModalEditFolder"
:collection-index="editingCollectionIndex"
:folder="editingFolder"
:folder-index="editingFolderIndex"
@hide-modal="displayModalEditFolder(false)"
/>
<CollectionsGraphqlEditRequest
:show="showModalEditRequest"
:collection-index="editingCollectionIndex"
:folder-index="editingFolderIndex"
:folder-name="editingFolderName"
:request="editingRequest"
:request-index="editingRequestIndex"
@hide-modal="displayModalEditRequest(false)"
/>
<CollectionsGraphqlImportExport
:show="showModalImportExport"
@hide-modal="displayModalImportExport(false)"
/>
<div class="border-b row-wrapper border-brdColor">
<button class="icon" @click="displayModalAdd(true)">
<i class="material-icons">add</i>
<span>{{ $t("new") }}</span>
</button>
<button class="icon" @click="displayModalImportExport(true)">
{{ $t("import_export") }}
</button>
</div>
<p v-if="collections.length === 0" class="info">
<i class="material-icons">help_outline</i> {{ $t("create_new_collection") }}
</p>
<div class="virtual-list">
<ul class="flex-col">
<li v-for="(collection, index) in filteredCollections" :key="collection.name">
<CollectionsGraphqlCollection
:name="collection.name"
:collection-index="index"
:collection="collection"
:doc="doc"
:isFiltered="filterText.length > 0"
@edit-collection="editCollection(collection, index)"
@add-folder="addFolder($event)"
@edit-folder="editFolder($event)"
@edit-request="editRequest($event)"
@select-collection="$emit('use-collection', collection)"
/>
</li>
</ul>
</div>
<p v-if="filterText && filteredCollections.length === 0" class="info">
<i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{ filterText }}"
</p>
</AppSection>
</template>
<style scoped lang="scss">
.virtual-list {
max-height: calc(100vh - 270px);
}
</style>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
doc: Boolean,
},
data() {
return {
showModalAdd: false,
showModalEdit: false,
showModalImportExport: false,
showModalAddFolder: false,
showModalEditFolder: false,
showModalEditRequest: false,
editingCollection: undefined,
editingCollectionIndex: undefined,
editingFolder: undefined,
editingFolderName: undefined,
editingFolderIndex: undefined,
editingFolderPath: undefined,
editingRequest: undefined,
editingRequestIndex: undefined,
filterText: "",
}
},
computed: {
collections() {
return fb.currentUser !== null
? fb.currentGraphqlCollections
: this.$store.state.postwoman.collectionsGraphql
},
filteredCollections() {
const collections =
fb.currentUser !== null
? fb.currentGraphqlCollections
: this.$store.state.postwoman.collectionsGraphql
if (!this.filterText) return collections
const filterText = this.filterText.toLowerCase()
const filteredCollections = []
for (let collection of collections) {
const filteredRequests = []
const filteredFolders = []
for (let request of collection.requests) {
if (request.name.toLowerCase().includes(filterText)) filteredRequests.push(request)
}
for (let folder of collection.folders) {
const filteredFolderRequests = []
for (let request of folder.requests) {
if (request.name.toLowerCase().includes(filterText))
filteredFolderRequests.push(request)
}
if (filteredFolderRequests.length > 0) {
const filteredFolder = Object.assign({}, folder)
filteredFolder.requests = filteredFolderRequests
filteredFolders.push(filteredFolder)
}
}
if (filteredRequests.length + filteredFolders.length > 0) {
const filteredCollection = Object.assign({}, collection)
filteredCollection.requests = filteredRequests
filteredCollection.folders = filteredFolders
filteredCollections.push(filteredCollection)
}
}
return filteredCollections
},
},
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()
},
onAddFolder({ name, path }) {
const flag = "graphql"
this.$store.commit("postwoman/addFolder", {
name,
path,
flag,
})
this.displayModalAddFolder(false)
this.syncCollections()
},
addFolder(payload) {
const { folder, path } = payload
this.$data.editingFolder = folder
this.$data.editingFolderPath = path
this.displayModalAddFolder(true)
},
editFolder(payload) {
const { collectionIndex, folder, folderIndex } = payload
this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolder = folder
this.$data.editingFolderIndex = folderIndex
this.displayModalEditFolder(true)
this.syncCollections()
},
editRequest(payload) {
const { collectionIndex, folderIndex, folderName, request, requestIndex } = payload
this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolderIndex = folderIndex
this.$data.editingFolderName = folderName
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 && fb.currentSettings[0]) {
if (fb.currentSettings[0].value) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collectionsGraphql)),
"collectionsGraphql"
)
}
}
},
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
}
</script>

View File

@@ -1,295 +0,0 @@
<template>
<modal v-if="show" @close="hideModal">
<div slot="header">
<ul>
<li>
<div class="flex-wrap">
<h3 class="title">{{ $t("import_export") }} {{ $t("collections") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
<div class="flex-wrap">
<span
v-tooltip="{
content: !fb.currentUser ? $t('login_first') : $t('replace_current'),
}"
>
<button :disabled="!fb.currentUser" class="icon" @click="syncCollections">
<i class="material-icons">folder_shared</i>
<span>{{ $t("import_from_sync") }}</span>
</button>
</span>
<button
class="icon"
@click="openDialogChooseFileToReplaceWith"
v-tooltip="$t('replace_current')"
>
<i class="material-icons">create_new_folder</i>
<span>{{ $t("replace_json") }}</span>
<input
type="file"
@change="replaceWithJSON"
style="display: none;"
ref="inputChooseFileToReplaceWith"
accept="application/json"
/>
</button>
<button
class="icon"
@click="openDialogChooseFileToImportFrom"
v-tooltip="$t('preserve_current')"
>
<i class="material-icons">folder_special</i>
<span>{{ $t("import_json") }}</span>
<input
type="file"
@change="importFromJSON"
style="display: none;"
ref="inputChooseFileToImportFrom"
accept="application/json"
/>
</button>
</div>
</li>
</ul>
</div>
<div slot="body">
<textarea v-model="collectionJson" rows="8"></textarea>
</div>
<div slot="footer">
<div class="flex-wrap">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="exportJSON" v-tooltip="$t('download_file')">
{{ $t("export") }}
</button>
</span>
</div>
</div>
</modal>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
data() {
return {
fb,
}
},
props: {
show: Boolean,
},
components: {
modal: () => import("~/components/ui/modal"),
},
computed: {
collectionJson() {
return JSON.stringify(this.$store.state.postwoman.collections, null, 2)
},
},
methods: {
hideModal() {
this.$emit("hide-modal")
},
openDialogChooseFileToReplaceWith() {
this.$refs.inputChooseFileToReplaceWith.click()
},
openDialogChooseFileToImportFrom() {
this.$refs.inputChooseFileToImportFrom.click()
},
replaceWithJSON() {
let reader = new FileReader()
reader.onload = (event) => {
let content = event.target.result
let collections = JSON.parse(content)
if (collections[0]) {
let [name, folders, requests] = Object.keys(collections[0])
if (name === "name" && folders === "folders" && requests === "requests") {
// Do nothing
}
} else if (collections.info && collections.info.schema.includes("v2.1.0")) {
collections = this.parsePostmanCollection(collections)
} else {
return this.failedImport()
}
this.$store.commit("postwoman/replaceCollections", collections)
this.fileImported()
this.syncToFBCollections()
}
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
},
importFromJSON() {
let reader = new FileReader()
reader.onload = (event) => {
let content = event.target.result
let collections = JSON.parse(content)
if (collections[0]) {
let [name, folders, requests] = Object.keys(collections[0])
if (name === "name" && folders === "folders" && requests === "requests") {
// Do nothing
}
} else if (collections.info && collections.info.schema.includes("v2.1.0")) {
collections = this.parsePostmanCollection(collections)
} else {
return this.failedImport()
}
this.$store.commit("postwoman/importCollections", collections)
this.fileImported()
this.syncToFBCollections()
}
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
},
exportJSON() {
let text = this.collectionJson
text = text.replace(/\n/g, "\r\n")
let blob = new Blob([text], {
type: "text/json",
})
let anchor = document.createElement("a")
anchor.download = "postwoman-collection.json"
anchor.href = window.URL.createObjectURL(blob)
anchor.target = "_blank"
anchor.style.display = "none"
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
this.$toast.success(this.$t("download_started"), {
icon: "done",
})
},
syncCollections() {
this.$store.commit("postwoman/replaceCollections", fb.currentCollections)
this.fileImported()
},
syncToFBCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
fileImported() {
this.$toast.info(this.$t("file_imported"), {
icon: "folder_shared",
})
},
failedImport() {
this.$toast.error(this.$t("import_failed"), {
icon: "error",
})
},
parsePostmanCollection(collection, folders = true) {
let postwomanCollection = folders
? [
{
name: "",
folders: [],
requests: [],
},
]
: {
name: "",
requests: [],
}
for (let collectionItem of collection.item) {
if (collectionItem.request) {
if (postwomanCollection[0]) {
postwomanCollection[0].name = collection.info ? collection.info.name : ""
postwomanCollection[0].requests.push(this.parsePostmanRequest(collectionItem))
} else {
postwomanCollection.name = collection.name ? collection.name : ""
postwomanCollection.requests.push(this.parsePostmanRequest(collectionItem))
}
} else if (collectionItem.item) {
if (collectionItem.item[0]) {
postwomanCollection[0].folders.push(this.parsePostmanCollection(collectionItem, false))
}
}
}
return postwomanCollection
},
parsePostmanRequest({ name, request }) {
let pwRequest = {
url: "",
path: "",
method: "",
auth: "",
httpUser: "",
httpPassword: "",
passwordFieldType: "password",
bearerToken: "",
headers: [],
params: [],
bodyParams: [],
rawParams: "",
rawInput: false,
contentType: "",
requestType: "",
name: "",
}
pwRequest.name = name
let requestObjectUrl = request.url.raw.match(/^(.+:\/\/[^\/]+|{[^\/]+})(\/[^\?]+|).*$/)
if (requestObjectUrl) {
pwRequest.url = requestObjectUrl[1]
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
}
pwRequest.method = request.method
let itemAuth = request.auth ? request.auth : ""
let authType = itemAuth ? itemAuth.type : ""
if (authType === "basic") {
pwRequest.auth = "Basic Auth"
pwRequest.httpUser =
itemAuth.basic[0].key === "username" ? itemAuth.basic[0].value : itemAuth.basic[1].value
pwRequest.httpPassword =
itemAuth.basic[0].key === "password" ? itemAuth.basic[0].value : itemAuth.basic[1].value
} else if (authType === "oauth2") {
pwRequest.auth = "OAuth 2.0"
pwRequest.bearerToken =
itemAuth.oauth2[0].key === "accessToken"
? itemAuth.oauth2[0].value
: itemAuth.oauth2[1].value
} else if (authType === "bearer") {
pwRequest.auth = "Bearer Token"
pwRequest.bearerToken = itemAuth.bearer[0].value
}
let requestObjectHeaders = request.header
if (requestObjectHeaders) {
pwRequest.headers = requestObjectHeaders
for (let header of pwRequest.headers) {
delete header.name
delete header.type
}
}
let requestObjectParams = request.url.query
if (requestObjectParams) {
pwRequest.params = requestObjectParams
for (let param of pwRequest.params) {
delete param.disabled
}
}
if (request.body) {
if (request.body.mode === "urlencoded") {
let params = request.body.urlencoded
pwRequest.bodyParams = params ? params : []
for (let param of pwRequest.bodyParams) {
delete param.type
}
} else if (request.body.mode === "raw") {
pwRequest.rawInput = true
pwRequest.rawParams = request.body.raw
}
}
return pwRequest
},
},
}
</script>

View File

@@ -1,78 +1,71 @@
<!--
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
<AppSection :label="$t('collections')" ref="collections" no-legend>
<div class="show-on-large-screen">
<input
aria-label="Search"
type="search"
:placeholder="$t('search')"
v-model="filterText"
class="rounded-t-lg"
/>
</div>
<CollectionsAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
<CollectionsEdit
:show="showModalEdit"
:editingCollection="editingCollection"
:editingCollectionIndex="editingCollectionIndex"
:editing-collection="editingCollection"
:editing-collection-index="editingCollectionIndex"
@hide-modal="displayModalEdit(false)"
/>
<addFolder
<CollectionsAddFolder
:show="showModalAddFolder"
:collection="editingCollection"
:collectionIndex="editingCollectionIndex"
:folder="editingFolder"
:folder-path="editingFolderPath"
@add-folder="onAddFolder($event)"
@hide-modal="displayModalAddFolder(false)"
/>
<editFolder
<CollectionsEditFolder
:show="showModalEditFolder"
:collection="editingCollection"
:collectionIndex="editingCollectionIndex"
:collection-index="editingCollectionIndex"
:folder="editingFolder"
:folderIndex="editingFolderIndex"
:folder-index="editingFolderIndex"
@hide-modal="displayModalEditFolder(false)"
/>
<editRequest
<CollectionsEditRequest
:show="showModalEditRequest"
:collectionIndex="editingCollectionIndex"
:folderIndex="editingFolderIndex"
:collection-index="editingCollectionIndex"
:folder-index="editingFolderIndex"
:folder-name="editingFolderName"
:request="editingRequest"
:requestIndex="editingRequestIndex"
:request-index="editingRequestIndex"
@hide-modal="displayModalEditRequest(false)"
/>
<importExportCollections
<CollectionsImportExport
:show="showModalImportExport"
@hide-modal="displayModalImportExport(false)"
/>
<div class="flex-wrap">
<div>
<button class="icon" @click="displayModalAdd(true)">
<i class="material-icons">add</i>
<span>{{ $t("new") }}</span>
</button>
</div>
<div>
<button class="icon" @click="displayModalImportExport(true)">
{{ $t("import_export") }}
</button>
<!-- <a
href="https://github.com/liyasthomas/postwoman/wiki/Collections"
target="_blank"
rel="noopener"
>
<button class="icon" v-tooltip="'Wiki'">
<i class="material-icons">help_outline</i>
</button>
</a> -->
</div>
<div class="border-b row-wrapper border-brdColor">
<button class="icon" @click="displayModalAdd(true)">
<i class="material-icons">add</i>
<span>{{ $t("new") }}</span>
</button>
<button class="icon" @click="displayModalImportExport(true)">
{{ $t("import_export") }}
</button>
</div>
<p v-if="collections.length === 0" class="info">
<i class="material-icons">help_outline</i> Create new collection
<i class="material-icons">help_outline</i> {{ $t("create_new_collection") }}
</p>
<div class="virtual-list">
<ul>
<li v-for="(collection, index) in collections" :key="collection.name">
<collection
<ul class="flex-col">
<li v-for="(collection, index) in filteredCollections" :key="collection.name">
<CollectionsCollection
:name="collection.name"
:collection-index="index"
:collection="collection"
:doc="doc"
:isFiltered="filterText.length > 0"
@edit-collection="editCollection(collection, index)"
@add-folder="addFolder(collection, index)"
@add-folder="addFolder($event)"
@edit-folder="editFolder($event)"
@edit-request="editRequest($event)"
@select-collection="$emit('use-collection', collection)"
@@ -80,35 +73,23 @@ TODO:
</li>
</ul>
</div>
</pw-section>
<p v-if="filterText && filteredCollections.length === 0" class="info">
<i class="material-icons">not_interested</i> {{ $t("nothing_found") }} "{{ filterText }}"
</p>
</AppSection>
</template>
<style scoped lang="scss">
.virtual-list {
max-height: calc(100vh - 245px);
}
ul {
display: flex;
flex-direction: column;
max-height: calc(100vh - 270px);
}
</style>
<script>
import collection from "./collection"
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
components: {
collection,
"pw-section": () => import("../layout/section"),
addCollection: () => import("./addCollection"),
addFolder: () => import("./addFolder"),
editCollection: () => import("./editCollection"),
editFolder: () => import("./editFolder"),
editRequest: () => import("./editRequest"),
importExportCollections: () => import("./importExportCollections"),
},
props: {
doc: Boolean,
},
@@ -123,9 +104,17 @@ export default {
editingCollection: undefined,
editingCollectionIndex: undefined,
editingFolder: undefined,
editingFolderName: undefined,
editingFolderIndex: undefined,
editingFolderPath: undefined,
editingRequest: undefined,
editingRequestIndex: undefined,
filterText: "",
}
},
subscriptions() {
return {
SYNC_COLLECTIONS: getSettingSubject("syncCollections"),
}
},
computed: {
@@ -134,6 +123,44 @@ export default {
? fb.currentCollections
: this.$store.state.postwoman.collections
},
filteredCollections() {
const collections =
fb.currentUser !== null ? fb.currentCollections : this.$store.state.postwoman.collections
if (!this.filterText) return collections
const filterText = this.filterText.toLowerCase()
const filteredCollections = []
for (let collection of collections) {
const filteredRequests = []
const filteredFolders = []
for (let request of collection.requests) {
if (request.name.toLowerCase().includes(filterText)) filteredRequests.push(request)
}
for (let folder of collection.folders) {
const filteredFolderRequests = []
for (let request of folder.requests) {
if (request.name.toLowerCase().includes(filterText))
filteredFolderRequests.push(request)
}
if (filteredFolderRequests.length > 0) {
const filteredFolder = Object.assign({}, folder)
filteredFolder.requests = filteredFolderRequests
filteredFolders.push(filteredFolder)
}
}
if (filteredRequests.length + filteredFolders.length > 0) {
const filteredCollection = Object.assign({}, collection)
filteredCollection.requests = filteredRequests
filteredCollection.folders = filteredFolders
filteredCollections.push(filteredCollection)
}
}
return filteredCollections
},
},
async mounted() {
this._keyListener = function (e) {
@@ -177,15 +204,25 @@ export default {
this.displayModalEdit(true)
this.syncCollections()
},
addFolder(collection, collectionIndex) {
this.$data.editingCollection = collection
this.$data.editingCollectionIndex = collectionIndex
this.displayModalAddFolder(true)
onAddFolder({ name, path }) {
const flag = "rest"
this.$store.commit("postwoman/addFolder", {
name,
path,
flag,
})
this.displayModalAddFolder(false)
this.syncCollections()
},
addFolder(payload) {
const { folder, path } = payload
this.$data.editingFolder = folder
this.$data.editingFolderPath = path
this.displayModalAddFolder(true)
},
editFolder(payload) {
const { collectionIndex, folder, folderIndex } = payload
this.$data.editingCollection = collection
this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolder = folder
this.$data.editingFolderIndex = folderIndex
@@ -193,9 +230,10 @@ export default {
this.syncCollections()
},
editRequest(payload) {
const { request, collectionIndex, folderIndex, requestIndex } = payload
const { collectionIndex, folderIndex, folderName, request, requestIndex } = payload
this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolderIndex = folderIndex
this.$data.editingFolderName = folderName
this.$data.editingRequest = request
this.$data.editingRequestIndex = requestIndex
this.displayModalEditRequest(true)
@@ -210,10 +248,11 @@ export default {
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)))
}
if (fb.currentUser !== null && this.SYNC_COLLECTIONS) {
fb.writeCollections(
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)),
"collections"
)
}
},
},

View File

@@ -1,81 +0,0 @@
<template>
<div class="flex-wrap">
<div>
<button
class="icon"
@click="!doc ? selectRequest() : {}"
v-tooltip="!doc ? $t('use_request') : ''"
>
<i class="material-icons">insert_drive_file</i>
<span>{{ request.name }}</span>
</button>
</div>
<v-popover>
<button class="tooltip-target icon" v-tooltip="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="$emit('edit-request')" v-close-popover>
<i class="material-icons">edit</i>
<span>{{ $t("edit") }}</span>
</button>
</div>
<div>
<button class="icon" @click="removeRequest" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
</template>
<style scoped lang="scss">
ul {
display: flex;
flex-direction: column;
}
ul li {
display: flex;
padding-left: 16px;
border-left: 1px solid var(--brd-color);
}
</style>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
request: Object,
collectionIndex: Number,
folderIndex: Number,
requestIndex: Number,
doc: Boolean,
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
selectRequest() {
this.$store.commit("postwoman/selectRequest", { request: this.request })
},
removeRequest() {
if (!confirm("Are you sure you want to remove this request?")) return
this.$store.commit("postwoman/removeRequest", {
collectionIndex: this.collectionIndex,
folderIndex: this.folderIndex,
requestIndex: this.requestIndex,
})
this.syncCollections()
},
},
}
</script>

View File

@@ -1,193 +0,0 @@
<template>
<modal v-if="show" @close="hideModal">
<div slot="header">
<ul>
<li>
<div class="flex-wrap">
<h3 class="title">{{ $t("save_request_as") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</li>
</ul>
</div>
<div slot="body">
<ul>
<li>
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="requestData.name"
:placeholder="defaultRequestName"
@keyup.enter="saveRequestAs"
/>
<label for="selectCollection">{{ $t("collection") }}</label>
<span class="select-wrapper">
<select type="text" id="selectCollection" v-model="requestData.collectionIndex">
<option :key="undefined" :value="undefined" hidden disabled selected>{{
$t("select_collection")
}}</option>
<option
v-for="(collection, index) in $store.state.postwoman.collections"
:key="index"
:value="index"
>
{{ collection.name }}
</option>
</select>
</span>
<label for="selectFolder">{{ $t("folder") }}</label>
<span class="select-wrapper">
<select type="text" id="selectFolder" v-model="requestData.folderIndex">
<option :key="undefined" :value="undefined">/</option>
<option v-for="(folder, index) in folders" :key="index" :value="index">
{{ folder.name }}
</option>
</select>
</span>
<label for="selectRequest">{{ $t("request") }}</label>
<span class="select-wrapper">
<select type="text" id="selectRequest" v-model="requestData.requestIndex">
<option :key="undefined" :value="undefined">/</option>
<option v-for="(folder, index) in requests" :key="index" :value="index">
{{ folder.name }}
</option>
</select>
</span>
</li>
</ul>
</div>
<div slot="footer">
<div class="flex-wrap">
<span></span>
<span>
<button class="icon" @click="hideModal">
{{ $t("cancel") }}
</button>
<button class="icon primary" @click="saveRequestAs">
{{ $t("save") }}
</button>
</span>
</div>
</div>
</modal>
</template>
<script>
import { fb } from "~/helpers/fb"
export default {
props: {
show: Boolean,
editingRequest: Object,
},
components: {
modal: () => import("~/components/ui/modal"),
},
data() {
return {
defaultRequestName: "My Request",
requestData: {
name: undefined,
collectionIndex: undefined,
folderIndex: undefined,
requestIndex: undefined,
},
}
},
watch: {
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
// if user choosen some folder, than selected other collection, which doesn't have any folders
// than `requestUpdateData.folderIndex` won't be reseted
this.$data.requestData.folderIndex = undefined
this.$data.requestData.requestIndex = undefined
},
"requestData.folderIndex": function resetRequestIndex() {
this.$data.requestData.requestIndex = undefined
},
editingRequest(request) {
this.defaultRequestName = request.label || "My Request"
},
},
computed: {
folders() {
const userSelectedAnyCollection = this.$data.requestData.collectionIndex !== undefined
if (!userSelectedAnyCollection) return []
const noCollectionAvailable =
this.$store.state.postwoman.collections[this.$data.requestData.collectionIndex] !==
undefined
if (!noCollectionAvailable) return []
return this.$store.state.postwoman.collections[this.$data.requestData.collectionIndex].folders
},
requests() {
const userSelectedAnyCollection = this.$data.requestData.collectionIndex !== undefined
if (!userSelectedAnyCollection) return []
const userSelectedAnyFolder = this.$data.requestData.folderIndex !== undefined
if (userSelectedAnyFolder) {
const collection = this.$store.state.postwoman.collections[
this.$data.requestData.collectionIndex
]
const folder = collection.folders[this.$data.requestData.folderIndex]
const requests = folder.requests
return requests
} else {
const collection = this.$store.state.postwoman.collections[
this.$data.requestData.collectionIndex
]
const noCollectionAvailable =
this.$store.state.postwoman.collections[this.$data.requestData.collectionIndex] !==
undefined
if (!noCollectionAvailable) return []
const requests = collection.requests
return requests
}
},
},
methods: {
syncCollections() {
if (fb.currentUser !== null) {
if (fb.currentSettings[0].value) {
fb.writeCollections(JSON.parse(JSON.stringify(this.$store.state.postwoman.collections)))
}
}
},
saveRequestAs() {
const userDidntSpecifyCollection = this.$data.requestData.collectionIndex === undefined
if (userDidntSpecifyCollection) {
this.$toast.error(this.$t("select_collection"), {
icon: "error",
})
return
}
const requestUpdated = {
...this.$props.editingRequest,
name: this.$data.requestData.name || this.$data.defaultRequestName,
collection: this.$data.requestData.collectionIndex,
}
this.$store.commit("postwoman/saveRequestAs", {
request: requestUpdated,
collectionIndex: this.$data.requestData.collectionIndex,
folderIndex: this.$data.requestData.folderIndex,
requestIndex: this.$data.requestData.requestIndex,
})
this.hideModal()
this.syncCollections()
},
hideModal() {
this.$emit("hide-modal")
this.$emit("hide-model") // for backward compatibility // TODO: use fixed event
},
},
}
</script>

View File

@@ -1,33 +1,27 @@
<template>
<modal v-if="show" @close="hideModal">
<SmartModal 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 class="row-wrapper">
<h3 class="title">{{ $t("new_environment") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body">
<ul>
<li>
<input
type="text"
v-model="name"
:placeholder="$t('my_new_environment')"
@keyup.enter="addNewEnvironment"
/>
</li>
</ul>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="$t('my_new_environment')"
@keyup.enter="addNewEnvironment"
/>
</div>
<div slot="footer">
<div class="flex-wrap">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
@@ -39,30 +33,31 @@
</span>
</div>
</div>
</modal>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
show: Boolean,
},
components: {
modal: () => import("~/components/ui/modal"),
},
data() {
return {
name: undefined,
}
},
subscriptions() {
return {
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments")
}
},
methods: {
syncEnvironments() {
if (fb.currentUser !== null) {
if (fb.currentSettings[1].value) {
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
}
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
}
},
addNewEnvironment() {

View File

@@ -1,46 +1,41 @@
<template>
<modal v-if="show" @close="hideModal">
<SmartModal 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 class="row-wrapper">
<h3 class="title">{{ $t("edit_environment") }}</h3>
<div>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body">
<ul>
<div slot="body" class="flex flex-col">
<label for="selectLabel">{{ $t("label") }}</label>
<input
type="text"
id="selectLabel"
v-model="name"
:placeholder="editingEnvironment.name"
@keyup.enter="saveEnvironment"
/>
<div class="row-wrapper">
<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>
<ul
v-for="(variable, index) in this.editingEnvCopy.variables"
:key="index"
class="border-b border-dashed divide-y md:divide-x border-brdColor divide-dashed divide-brdColor md:divide-y-0"
:class="{ 'border-t': index == 0 }"
>
<li>
<input
type="text"
v-model="name"
:placeholder="editingEnvironment.name"
@keyup.enter="saveEnvironment"
/>
</li>
</ul>
<ul>
<li>
<div class="flex-wrap">
<label for="variableList">{{ $t("env_variable_list") }}</label>
<div>
<button class="icon" @click="clearContent($event)" v-tooltip.bottom="$t('clear')">
<i class="material-icons">clear_all</i>
</button>
</div>
</div>
</li>
</ul>
<ul v-for="(variable, index) in this.editingEnvCopy.variables" :key="index">
<li>
<input
:placeholder="$t('parameter_count', { count: index + 1 })"
:placeholder="$t('variable_count', { count: index + 1 })"
:name="'param' + index"
:value="variable.key"
@change="
@@ -90,7 +85,7 @@
</ul>
</div>
<div slot="footer">
<div class="flex-wrap">
<div class="row-wrapper">
<span></span>
<span>
<button class="icon" @click="hideModal">
@@ -102,11 +97,12 @@
</span>
</div>
</div>
</modal>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
@@ -114,16 +110,19 @@ export default {
editingEnvironment: Object,
editingEnvironmentIndex: Number,
},
components: {
modal: () => import("~/components/ui/modal"),
},
data() {
return {
name: undefined,
doneButton: '<i class="material-icons">done</i>',
}
},
subscriptions() {
return {
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments")
}
},
watch: {
editingEnvironment: function (update) {
editingEnvironment(update) {
this.name =
this.$props.editingEnvironment && this.$props.editingEnvironment.name
? this.$props.editingEnvironment.name
@@ -142,19 +141,17 @@ export default {
},
methods: {
syncEnvironments() {
if (fb.currentUser !== null) {
if (fb.currentSettings[1].value) {
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
}
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
}
},
clearContent(e) {
clearContent({ target }) {
this.$store.commit("postwoman/removeVariables", [])
e.target.innerHTML = this.doneButton
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)
setTimeout(() => (target.innerHTML = '<i class="material-icons">clear_all</i>'), 1000)
},
addEnvironmentVariable() {
let value = { key: "", value: "" }

View File

@@ -0,0 +1,73 @@
<template>
<div>
<div class="row-wrapper">
<div>
<button class="icon" @click="$emit('edit-environment')">
<i class="material-icons">layers</i>
<span>{{ environment.name }}</span>
</button>
</div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="$emit('edit-environment')" v-close-popover>
<i class="material-icons">create</i>
<span>{{ $t("edit") }}</span>
</button>
</div>
<div>
<button class="icon" @click="confirmRemove = true" v-close-popover>
<i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span>
</button>
</div>
</template>
</v-popover>
</div>
<SmartConfirmModal
:show="confirmRemove"
:title="$t('are_you_sure_remove_environment')"
@hide-modal="confirmRemove = false"
@resolve="removeEnvironment"
/>
</div>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
props: {
environment: Object,
environmentIndex: Number,
},
data() {
return {
confirmRemove: false,
}
},
subscriptions() {
return {
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments")
}
},
methods: {
syncEnvironments() {
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
}
},
removeEnvironment() {
this.$store.commit("postwoman/removeEnvironment", this.environmentIndex)
this.$toast.error(this.$t("deleted"), {
icon: "delete",
})
this.syncEnvironments()
},
},
}
</script>

View File

@@ -0,0 +1,276 @@
<template>
<SmartModal v-if="show" @close="hideModal">
<div slot="header">
<div class="row-wrapper">
<h3 class="title">{{ $t("import_export") }} {{ $t("environments") }}</h3>
<div>
<v-popover>
<button class="tooltip-target icon" v-tooltip.left="$t('more')">
<i class="material-icons">more_vert</i>
</button>
<template slot="popover">
<div>
<button class="icon" @click="readEnvironmentGist" v-close-popover>
<i class="material-icons">assignment_returned</i>
<span>{{ $t("import_from_gist") }}</span>
</button>
</div>
<div
v-tooltip.bottom="{
content: !fb.currentUser
? $t('login_with_github_to') + $t('create_secret_gist')
: fb.currentUser.provider !== 'github.com'
? $t('login_with_github_to') + $t('create_secret_gist')
: null,
}"
>
<button
:disabled="
!fb.currentUser ? true : fb.currentUser.provider !== 'github.com' ? true : false
"
class="icon"
@click="createEnvironmentGist"
v-close-popover
>
<i class="material-icons">assignment_turned_in</i>
<span>{{ $t("create_secret_gist") }}</span>
</button>
</div>
</template>
</v-popover>
<button class="icon" @click="hideModal">
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
<div slot="body" class="flex flex-col">
<div class="flex flex-col items-start p-2">
<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>
<div v-if="showJsonCode" class="row-wrapper">
<textarea v-model="environmentJson" rows="8" readonly></textarea>
</div>
</div>
<div slot="footer">
<div class="row-wrapper">
<span>
<SmartToggle :on="showJsonCode" @change="showJsonCode = $event">
{{ $t("show_code") }}
</SmartToggle>
</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>
</SmartModal>
</template>
<script>
import { fb } from "~/helpers/fb"
import { getSettingSubject } from "~/newstore/settings"
export default {
data() {
return {
fb,
showJsonCode: false,
}
},
subscriptions() {
return {
SYNC_ENVIRONMENTS: getSettingSubject("syncEnvironments")
}
},
props: {
show: Boolean,
},
computed: {
environmentJson() {
return JSON.stringify(this.$store.state.postwoman.environments, null, 2)
},
},
methods: {
async createEnvironmentGist() {
await this.$axios
.$post(
"https://api.github.com/gists",
{
files: {
"hoppscotch-environments.json": {
content: this.environmentJson,
},
},
},
{
headers: {
Authorization: `token ${fb.currentUser.accessToken}`,
Accept: "application/vnd.github.v3+json",
},
}
)
.then(({ html_url }) => {
this.$toast.success(this.$t("gist_created"), {
icon: "done",
})
window.open(html_url)
})
.catch((error) => {
this.$toast.error(this.$t("something_went_wrong"), {
icon: "error",
})
console.log(error)
})
},
async readEnvironmentGist() {
let gist = prompt(this.$t("enter_gist_url"))
if (!gist) return
await this.$axios
.$get(`https://api.github.com/gists/${gist.split("/").pop()}`, {
headers: {
Accept: "application/vnd.github.v3+json",
},
})
.then(({ files }) => {
let environments = JSON.parse(Object.values(files)[0].content)
this.$store.commit("postwoman/replaceEnvironments", environments)
this.fileImported()
this.syncToFBEnvironments()
})
.catch((error) => {
this.failedImport()
console.log(error)
})
},
hideModal() {
this.$emit("hide-modal")
},
openDialogChooseFileToReplaceWith() {
this.$refs.inputChooseFileToReplaceWith.click()
},
openDialogChooseFileToImportFrom() {
this.$refs.inputChooseFileToImportFrom.click()
},
replaceWithJSON() {
let reader = new FileReader()
reader.onload = ({ target }) => {
let content = target.result
let environments = JSON.parse(content)
this.$store.commit("postwoman/replaceEnvironments", environments)
}
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
this.fileImported()
this.syncToFBEnvironments()
this.$refs.inputChooseFileToReplaceWith.value = ""
},
importFromJSON() {
let reader = new FileReader()
reader.onload = ({ target }) => {
let content = target.result
let importFileObj = JSON.parse(content)
if (
importFileObj["_postman_variable_scope"] === "environment" ||
importFileObj["_postman_variable_scope"] === "globals"
) {
this.importFromPostman(importFileObj)
} else {
this.importFromPostwoman(importFileObj)
}
}
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
this.syncToFBEnvironments()
this.$refs.inputChooseFileToImportFrom.value = ""
},
importFromPostwoman(environments) {
let confirmation = this.$t("file_imported")
this.$store.commit("postwoman/importAddEnvironments", {
environments,
confirmation,
})
},
importFromPostman({ name, values }) {
let environment = { name, variables: [] }
values.forEach(({ key, value }) => environment.variables.push({ key, 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 = "hoppscotch-environment.json"
anchor.href = window.URL.createObjectURL(blob)
anchor.target = "_blank"
anchor.style.display = "none"
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
this.$toast.success(this.$t("download_started"), {
icon: "done",
})
},
syncEnvironments() {
this.$store.commit("postwoman/replaceEnvironments", fb.currentEnvironments)
this.fileImported()
},
syncToFBEnvironments() {
if (fb.currentUser !== null && this.SYNC_ENVIRONMENTS) {
fb.writeEnvironments(JSON.parse(JSON.stringify(this.$store.state.postwoman.environments)))
}
},
fileImported() {
this.$toast.info(this.$t("file_imported"), {
icon: "folder_shared",
})
},
},
}
</script>

Some files were not shown because too many files have changed in this diff Show More