Compare commits
1 Commits
v2.2.1
...
refactor/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ffc9e3a4d |
26
.github/pull_request_template.md
vendored
@@ -1,26 +0,0 @@
|
|||||||
<!--
|
|
||||||
Thanks for creating this pull request 🤗
|
|
||||||
|
|
||||||
Please make sure that the pull request is limited to one type (docs, feature, etc.) and keep it as small as possible. You can open multiple prs instead of opening a huge one.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- If this pull request closes an issue, please mention the issue number below -->
|
|
||||||
Closes # <!-- Issue # here -->
|
|
||||||
|
|
||||||
### Description
|
|
||||||
<!-- Add a brief description of the pull request -->
|
|
||||||
|
|
||||||
<!-- You can also choose to add a list of changes and if they have been completed or not by using the markdown to-do list syntax
|
|
||||||
- [ ] Not Completed
|
|
||||||
- [x] Completed
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Checks
|
|
||||||
<!-- Make sure your pull request passes the CI checks and do check the following fields as needed - -->
|
|
||||||
- [ ] My pull request adheres to the code style of this project
|
|
||||||
- [ ] My code requires changes to the documentation
|
|
||||||
- [ ] I have updated the documentation as required
|
|
||||||
- [ ] All the tests have passed
|
|
||||||
|
|
||||||
### Additional Information
|
|
||||||
<!-- Any additional information like breaking changes, dependencies added, screenshots, comparisons between new and old behavior, etc. -->
|
|
||||||
832
CHANGELOG.md
@@ -1,3 +1,833 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
Visit [releases](https://github.com/hoppscotch/hoppscotch/releases) for full changelog.
|
## [v1.12.0](https://github.com/hoppscotch/hoppscotch/tree/v1.12.0) (2020-05-27)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/hoppscotch/hoppscotch/compare/v1.10.0...v1.12.0)
|
||||||
|
|
||||||
|
## [v1.10.0](https://github.com/hoppscotch/hoppscotch/tree/v1.10.0) (2020-04-10)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/hoppscotch/hoppscotch/compare/v1.9.9...v1.10.0)
|
||||||
|
|
||||||
|
## [v1.9.9](https://github.com/liyasthomas/postwoman/tree/v1.9.9) (2020-07-30)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.7...v1.9.9)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- TextDecoder.decode\(\) TypeError hangs the whole app [\#1032](https://github.com/liyasthomas/postwoman/issues/1032)
|
||||||
|
- response content doesn't fit to the text area when resizing [\#970](https://github.com/liyasthomas/postwoman/issues/970)
|
||||||
|
- typing into headers input fields [\#912](https://github.com/liyasthomas/postwoman/issues/912)
|
||||||
|
- Environment variable template \(\<\<foo\>\>\) appears urlencoded \(%3C%3Cfoo%3E%3E\) [\#896](https://github.com/liyasthomas/postwoman/issues/896)
|
||||||
|
- TypeError: Cannot read property 'startsWith' of undefined - after getting 401 response [\#894](https://github.com/liyasthomas/postwoman/issues/894)
|
||||||
|
- When deleting the header, the key is not updated [\#886](https://github.com/liyasthomas/postwoman/issues/886)
|
||||||
|
- Cannot introduce query parameters in URL for WebSocket [\#873](https://github.com/liyasthomas/postwoman/issues/873)
|
||||||
|
- Response content-type as `text/html` with content in `json` cause content area display empty [\#869](https://github.com/liyasthomas/postwoman/issues/869)
|
||||||
|
- Proxy privacy policy link [\#865](https://github.com/liyasthomas/postwoman/issues/865)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Collections | Request UI Issue [\#1028](https://github.com/liyasthomas/postwoman/issues/1028)
|
||||||
|
- JSON not showing up in the correct format [\#1023](https://github.com/liyasthomas/postwoman/issues/1023)
|
||||||
|
- ignore duplicates in history [\#1022](https://github.com/liyasthomas/postwoman/issues/1022)
|
||||||
|
- change history menu [\#1021](https://github.com/liyasthomas/postwoman/issues/1021)
|
||||||
|
- integrate parameters with history [\#1020](https://github.com/liyasthomas/postwoman/issues/1020)
|
||||||
|
- Why some Chrome do not have the ability to install PWA? [\#1015](https://github.com/liyasthomas/postwoman/issues/1015)
|
||||||
|
- Shall we have the team management ability and some public documents? [\#1014](https://github.com/liyasthomas/postwoman/issues/1014)
|
||||||
|
- I have edit this config, but it is not available to login. [\#1013](https://github.com/liyasthomas/postwoman/issues/1013)
|
||||||
|
- User login is disabled after i run it on our local server. [\#1012](https://github.com/liyasthomas/postwoman/issues/1012)
|
||||||
|
- postwoman google login doesn't work behind ingress or reverse proxy [\#1009](https://github.com/liyasthomas/postwoman/issues/1009)
|
||||||
|
- Compile error [\#1006](https://github.com/liyasthomas/postwoman/issues/1006)
|
||||||
|
- Postman Web is now out. It might be great to find a USP for Postwoman [\#1000](https://github.com/liyasthomas/postwoman/issues/1000)
|
||||||
|
- Saving response data in env variable [\#984](https://github.com/liyasthomas/postwoman/issues/984)
|
||||||
|
- contentType 无法使用 form-date 上传文件 [\#983](https://github.com/liyasthomas/postwoman/issues/983)
|
||||||
|
- Currently completely broken [\#980](https://github.com/liyasthomas/postwoman/issues/980)
|
||||||
|
- localhost request error [\#979](https://github.com/liyasthomas/postwoman/issues/979)
|
||||||
|
- Installing postwoman locally [\#969](https://github.com/liyasthomas/postwoman/issues/969)
|
||||||
|
- Do I install NodeJS for my online environment [\#968](https://github.com/liyasthomas/postwoman/issues/968)
|
||||||
|
- Collections and Environment Module [\#967](https://github.com/liyasthomas/postwoman/issues/967)
|
||||||
|
- Textarea display problem in super hi-dpi [\#965](https://github.com/liyasthomas/postwoman/issues/965)
|
||||||
|
- TypeError: Cannot read property 'value' of undefined - when logged in [\#961](https://github.com/liyasthomas/postwoman/issues/961)
|
||||||
|
- Enable user-select on websocket and other realtime message logs [\#951](https://github.com/liyasthomas/postwoman/issues/951)
|
||||||
|
- POST requet error [\#947](https://github.com/liyasthomas/postwoman/issues/947)
|
||||||
|
- Unable to fetch schema from localhost GraphQL server. [\#940](https://github.com/liyasthomas/postwoman/issues/940)
|
||||||
|
- Support downloading binary responses [\#929](https://github.com/liyasthomas/postwoman/issues/929)
|
||||||
|
- Integrate PostWoman In our Webapp [\#918](https://github.com/liyasthomas/postwoman/issues/918)
|
||||||
|
- proxy issue [\#911](https://github.com/liyasthomas/postwoman/issues/911)
|
||||||
|
- Button to cancel requests [\#909](https://github.com/liyasthomas/postwoman/issues/909)
|
||||||
|
- How to upload a file with a post request [\#908](https://github.com/liyasthomas/postwoman/issues/908)
|
||||||
|
- Cant Import Postman Global Environment Variables [\#907](https://github.com/liyasthomas/postwoman/issues/907)
|
||||||
|
- Postwoman Docker Container behind Reverse Proxy [\#906](https://github.com/liyasthomas/postwoman/issues/906)
|
||||||
|
- `pw.response` seems not to work [\#905](https://github.com/liyasthomas/postwoman/issues/905)
|
||||||
|
- Could postman add Sign in with LDAP server? [\#901](https://github.com/liyasthomas/postwoman/issues/901)
|
||||||
|
- Collections & Environments not synced [\#900](https://github.com/liyasthomas/postwoman/issues/900)
|
||||||
|
- Add authentication to MQTT [\#898](https://github.com/liyasthomas/postwoman/issues/898)
|
||||||
|
- Labels are lost when using requests from collections [\#897](https://github.com/liyasthomas/postwoman/issues/897)
|
||||||
|
- Handle JSON Parameter list validation [\#891](https://github.com/liyasthomas/postwoman/issues/891)
|
||||||
|
- Nuxt fatal error [\#883](https://github.com/liyasthomas/postwoman/issues/883)
|
||||||
|
- Cannot connect my local websocket server [\#880](https://github.com/liyasthomas/postwoman/issues/880)
|
||||||
|
- Environments not synced after edit [\#877](https://github.com/liyasthomas/postwoman/issues/877)
|
||||||
|
- Can't find Desktop app link anywhere [\#872](https://github.com/liyasthomas/postwoman/issues/872)
|
||||||
|
- Show request completion time [\#871](https://github.com/liyasthomas/postwoman/issues/871)
|
||||||
|
- Make docs on self-hosting Postwoman [\#868](https://github.com/liyasthomas/postwoman/issues/868)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Add trailing backslash to generated cURL code for easier paste-and-execute [\#1033](https://github.com/liyasthomas/postwoman/pull/1033) ([ushuz](https://github.com/ushuz))
|
||||||
|
- Update zh-CN.json [\#1031](https://github.com/liyasthomas/postwoman/pull/1031) ([hantianwei](https://github.com/hantianwei))
|
||||||
|
- Bump firebase from 7.17.0 to 7.17.1 [\#1026](https://github.com/liyasthomas/postwoman/pull/1026) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Update zh-CN.json [\#1024](https://github.com/liyasthomas/postwoman/pull/1024) ([hantianwei](https://github.com/hantianwei))
|
||||||
|
- Bump @nuxtjs/gtm from 2.3.0 to 2.3.2 [\#1019](https://github.com/liyasthomas/postwoman/pull/1019) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump firebase from 7.16.1 to 7.17.0 [\#1018](https://github.com/liyasthomas/postwoman/pull/1018) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Fix bugs with the renderer mixins [\#1008](https://github.com/liyasthomas/postwoman/pull/1008) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Bump eslint from 7.4.0 to 7.5.0 [\#1005](https://github.com/liyasthomas/postwoman/pull/1005) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Add Collections section in Docs page [\#1004](https://github.com/liyasthomas/postwoman/pull/1004) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Bump lodash from 4.17.15 to 4.17.19 in /functions [\#999](https://github.com/liyasthomas/postwoman/pull/999) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump @nuxtjs/google-analytics from 2.3.0 to 2.4.0 [\#998](https://github.com/liyasthomas/postwoman/pull/998) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump firebase from 7.16.0 to 7.16.1 [\#997](https://github.com/liyasthomas/postwoman/pull/997) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Fixed broken network requests in GraphQL [\#995](https://github.com/liyasthomas/postwoman/pull/995) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- fix: replaceWithJSON used wrong commit name [\#993](https://github.com/liyasthomas/postwoman/pull/993) ([perseveringman](https://github.com/perseveringman))
|
||||||
|
- ⬆️ Bump @nuxtjs/toast from 3.3.0 to 3.3.1 [\#992](https://github.com/liyasthomas/postwoman/pull/992) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump start-server-and-test from 1.11.1 to 1.11.2 [\#991](https://github.com/liyasthomas/postwoman/pull/991) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump @nuxtjs/axios from 5.11.0 to 5.12.0 [\#990](https://github.com/liyasthomas/postwoman/pull/990) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump firebase from 7.15.5 to 7.16.0 [\#989](https://github.com/liyasthomas/postwoman/pull/989) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump start-server-and-test from 1.11.0 to 1.11.1 [\#988](https://github.com/liyasthomas/postwoman/pull/988) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump sass-loader from 9.0.1 to 9.0.2 [\#986](https://github.com/liyasthomas/postwoman/pull/986) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump cypress from 4.9.0 to 4.10.0 [\#985](https://github.com/liyasthomas/postwoman/pull/985) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump ace-builds from 1.4.11 to 1.4.12 [\#982](https://github.com/liyasthomas/postwoman/pull/982) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump vuefire from 2.2.2 to 2.2.3 [\#981](https://github.com/liyasthomas/postwoman/pull/981) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump eslint from 7.3.1 to 7.4.0 [\#978](https://github.com/liyasthomas/postwoman/pull/978) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump sass-loader from 9.0.0 to 9.0.1 [\#977](https://github.com/liyasthomas/postwoman/pull/977) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump graphql from 15.2.0 to 15.3.0 [\#976](https://github.com/liyasthomas/postwoman/pull/976) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump nuxt-i18n from 6.13.0 to 6.13.1 [\#975](https://github.com/liyasthomas/postwoman/pull/975) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump sass-loader from 8.0.2 to 9.0.0 [\#973](https://github.com/liyasthomas/postwoman/pull/973) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump nuxt-i18n from 6.12.2 to 6.13.0 [\#972](https://github.com/liyasthomas/postwoman/pull/972) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump graphql from 15.1.0 to 15.2.0 [\#966](https://github.com/liyasthomas/postwoman/pull/966) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump @nuxtjs/sitemap from 2.3.2 to 2.4.0 [\#963](https://github.com/liyasthomas/postwoman/pull/963) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump firebase from 7.15.4 to 7.15.5 [\#962](https://github.com/liyasthomas/postwoman/pull/962) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump eslint from 7.3.0 to 7.3.1 [\#958](https://github.com/liyasthomas/postwoman/pull/958) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump cypress from 4.8.0 to 4.9.0 [\#957](https://github.com/liyasthomas/postwoman/pull/957) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump firebase from 7.15.3 to 7.15.4 [\#956](https://github.com/liyasthomas/postwoman/pull/956) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Binary Responses & Response Lenses [\#955](https://github.com/liyasthomas/postwoman/pull/955) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Improving SEO [\#954](https://github.com/liyasthomas/postwoman/pull/954) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Isolate Netlify, Firebase and Helper functions + Import from absolute… [\#953](https://github.com/liyasthomas/postwoman/pull/953) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Added ability to select text in realtime log [\#952](https://github.com/liyasthomas/postwoman/pull/952) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- ⬆️ Bump firebase from 7.15.1 to 7.15.3 [\#950](https://github.com/liyasthomas/postwoman/pull/950) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump eslint from 7.2.0 to 7.3.0 [\#949](https://github.com/liyasthomas/postwoman/pull/949) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Revert "⬆️ Bump nuxt from 2.12.2 to 2.13.0" [\#946](https://github.com/liyasthomas/postwoman/pull/946) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- ⬆️ Bump nuxt from 2.12.2 to 2.13.0 [\#942](https://github.com/liyasthomas/postwoman/pull/942) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump @nuxtjs/sitemap from 2.3.1 to 2.3.2 [\#939](https://github.com/liyasthomas/postwoman/pull/939) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump graphql from 14.6.0 to 15.1.0 [\#938](https://github.com/liyasthomas/postwoman/pull/938) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Updated readme [\#937](https://github.com/liyasthomas/postwoman/pull/937) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- ⬆️ Bump graphql-language-service-interface from 2.3.3 to 2.4.0 [\#936](https://github.com/liyasthomas/postwoman/pull/936) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- ⬆️ Bump firebase from 7.15.0 to 7.15.1 [\#935](https://github.com/liyasthomas/postwoman/pull/935) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Transpiled ES5 code to ES6/ES7 [\#934](https://github.com/liyasthomas/postwoman/pull/934) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Create Dependabot config file [\#932](https://github.com/liyasthomas/postwoman/pull/932) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Hide download response button for non-JSON responses [\#931](https://github.com/liyasthomas/postwoman/pull/931) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.7.0 to 4.8.0 [\#928](https://github.com/liyasthomas/postwoman/pull/928) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- fix: environment and collection sync issue with firebase [\#926](https://github.com/liyasthomas/postwoman/pull/926) ([myussufz](https://github.com/myussufz))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.10.3 to 5.11.0 [\#925](https://github.com/liyasthomas/postwoman/pull/925) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump eslint from 7.1.0 to 7.2.0 [\#924](https://github.com/liyasthomas/postwoman/pull/924) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- GraphQL response options only visible when a response is shown [\#923](https://github.com/liyasthomas/postwoman/pull/923) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps\): bump firebase from 7.14.6 to 7.15.0 [\#922](https://github.com/liyasthomas/postwoman/pull/922) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/sitemap from 2.3.0 to 2.3.1 [\#921](https://github.com/liyasthomas/postwoman/pull/921) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Added ability to download GraphQL responses [\#920](https://github.com/liyasthomas/postwoman/pull/920) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.12.1 to 6.12.2 [\#919](https://github.com/liyasthomas/postwoman/pull/919) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/gtm from 2.2.3 to 2.3.0 [\#917](https://github.com/liyasthomas/postwoman/pull/917) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Cancel Request from the Keyboard [\#916](https://github.com/liyasthomas/postwoman/pull/916) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Cancellable Requests [\#915](https://github.com/liyasthomas/postwoman/pull/915) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.12.0 to 6.12.1 [\#914](https://github.com/liyasthomas/postwoman/pull/914) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.14.5 to 7.14.6 [\#913](https://github.com/liyasthomas/postwoman/pull/913) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
|
||||||
|
## [v1.9.7](https://github.com/liyasthomas/postwoman/tree/v1.9.7) (2020-05-12)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.5...v1.9.7)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Empty header in headers list results in SyntaxError: Failed to execute 'setRequestHeader' [\#765](https://github.com/liyasthomas/postwoman/issues/765)
|
||||||
|
- Getting cannot read value of undefined [\#731](https://github.com/liyasthomas/postwoman/issues/731)
|
||||||
|
- Environment variables in collections [\#642](https://github.com/liyasthomas/postwoman/issues/642)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Import/Export collections from private github repos to share among teams. [\#867](https://github.com/liyasthomas/postwoman/issues/867)
|
||||||
|
- Unable to use postwoman with latest docker image from docker hub [\#866](https://github.com/liyasthomas/postwoman/issues/866)
|
||||||
|
- Access to nonexistent routes will not be redirect to page 404 [\#849](https://github.com/liyasthomas/postwoman/issues/849)
|
||||||
|
- Error: Network Error. Check console for details. [\#827](https://github.com/liyasthomas/postwoman/issues/827)
|
||||||
|
- 'Documentation Generated' response stacking past top of page if submit clicked enough times/fast enough [\#826](https://github.com/liyasthomas/postwoman/issues/826)
|
||||||
|
- The UI could use some improvements [\#825](https://github.com/liyasthomas/postwoman/issues/825)
|
||||||
|
- Postwoman won't build, produces 'FATAL Nuxt build error' [\#824](https://github.com/liyasthomas/postwoman/issues/824)
|
||||||
|
- Improve contrast of UI components in all themes [\#819](https://github.com/liyasthomas/postwoman/issues/819)
|
||||||
|
- Add an option to hide and/or collapse the right panel [\#818](https://github.com/liyasthomas/postwoman/issues/818)
|
||||||
|
- Docker Cannot log in normally in the container [\#817](https://github.com/liyasthomas/postwoman/issues/817)
|
||||||
|
- Body in Request [\#815](https://github.com/liyasthomas/postwoman/issues/815)
|
||||||
|
- How to run postwoman under reverse proxy [\#812](https://github.com/liyasthomas/postwoman/issues/812)
|
||||||
|
- Call local support [\#811](https://github.com/liyasthomas/postwoman/issues/811)
|
||||||
|
- feature [\#810](https://github.com/liyasthomas/postwoman/issues/810)
|
||||||
|
- support response json array [\#805](https://github.com/liyasthomas/postwoman/issues/805)
|
||||||
|
- socket binnery support [\#801](https://github.com/liyasthomas/postwoman/issues/801)
|
||||||
|
- Ability to join and leave rooms in Socket.IO connection [\#796](https://github.com/liyasthomas/postwoman/issues/796)
|
||||||
|
- How can I synchronize my data on local? [\#794](https://github.com/liyasthomas/postwoman/issues/794)
|
||||||
|
- I cant login [\#792](https://github.com/liyasthomas/postwoman/issues/792)
|
||||||
|
- Unresolved merge conflict in index.vue.orig [\#786](https://github.com/liyasthomas/postwoman/issues/786)
|
||||||
|
- You send data my request to Google [\#780](https://github.com/liyasthomas/postwoman/issues/780)
|
||||||
|
- I have question by \#750, it's closed,but my problem is still.... [\#770](https://github.com/liyasthomas/postwoman/issues/770)
|
||||||
|
- Add Format Body option [\#767](https://github.com/liyasthomas/postwoman/issues/767)
|
||||||
|
- Body scroll after modal is open [\#766](https://github.com/liyasthomas/postwoman/issues/766)
|
||||||
|
- text.match is not a function [\#764](https://github.com/liyasthomas/postwoman/issues/764)
|
||||||
|
- Request : Copy response headers [\#763](https://github.com/liyasthomas/postwoman/issues/763)
|
||||||
|
- The accordion \(expand\) labels are out of place on mobile [\#762](https://github.com/liyasthomas/postwoman/issues/762)
|
||||||
|
- why does the graphql case can't be saved? [\#761](https://github.com/liyasthomas/postwoman/issues/761)
|
||||||
|
- Mobile responsiveness issues [\#760](https://github.com/liyasthomas/postwoman/issues/760)
|
||||||
|
- Allow importing environment variables via Postman environment json files [\#759](https://github.com/liyasthomas/postwoman/issues/759)
|
||||||
|
- Report abuse: liyasthomas/postwoman \(Contact Links\) [\#754](https://github.com/liyasthomas/postwoman/issues/754)
|
||||||
|
- Report abuse: liyasthomas/postwoman \(Contact Links\) [\#753](https://github.com/liyasthomas/postwoman/issues/753)
|
||||||
|
- Request headers kept same after changing request type [\#752](https://github.com/liyasthomas/postwoman/issues/752)
|
||||||
|
- I used it to post test,but response network error [\#750](https://github.com/liyasthomas/postwoman/issues/750)
|
||||||
|
- Add compatibility for postman collections & environments [\#746](https://github.com/liyasthomas/postwoman/issues/746)
|
||||||
|
- Improve documentation on how to use environments [\#742](https://github.com/liyasthomas/postwoman/issues/742)
|
||||||
|
- Add GraphQL syntax highlighting [\#741](https://github.com/liyasthomas/postwoman/issues/741)
|
||||||
|
- Broken link to translations branch [\#737](https://github.com/liyasthomas/postwoman/issues/737)
|
||||||
|
- Add docker Images for all Tags [\#722](https://github.com/liyasthomas/postwoman/issues/722)
|
||||||
|
- Provide post-build resources and default setting files [\#714](https://github.com/liyasthomas/postwoman/issues/714)
|
||||||
|
- Theme lacks of contrast [\#709](https://github.com/liyasthomas/postwoman/issues/709)
|
||||||
|
- Postwoman raiase a connection error when communicate with localhost. [\#708](https://github.com/liyasthomas/postwoman/issues/708)
|
||||||
|
- CORS issue when hosting [\#707](https://github.com/liyasthomas/postwoman/issues/707)
|
||||||
|
- Add a description to the request or collection when saving it [\#706](https://github.com/liyasthomas/postwoman/issues/706)
|
||||||
|
- Error: Network Error. Check console for details [\#704](https://github.com/liyasthomas/postwoman/issues/704)
|
||||||
|
- Save collections on account sync turn on [\#679](https://github.com/liyasthomas/postwoman/issues/679)
|
||||||
|
- Tests should be saved together with requests [\#643](https://github.com/liyasthomas/postwoman/issues/643)
|
||||||
|
- npm modules of postwoman's vue components [\#384](https://github.com/liyasthomas/postwoman/issues/384)
|
||||||
|
- \[request\] VS code extension [\#313](https://github.com/liyasthomas/postwoman/issues/313)
|
||||||
|
- remove prerequest \<\< \>\> bindings when pre-request script is toggled off [\#234](https://github.com/liyasthomas/postwoman/issues/234)
|
||||||
|
- Dynamic Headers [\#91](https://github.com/liyasthomas/postwoman/issues/91)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- chore\(deps\): bump @nuxtjs/sitemap from 2.2.1 to 2.3.0 [\#864](https://github.com/liyasthomas/postwoman/pull/864) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- docs: add sboulema as a contributor [\#863](https://github.com/liyasthomas/postwoman/pull/863) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- Allow importing environment variables via Postman environment json files [\#862](https://github.com/liyasthomas/postwoman/pull/862) ([sboulema](https://github.com/sboulema))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.11.0 to 6.11.1 [\#861](https://github.com/liyasthomas/postwoman/pull/861) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Produce valid output when showing/copying as code [\#857](https://github.com/liyasthomas/postwoman/pull/857) ([Hydrophobefireman](https://github.com/Hydrophobefireman))
|
||||||
|
- dotenv [\#856](https://github.com/liyasthomas/postwoman/pull/856) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Save Collections/Environments on enabling sync [\#854](https://github.com/liyasthomas/postwoman/pull/854) ([sboulema](https://github.com/sboulema))
|
||||||
|
- chore\(deps\): bump firebase from 7.14.2 to 7.14.3 [\#853](https://github.com/liyasthomas/postwoman/pull/853) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Environment variables in collections [\#851](https://github.com/liyasthomas/postwoman/pull/851) ([sboulema](https://github.com/sboulema))
|
||||||
|
- Add format body option [\#847](https://github.com/liyasthomas/postwoman/pull/847) ([sboulema](https://github.com/sboulema))
|
||||||
|
- Save GraphQL Docs [\#846](https://github.com/liyasthomas/postwoman/pull/846) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps-dev\): bump node-sass from 4.14.0 to 4.14.1 [\#844](https://github.com/liyasthomas/postwoman/pull/844) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Remove not-deleted index.vue merge file [\#842](https://github.com/liyasthomas/postwoman/pull/842) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- URL Path Parameters \#834 [\#840](https://github.com/liyasthomas/postwoman/pull/840) ([sboulema](https://github.com/sboulema))
|
||||||
|
- chore\(deps\): remove stale dependency [\#839](https://github.com/liyasthomas/postwoman/pull/839) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- GraphQL Query Editor Syntax Highlighting [\#838](https://github.com/liyasthomas/postwoman/pull/838) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.2.1 to 10.2.2 [\#837](https://github.com/liyasthomas/postwoman/pull/837) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(store\): better code structure [\#835](https://github.com/liyasthomas/postwoman/pull/835) ([jameslahm](https://github.com/jameslahm))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.10.2 to 5.10.3 [\#832](https://github.com/liyasthomas/postwoman/pull/832) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.10.1 to 6.11.0 [\#831](https://github.com/liyasthomas/postwoman/pull/831) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.2.0 to 10.2.1 [\#830](https://github.com/liyasthomas/postwoman/pull/830) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Add ability to navigate through message history [\#828](https://github.com/liyasthomas/postwoman/pull/828) ([jinyus](https://github.com/jinyus))
|
||||||
|
- chore\(config\): delete render option [\#823](https://github.com/liyasthomas/postwoman/pull/823) ([jameslahm](https://github.com/jameslahm))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.4.1 to 4.5.0 [\#822](https://github.com/liyasthomas/postwoman/pull/822) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.1.7 to 10.2.0 [\#821](https://github.com/liyasthomas/postwoman/pull/821) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Realtime SocketIO support for json user input [\#820](https://github.com/liyasthomas/postwoman/pull/820) ([feydan](https://github.com/feydan))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.10.1 to 5.10.2 [\#816](https://github.com/liyasthomas/postwoman/pull/816) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Modify responseType by Object\(json\) or Array\(json5\) [\#813](https://github.com/liyasthomas/postwoman/pull/813) ([shtakai](https://github.com/shtakai))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.9.2 to 6.10.1 [\#809](https://github.com/liyasthomas/postwoman/pull/809) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.14.1 to 7.14.2 [\#808](https://github.com/liyasthomas/postwoman/pull/808) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump node-sass from 4.13.1 to 4.14.0 [\#807](https://github.com/liyasthomas/postwoman/pull/807) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/sitemap from 2.2.0 to 2.2.1 [\#806](https://github.com/liyasthomas/postwoman/pull/806) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.10.0 to 5.10.1 [\#803](https://github.com/liyasthomas/postwoman/pull/803) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.9.1 to 6.9.2 [\#802](https://github.com/liyasthomas/postwoman/pull/802) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.1.6 to 10.1.7 [\#800](https://github.com/liyasthomas/postwoman/pull/800) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump prettier from 2.0.4 to 2.0.5 [\#799](https://github.com/liyasthomas/postwoman/pull/799) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.9.7 to 5.10.0 [\#798](https://github.com/liyasthomas/postwoman/pull/798) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.4.0 to 4.4.1 [\#797](https://github.com/liyasthomas/postwoman/pull/797) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Listen to all events in socket.io connection [\#795](https://github.com/liyasthomas/postwoman/pull/795) ([konradkalemba](https://github.com/konradkalemba))
|
||||||
|
- Fix postman import with empty url [\#791](https://github.com/liyasthomas/postwoman/pull/791) ([Nikita240](https://github.com/Nikita240))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.1.5 to 10.1.6 [\#789](https://github.com/liyasthomas/postwoman/pull/789) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.1.3 to 10.1.5 [\#787](https://github.com/liyasthomas/postwoman/pull/787) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.14.0 to 7.14.1 [\#782](https://github.com/liyasthomas/postwoman/pull/782) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump yargs-parser from 18.1.2 to 18.1.3 [\#781](https://github.com/liyasthomas/postwoman/pull/781) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump start-server-and-test from 1.10.11 to 1.11.0 [\#778](https://github.com/liyasthomas/postwoman/pull/778) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.8.1 to 6.9.1 [\#776](https://github.com/liyasthomas/postwoman/pull/776) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump ace-builds from 1.4.9 to 1.4.11 [\#775](https://github.com/liyasthomas/postwoman/pull/775) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.3.0 to 4.4.0 [\#774](https://github.com/liyasthomas/postwoman/pull/774) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.13.2 to 7.14.0 [\#758](https://github.com/liyasthomas/postwoman/pull/758) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump husky from 4.2.3 to 4.2.5 [\#757](https://github.com/liyasthomas/postwoman/pull/757) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.1.2 to 10.1.3 [\#756](https://github.com/liyasthomas/postwoman/pull/756) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Fix indicator if extension is installed [\#748](https://github.com/liyasthomas/postwoman/pull/748) ([levrik](https://github.com/levrik))
|
||||||
|
- Remove support for legacy extensions [\#747](https://github.com/liyasthomas/postwoman/pull/747) ([levrik](https://github.com/levrik))
|
||||||
|
- Fix GQL introspection query not sent through extension [\#745](https://github.com/liyasthomas/postwoman/pull/745) ([levrik](https://github.com/levrik))
|
||||||
|
- chore\(deps-dev\): bump prettier from 2.0.2 to 2.0.4 [\#744](https://github.com/liyasthomas/postwoman/pull/744) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/sitemap from 2.1.0 to 2.2.0 [\#743](https://github.com/liyasthomas/postwoman/pull/743) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.1.1 to 10.1.2 [\#740](https://github.com/liyasthomas/postwoman/pull/740) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.8.0 to 6.8.1 [\#736](https://github.com/liyasthomas/postwoman/pull/736) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump ace-builds from 1.4.8 to 1.4.9 [\#735](https://github.com/liyasthomas/postwoman/pull/735) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.13.1 to 7.13.2 [\#734](https://github.com/liyasthomas/postwoman/pull/734) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump vue-virtual-scroll-list from 1.4.6 to 1.4.7 [\#733](https://github.com/liyasthomas/postwoman/pull/733) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.7.2 to 6.8.0 [\#732](https://github.com/liyasthomas/postwoman/pull/732) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt from 2.12.1 to 2.12.2 [\#729](https://github.com/liyasthomas/postwoman/pull/729) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.7.1 to 6.7.2 [\#728](https://github.com/liyasthomas/postwoman/pull/728) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.1.0 to 10.1.1 [\#727](https://github.com/liyasthomas/postwoman/pull/727) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.2.0 to 4.3.0 [\#726](https://github.com/liyasthomas/postwoman/pull/726) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.0.10 to 10.1.0 [\#725](https://github.com/liyasthomas/postwoman/pull/725) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.7.0 to 6.7.1 [\#724](https://github.com/liyasthomas/postwoman/pull/724) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.9.6 to 5.9.7 [\#723](https://github.com/liyasthomas/postwoman/pull/723) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.0.9 to 10.0.10 [\#721](https://github.com/liyasthomas/postwoman/pull/721) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.9.5 to 5.9.6 [\#719](https://github.com/liyasthomas/postwoman/pull/719) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/sitemap from 2.0.1 to 2.1.0 [\#718](https://github.com/liyasthomas/postwoman/pull/718) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.13.0 to 7.13.1 [\#717](https://github.com/liyasthomas/postwoman/pull/717) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump yargs-parser from 18.1.1 to 18.1.2 [\#713](https://github.com/liyasthomas/postwoman/pull/713) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.6.1 to 6.7.0 [\#712](https://github.com/liyasthomas/postwoman/pull/712) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt from 2.12.0 to 2.12.1 [\#711](https://github.com/liyasthomas/postwoman/pull/711) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.12.0 to 7.13.0 [\#710](https://github.com/liyasthomas/postwoman/pull/710) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Updating the UI and style files [\#705](https://github.com/liyasthomas/postwoman/pull/705) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.0.8 to 10.0.9 [\#703](https://github.com/liyasthomas/postwoman/pull/703) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Improving performance [\#702](https://github.com/liyasthomas/postwoman/pull/702) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- :package: Updating packages [\#701](https://github.com/liyasthomas/postwoman/pull/701) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps-dev\): bump prettier from 2.0.1 to 2.0.2 [\#700](https://github.com/liyasthomas/postwoman/pull/700) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump prettier from 1.19.1 to 2.0.1 [\#697](https://github.com/liyasthomas/postwoman/pull/697) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
|
||||||
|
## [v1.9.5](https://github.com/liyasthomas/postwoman/tree/v1.9.5) (2020-03-22)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.9.0...v1.9.5)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Test script is not run after failing request [\#644](https://github.com/liyasthomas/postwoman/issues/644)
|
||||||
|
- \[HELP\] Auth permission denied [\#621](https://github.com/liyasthomas/postwoman/issues/621)
|
||||||
|
- Can't login on Brave Browser [\#607](https://github.com/liyasthomas/postwoman/issues/607)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- \[UI/UX\] - Change place of Send button [\#696](https://github.com/liyasthomas/postwoman/issues/696)
|
||||||
|
- Support preview of JSON:API's "application/vnd.api+json" Content-Type [\#694](https://github.com/liyasthomas/postwoman/issues/694)
|
||||||
|
- Report Portal integration [\#691](https://github.com/liyasthomas/postwoman/issues/691)
|
||||||
|
- Docs request: how to prevent secrets from leaving local storage wrt. sync. [\#686](https://github.com/liyasthomas/postwoman/issues/686)
|
||||||
|
- \[bug\] - Can't make a request to HTTP [\#676](https://github.com/liyasthomas/postwoman/issues/676)
|
||||||
|
- Looking forward to that the postwoman Compatible 'swagger ' at next version [\#675](https://github.com/liyasthomas/postwoman/issues/675)
|
||||||
|
- Error: Network Error. Check console for details. [\#673](https://github.com/liyasthomas/postwoman/issues/673)
|
||||||
|
- \[Bug\] - Can't login to Github and Google [\#661](https://github.com/liyasthomas/postwoman/issues/661)
|
||||||
|
- A question that has been raised but not resolved [\#658](https://github.com/liyasthomas/postwoman/issues/658)
|
||||||
|
- An unknown error occurred whilst the proxy was processing your request. [\#656](https://github.com/liyasthomas/postwoman/issues/656)
|
||||||
|
- Running app from downloaded zip fails to compile [\#651](https://github.com/liyasthomas/postwoman/issues/651)
|
||||||
|
- Environment variable in path won't update [\#641](https://github.com/liyasthomas/postwoman/issues/641)
|
||||||
|
- Info: The current domain is not authorized for OAuth operations Error [\#637](https://github.com/liyasthomas/postwoman/issues/637)
|
||||||
|
- A suggestion for UI [\#635](https://github.com/liyasthomas/postwoman/issues/635)
|
||||||
|
- How to use postwoman for local development and testing [\#634](https://github.com/liyasthomas/postwoman/issues/634)
|
||||||
|
- How to debug localhost \(cors\) [\#630](https://github.com/liyasthomas/postwoman/issues/630)
|
||||||
|
- Support SocketIO connections on Realtime page [\#611](https://github.com/liyasthomas/postwoman/issues/611)
|
||||||
|
- Requests to local API returning error response [\#608](https://github.com/liyasthomas/postwoman/issues/608)
|
||||||
|
- Why does the URL input field display only one line [\#604](https://github.com/liyasthomas/postwoman/issues/604)
|
||||||
|
- Parameter list not showing JSON object fields \(force raw?\) [\#597](https://github.com/liyasthomas/postwoman/issues/597)
|
||||||
|
- Add setting to disable scroll animations [\#592](https://github.com/liyasthomas/postwoman/issues/592)
|
||||||
|
- Bigger URL and/or Path input field [\#581](https://github.com/liyasthomas/postwoman/issues/581)
|
||||||
|
- Ability to connect to a MQTT broker [\#342](https://github.com/liyasthomas/postwoman/issues/342)
|
||||||
|
- \[request\] Offline cross-platform native build [\#267](https://github.com/liyasthomas/postwoman/issues/267)
|
||||||
|
- On Save Update existing API [\#204](https://github.com/liyasthomas/postwoman/issues/204)
|
||||||
|
- Import and export environments from JSON [\#190](https://github.com/liyasthomas/postwoman/issues/190)
|
||||||
|
- Fast URL entry [\#62](https://github.com/liyasthomas/postwoman/issues/62)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Add application/vnd.api+json [\#695](https://github.com/liyasthomas/postwoman/pull/695) ([allthesignals](https://github.com/allthesignals))
|
||||||
|
- Fix raw input \(JSON\) [\#693](https://github.com/liyasthomas/postwoman/pull/693) ([leomp12](https://github.com/leomp12))
|
||||||
|
- chore\(deps\): bump firebase from 7.11.0 to 7.12.0 [\#689](https://github.com/liyasthomas/postwoman/pull/689) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump vuefire from 2.2.1 to 2.2.2 [\#688](https://github.com/liyasthomas/postwoman/pull/688) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.1.0 to 4.2.0 [\#685](https://github.com/liyasthomas/postwoman/pull/685) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump start-server-and-test from 1.10.10 to 1.10.11 [\#684](https://github.com/liyasthomas/postwoman/pull/684) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.6.0 to 6.6.1 [\#683](https://github.com/liyasthomas/postwoman/pull/683) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt from 2.11.0 to 2.12.0 [\#682](https://github.com/liyasthomas/postwoman/pull/682) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Fix setting default raw params [\#681](https://github.com/liyasthomas/postwoman/pull/681) ([leomp12](https://github.com/leomp12))
|
||||||
|
- Fix handling content type and raw input [\#678](https://github.com/liyasthomas/postwoman/pull/678) ([leomp12](https://github.com/leomp12))
|
||||||
|
- \[Snyk\] Security upgrade yargs-parser from 18.1.0 to 18.1.1 [\#674](https://github.com/liyasthomas/postwoman/pull/674) ([snyk-bot](https://github.com/snyk-bot))
|
||||||
|
- chore\(deps-dev\): bump start-server-and-test from 1.10.9 to 1.10.10 [\#672](https://github.com/liyasthomas/postwoman/pull/672) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.10.0 to 7.11.0 [\#671](https://github.com/liyasthomas/postwoman/pull/671) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ✅ Updating tests [\#669](https://github.com/liyasthomas/postwoman/pull/669) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Updating tests [\#668](https://github.com/liyasthomas/postwoman/pull/668) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- APIs [\#667](https://github.com/liyasthomas/postwoman/pull/667) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Insecure Websocket connection issue while connecting to MQTT broker. [\#666](https://github.com/liyasthomas/postwoman/pull/666) ([rahulnpadalkar](https://github.com/rahulnpadalkar))
|
||||||
|
- Improving performance [\#664](https://github.com/liyasthomas/postwoman/pull/664) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Feature/mqtt [\#663](https://github.com/liyasthomas/postwoman/pull/663) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Added Support for MQTT [\#662](https://github.com/liyasthomas/postwoman/pull/662) ([rahulnpadalkar](https://github.com/rahulnpadalkar))
|
||||||
|
- chore\(deps\): bump yargs-parser from 18.0.0 to 18.1.0 [\#660](https://github.com/liyasthomas/postwoman/pull/660) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump start-server-and-test from 1.10.8 to 1.10.9 [\#659](https://github.com/liyasthomas/postwoman/pull/659) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Added icon slot to tabs [\#657](https://github.com/liyasthomas/postwoman/pull/657) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Refactor/ui [\#655](https://github.com/liyasthomas/postwoman/pull/655) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- even [\#654](https://github.com/liyasthomas/postwoman/pull/654) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps\): bump yargs-parser from 17.0.0 to 18.0.0 [\#653](https://github.com/liyasthomas/postwoman/pull/653) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.9.3 to 7.10.0 [\#652](https://github.com/liyasthomas/postwoman/pull/652) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Added the ability to prettify GraphQL queries [\#650](https://github.com/liyasthomas/postwoman/pull/650) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Add http/https support to socketio url valid [\#648](https://github.com/liyasthomas/postwoman/pull/648) ([moonrailgun](https://github.com/moonrailgun))
|
||||||
|
- Refactor/ui [\#647](https://github.com/liyasthomas/postwoman/pull/647) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Even [\#646](https://github.com/liyasthomas/postwoman/pull/646) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Run tests even after failed request [\#645](https://github.com/liyasthomas/postwoman/pull/645) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Feature: add socket io support [\#640](https://github.com/liyasthomas/postwoman/pull/640) ([moonrailgun](https://github.com/moonrailgun))
|
||||||
|
- Removed linting for the collection docs import editor [\#639](https://github.com/liyasthomas/postwoman/pull/639) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Moving or renaming files [\#638](https://github.com/liyasthomas/postwoman/pull/638) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Refactor/ui [\#636](https://github.com/liyasthomas/postwoman/pull/636) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Updated messages for when GraphQL Get Schema fails [\#633](https://github.com/liyasthomas/postwoman/pull/633) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Minor GraphQL page improvements [\#631](https://github.com/liyasthomas/postwoman/pull/631) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||||
|
- Ignore empty GQL Variable Strings [\#629](https://github.com/liyasthomas/postwoman/pull/629) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.0.2 to 4.1.0 [\#628](https://github.com/liyasthomas/postwoman/pull/628) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.5.0 to 6.6.0 [\#627](https://github.com/liyasthomas/postwoman/pull/627) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.9.1 to 7.9.3 [\#626](https://github.com/liyasthomas/postwoman/pull/626) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/google-tag-manager from 2.3.1 to 2.3.2 [\#625](https://github.com/liyasthomas/postwoman/pull/625) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- test: purge travis [\#623](https://github.com/liyasthomas/postwoman/pull/623) ([yubathom](https://github.com/yubathom))
|
||||||
|
- Added shortcut to quickly run the GraphQL query [\#620](https://github.com/liyasthomas/postwoman/pull/620) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- docs: add dmitryyankowski as a contributor [\#619](https://github.com/liyasthomas/postwoman/pull/619) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- Link multiple auth providers [\#618](https://github.com/liyasthomas/postwoman/pull/618) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Add --staged parameter to pretty-quick pre-commit [\#617](https://github.com/liyasthomas/postwoman/pull/617) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||||
|
- :bug: FIxed URI not updating on Clear content, minor formData improve… [\#612](https://github.com/liyasthomas/postwoman/pull/612) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Update proxy information. [\#610](https://github.com/liyasthomas/postwoman/pull/610) ([NBTX](https://github.com/NBTX))
|
||||||
|
- Fixed install extension toast appearing even when extension is installed [\#609](https://github.com/liyasthomas/postwoman/pull/609) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps-dev\): bump lint-staged from 10.0.7 to 10.0.8 [\#606](https://github.com/liyasthomas/postwoman/pull/606) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- JSON linting in the code editor [\#605](https://github.com/liyasthomas/postwoman/pull/605) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Added regex to handle url parts [\#603](https://github.com/liyasthomas/postwoman/pull/603) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||||
|
- GraphQL page improvements, and more [\#602](https://github.com/liyasthomas/postwoman/pull/602) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||||
|
- I18n [\#601](https://github.com/liyasthomas/postwoman/pull/601) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- feat\(i18n\): add Korean [\#600](https://github.com/liyasthomas/postwoman/pull/600) ([9j](https://github.com/9j))
|
||||||
|
- Improve page load/unload experience \(remove FOUCs\) [\#599](https://github.com/liyasthomas/postwoman/pull/599) ([NBTX](https://github.com/NBTX))
|
||||||
|
- Feature: Add prettier/pretty-quick formatting w/ Husky pre-commit [\#596](https://github.com/liyasthomas/postwoman/pull/596) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||||
|
|
||||||
|
## [v1.9.0](https://github.com/liyasthomas/postwoman/tree/v1.9.0) (2020-02-24)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.8.0...v1.9.0)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Auto Theme Selection is kinda difficult to see [\#563](https://github.com/liyasthomas/postwoman/issues/563)
|
||||||
|
- Can't send request to localhost via Chrome extention [\#560](https://github.com/liyasthomas/postwoman/issues/560)
|
||||||
|
- Validation for duplicate collection ignores letter case [\#547](https://github.com/liyasthomas/postwoman/issues/547)
|
||||||
|
- Build failed [\#327](https://github.com/liyasthomas/postwoman/issues/327)
|
||||||
|
- Fixed typo in translation file for Auto theme [\#556](https://github.com/liyasthomas/postwoman/pull/556) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- don't run [\#577](https://github.com/liyasthomas/postwoman/issues/577)
|
||||||
|
- Get correct response data but occurs with error "Cannot read property 'value' of undefined" [\#575](https://github.com/liyasthomas/postwoman/issues/575)
|
||||||
|
- firebase_app\_\_WEBPACK_IMPORTED_MODULE_2\_\_\_default.a.firestore is not a function [\#558](https://github.com/liyasthomas/postwoman/issues/558)
|
||||||
|
- Disable SSL cert for websockets [\#557](https://github.com/liyasthomas/postwoman/issues/557)
|
||||||
|
- relative module not found during start [\#552](https://github.com/liyasthomas/postwoman/issues/552)
|
||||||
|
- Feature Request: Subfolders [\#540](https://github.com/liyasthomas/postwoman/issues/540)
|
||||||
|
- Feature request: Keyboard shortcuts for folder creation [\#539](https://github.com/liyasthomas/postwoman/issues/539)
|
||||||
|
- Add max-height and overflow: auto to "parameter list" textarea [\#532](https://github.com/liyasthomas/postwoman/issues/532)
|
||||||
|
- Friendly minded GraphQL [\#468](https://github.com/liyasthomas/postwoman/issues/468)
|
||||||
|
- multipart/form-data support [\#434](https://github.com/liyasthomas/postwoman/issues/434)
|
||||||
|
- IE Support [\#386](https://github.com/liyasthomas/postwoman/issues/386)
|
||||||
|
- ⏬ Import a Postman's Collection [\#333](https://github.com/liyasthomas/postwoman/issues/333)
|
||||||
|
- Implement pre-request and post-request scripts \(and request chaining\) [\#218](https://github.com/liyasthomas/postwoman/issues/218)
|
||||||
|
- Environment management and configuration [\#147](https://github.com/liyasthomas/postwoman/issues/147)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- POST request body editor reacts to the content type [\#594](https://github.com/liyasthomas/postwoman/pull/594) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Fix variablesJSONString store default for GraphQL page [\#593](https://github.com/liyasthomas/postwoman/pull/593) ([dmitryyankowski](https://github.com/dmitryyankowski))
|
||||||
|
- Environment Mangement [\#591](https://github.com/liyasthomas/postwoman/pull/591) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||||
|
- GraphQL Query Autocompletion [\#590](https://github.com/liyasthomas/postwoman/pull/590) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Refactor/lint [\#589](https://github.com/liyasthomas/postwoman/pull/589) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Even [\#588](https://github.com/liyasthomas/postwoman/pull/588) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps\): bump firebase from 7.9.0 to 7.9.1 [\#587](https://github.com/liyasthomas/postwoman/pull/587) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Even [\#586](https://github.com/liyasthomas/postwoman/pull/586) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps\): bump firebase from 7.8.2 to 7.9.0 [\#585](https://github.com/liyasthomas/postwoman/pull/585) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump vue-virtual-scroll-list from 1.4.5 to 1.4.6 [\#584](https://github.com/liyasthomas/postwoman/pull/584) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Adapt extension check to new extensions [\#583](https://github.com/liyasthomas/postwoman/pull/583) ([levrik](https://github.com/levrik))
|
||||||
|
- Update link to extension repo in README [\#582](https://github.com/liyasthomas/postwoman/pull/582) ([levrik](https://github.com/levrik))
|
||||||
|
- Even [\#579](https://github.com/liyasthomas/postwoman/pull/579) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Refactor/lint [\#578](https://github.com/liyasthomas/postwoman/pull/578) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps\): bump vue-virtual-scroll-list from 1.4.4 to 1.4.5 [\#576](https://github.com/liyasthomas/postwoman/pull/576) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Postman collection parsing [\#574](https://github.com/liyasthomas/postwoman/pull/574) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||||
|
- Unify Chrome and Firefox extensions [\#573](https://github.com/liyasthomas/postwoman/pull/573) ([levrik](https://github.com/levrik))
|
||||||
|
- fix: drop the toast which doesn't show up [\#572](https://github.com/liyasthomas/postwoman/pull/572) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- :sparkles: Native share + updated meta description [\#571](https://github.com/liyasthomas/postwoman/pull/571) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps\): bump firebase from 7.8.1 to 7.8.2 [\#570](https://github.com/liyasthomas/postwoman/pull/570) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.0.1 to 4.0.2 [\#569](https://github.com/liyasthomas/postwoman/pull/569) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Added create collection and save request syncs [\#568](https://github.com/liyasthomas/postwoman/pull/568) ([JacobAnavisca](https://github.com/JacobAnavisca))
|
||||||
|
- Moved common headers to a separate file [\#566](https://github.com/liyasthomas/postwoman/pull/566) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(deps-dev\): bump cypress from 4.0.0 to 4.0.1 [\#565](https://github.com/liyasthomas/postwoman/pull/565) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump yargs-parser from 16.1.0 to 17.0.0 [\#564](https://github.com/liyasthomas/postwoman/pull/564) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump cypress from 3.8.3 to 4.0.0 [\#562](https://github.com/liyasthomas/postwoman/pull/562) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump firebase from 7.8.0 to 7.8.1 [\#561](https://github.com/liyasthomas/postwoman/pull/561) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore: use typeof as an operator and make use of localizable strings [\#559](https://github.com/liyasthomas/postwoman/pull/559) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- Support for Formdata [\#555](https://github.com/liyasthomas/postwoman/pull/555) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps\): bump @nuxtjs/pwa from 3.0.0-beta.19 to 3.0.0-beta.20 [\#554](https://github.com/liyasthomas/postwoman/pull/554) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.9.4 to 5.9.5 [\#553](https://github.com/liyasthomas/postwoman/pull/553) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Added toggle to decide whether extensions should be used [\#551](https://github.com/liyasthomas/postwoman/pull/551) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Show Ctrl instead of Command for shortcuts non-Apple platforms [\#549](https://github.com/liyasthomas/postwoman/pull/549) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- fix\(chore\): Take letter casing into account while checking for duplicate collection [\#548](https://github.com/liyasthomas/postwoman/pull/548) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- update e2e tests [\#546](https://github.com/liyasthomas/postwoman/pull/546) ([yubathom](https://github.com/yubathom))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.9.3 to 5.9.4 [\#545](https://github.com/liyasthomas/postwoman/pull/545) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Refactor [\#543](https://github.com/liyasthomas/postwoman/pull/543) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps\): bump firebase from 7.7.0 to 7.8.0 [\#542](https://github.com/liyasthomas/postwoman/pull/542) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump graphql from 14.5.8 to 14.6.0 [\#541](https://github.com/liyasthomas/postwoman/pull/541) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- i18n [\#538](https://github.com/liyasthomas/postwoman/pull/538) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Modification of French translations [\#537](https://github.com/liyasthomas/postwoman/pull/537) ([thomasbnt](https://github.com/thomasbnt))
|
||||||
|
- Even [\#535](https://github.com/liyasthomas/postwoman/pull/535) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Updated GraphQL Query Variable Editor [\#534](https://github.com/liyasthomas/postwoman/pull/534) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Updating spanish translation [\#529](https://github.com/liyasthomas/postwoman/pull/529) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- even merge [\#528](https://github.com/liyasthomas/postwoman/pull/528) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
|
||||||
|
## [v1.8.0](https://github.com/liyasthomas/postwoman/tree/v1.8.0) (2020-01-28)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.5.0...v1.8.0)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Warn the user if name field was left blank while creating a new collection [\#515](https://github.com/liyasthomas/postwoman/issues/515)
|
||||||
|
- Multiple collections with the same name shouldn't exist [\#509](https://github.com/liyasthomas/postwoman/issues/509)
|
||||||
|
- GraphQL String variables are null [\#497](https://github.com/liyasthomas/postwoman/issues/497)
|
||||||
|
- Post request body is empty [\#473](https://github.com/liyasthomas/postwoman/issues/473)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Allow importing Postman collections [\#524](https://github.com/liyasthomas/postwoman/issues/524)
|
||||||
|
- Request descriptions [\#511](https://github.com/liyasthomas/postwoman/issues/511)
|
||||||
|
- Sync collection with a cloud storage \(e.g: Google drive\) [\#507](https://github.com/liyasthomas/postwoman/issues/507)
|
||||||
|
- Ability to run all requests of a folder/collection [\#498](https://github.com/liyasthomas/postwoman/issues/498)
|
||||||
|
- Change import/export collection on requests page icon [\#495](https://github.com/liyasthomas/postwoman/issues/495)
|
||||||
|
- Application contains many hard-coded strings that aren't translatable [\#488](https://github.com/liyasthomas/postwoman/issues/488)
|
||||||
|
- import cURL error [\#477](https://github.com/liyasthomas/postwoman/issues/477)
|
||||||
|
- move to postwoman org [\#475](https://github.com/liyasthomas/postwoman/issues/475)
|
||||||
|
- Create standalone vue components of the request builder. [\#474](https://github.com/liyasthomas/postwoman/issues/474)
|
||||||
|
- ULR parsing and var auto creation [\#469](https://github.com/liyasthomas/postwoman/issues/469)
|
||||||
|
- What about additional loaders: + Pug, TypeScript, SASS, material-vue ? [\#467](https://github.com/liyasthomas/postwoman/issues/467)
|
||||||
|
- \[suggestion\] - Tests tab [\#465](https://github.com/liyasthomas/postwoman/issues/465)
|
||||||
|
- cookie not found support [\#443](https://github.com/liyasthomas/postwoman/issues/443)
|
||||||
|
- Feature Request: Consumer Driven Contract Testing [\#420](https://github.com/liyasthomas/postwoman/issues/420)
|
||||||
|
- Feature Request: Support OAuth2/OIDC [\#337](https://github.com/liyasthomas/postwoman/issues/337)
|
||||||
|
- Enable running proxy as a backend for Request Capture [\#325](https://github.com/liyasthomas/postwoman/issues/325)
|
||||||
|
- Label doesn't change when switching between collection requests [\#291](https://github.com/liyasthomas/postwoman/issues/291)
|
||||||
|
- Add DB cache [\#26](https://github.com/liyasthomas/postwoman/issues/26)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Enhancements [\#531](https://github.com/liyasthomas/postwoman/pull/531) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- Merge pull request \#530 from liyasthomas/feature/post-request-tests [\#530](https://github.com/liyasthomas/postwoman/pull/530) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Refactor [\#523](https://github.com/liyasthomas/postwoman/pull/523) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- chore\(deps\): bump nuxt-i18n from 6.4.1 to 6.5.0 [\#522](https://github.com/liyasthomas/postwoman/pull/522) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump v-tooltip from 2.0.2 to 2.0.3 [\#521](https://github.com/liyasthomas/postwoman/pull/521) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump cypress from 3.8.2 to 3.8.3 [\#520](https://github.com/liyasthomas/postwoman/pull/520) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Feature/post request tests [\#518](https://github.com/liyasthomas/postwoman/pull/518) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||||
|
- Validations for edit and create collections activity [\#516](https://github.com/liyasthomas/postwoman/pull/516) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- Auth [\#513](https://github.com/liyasthomas/postwoman/pull/513) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Support for Google Chrome Extension [\#512](https://github.com/liyasthomas/postwoman/pull/512) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Validate duplicate collections [\#510](https://github.com/liyasthomas/postwoman/pull/510) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- GraphQL query validation based on schema [\#508](https://github.com/liyasthomas/postwoman/pull/508) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Lint and refactor [\#506](https://github.com/liyasthomas/postwoman/pull/506) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Syntax Error marking in GraphQL query editor [\#505](https://github.com/liyasthomas/postwoman/pull/505) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Merge pull request \#504 from liyasthomas/dependabot/npm_and_yarn/node-sass-4.13.1 [\#504](https://github.com/liyasthomas/postwoman/pull/504) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump @nuxtjs/axios from 5.9.2 to 5.9.3 [\#503](https://github.com/liyasthomas/postwoman/pull/503) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps-dev\): bump sass-loader from 8.0.1 to 8.0.2 [\#502](https://github.com/liyasthomas/postwoman/pull/502) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- chore\(deps\): bump ace-builds from 1.4.7 to 1.4.8 [\#501](https://github.com/liyasthomas/postwoman/pull/501) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Refactoring proxy handling to be done in strategies [\#500](https://github.com/liyasthomas/postwoman/pull/500) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- 💚 Fixed \#497 [\#499](https://github.com/liyasthomas/postwoman/pull/499) ([pushrbx](https://github.com/pushrbx))
|
||||||
|
- Feat/firefox strategy [\#496](https://github.com/liyasthomas/postwoman/pull/496) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Firefox Extension compatibility [\#494](https://github.com/liyasthomas/postwoman/pull/494) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- i18n Japanese: Added new translations [\#492](https://github.com/liyasthomas/postwoman/pull/492) ([reefqi037](https://github.com/reefqi037))
|
||||||
|
- Merge pull request \#491 from liyasthomas/i18n [\#491](https://github.com/liyasthomas/postwoman/pull/491) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Replaced hard-coded strings with localizable strings [\#490](https://github.com/liyasthomas/postwoman/pull/490) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Network Strategies [\#487](https://github.com/liyasthomas/postwoman/pull/487) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- chore\(oauth\): Added method signatures as per JSDoc conventions [\#486](https://github.com/liyasthomas/postwoman/pull/486) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- chore: Minor tweaks [\#485](https://github.com/liyasthomas/postwoman/pull/485) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- ⬆️ Bump cypress from 3.8.1 to 3.8.2 [\#483](https://github.com/liyasthomas/postwoman/pull/483) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump sass-loader from 8.0.0 to 8.0.1 [\#482](https://github.com/liyasthomas/postwoman/pull/482) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.2 to 2.2.3 [\#481](https://github.com/liyasthomas/postwoman/pull/481) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- GraphQL Type Highlight and Links [\#479](https://github.com/liyasthomas/postwoman/pull/479) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- OAuth 2.0/OIDC Access Token Retrieval Support [\#476](https://github.com/liyasthomas/postwoman/pull/476) ([reefqi037](https://github.com/reefqi037))
|
||||||
|
|
||||||
|
## [v1.5.0](https://github.com/liyasthomas/postwoman/tree/v1.5.0) (2020-01-04)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v1.0.0...v1.5.0)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- WebSocket page freezes when pasting long URL [\#471](https://github.com/liyasthomas/postwoman/issues/471)
|
||||||
|
- API Documentation won't be generated [\#456](https://github.com/liyasthomas/postwoman/issues/456)
|
||||||
|
- Sharing Requests via link is not working [\#435](https://github.com/liyasthomas/postwoman/issues/435)
|
||||||
|
- URL input text is so stutters [\#412](https://github.com/liyasthomas/postwoman/issues/412)
|
||||||
|
- Save to collections after deleting all the collections causes an error page [\#390](https://github.com/liyasthomas/postwoman/issues/390)
|
||||||
|
- Bearer token doesn’t work with CORS when only authorization header is allowed [\#353](https://github.com/liyasthomas/postwoman/issues/353)
|
||||||
|
- Make the UI more compact [\#314](https://github.com/liyasthomas/postwoman/issues/314)
|
||||||
|
- Allow reserved characters on websocket URI [\#289](https://github.com/liyasthomas/postwoman/issues/289)
|
||||||
|
- Unable to edit or delete row in history table [\#281](https://github.com/liyasthomas/postwoman/issues/281)
|
||||||
|
- Allow url request with `/` at eol [\#275](https://github.com/liyasthomas/postwoman/issues/275)
|
||||||
|
- \[request\] localhost support [\#274](https://github.com/liyasthomas/postwoman/issues/274)
|
||||||
|
- Code generation for Fetch request type of some methods \(POST, PUT, PATCH\) won't be shown [\#268](https://github.com/liyasthomas/postwoman/issues/268)
|
||||||
|
- \[BUG\] \[UI\] \[Mobile\] Get results not scrollable. [\#266](https://github.com/liyasthomas/postwoman/issues/266)
|
||||||
|
- POSTing large raw JSON packets [\#265](https://github.com/liyasthomas/postwoman/issues/265)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Can WSDL be implemented, similar to SoapUI? [\#461](https://github.com/liyasthomas/postwoman/issues/461)
|
||||||
|
- Module not found: Error: Can't resolve '../.postwoman/version.json' [\#457](https://github.com/liyasthomas/postwoman/issues/457)
|
||||||
|
- \* ../.postwoman/version.json in ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./layouts/default.vue?vue&type=script&lang=js& friendly-errors 11:12:37 [\#448](https://github.com/liyasthomas/postwoman/issues/448)
|
||||||
|
- Raw Request Body should be supported to format the JSON string [\#446](https://github.com/liyasthomas/postwoman/issues/446)
|
||||||
|
- npm run dev module was not found: ../.postwoman/version.json [\#442](https://github.com/liyasthomas/postwoman/issues/442)
|
||||||
|
- graphql and websocket work, but http and https do not [\#441](https://github.com/liyasthomas/postwoman/issues/441)
|
||||||
|
- Can I test localhost? [\#433](https://github.com/liyasthomas/postwoman/issues/433)
|
||||||
|
- No 'Access-Control-Allow-Origin' [\#426](https://github.com/liyasthomas/postwoman/issues/426)
|
||||||
|
- When uninstall the PWA the "install PWA" link in postwoman.io isn't appear anymore [\#419](https://github.com/liyasthomas/postwoman/issues/419)
|
||||||
|
- Toggling options will reset the UI to English [\#417](https://github.com/liyasthomas/postwoman/issues/417)
|
||||||
|
- Ability to send Binary data using Postwoman [\#415](https://github.com/liyasthomas/postwoman/issues/415)
|
||||||
|
- Oh my dear god why don't we just wrap it in electron [\#413](https://github.com/liyasthomas/postwoman/issues/413)
|
||||||
|
- UI improvement suggestion for request method drop down [\#409](https://github.com/liyasthomas/postwoman/issues/409)
|
||||||
|
- Can I share a request with my team? [\#408](https://github.com/liyasthomas/postwoman/issues/408)
|
||||||
|
- Does it not support the post method? [\#403](https://github.com/liyasthomas/postwoman/issues/403)
|
||||||
|
- Post can't send request [\#401](https://github.com/liyasthomas/postwoman/issues/401)
|
||||||
|
- \[Bug\] fix header icons overlap [\#399](https://github.com/liyasthomas/postwoman/issues/399)
|
||||||
|
- Custom request method [\#398](https://github.com/liyasthomas/postwoman/issues/398)
|
||||||
|
- Improve translate for pt-BR \(i18n\) [\#395](https://github.com/liyasthomas/postwoman/issues/395)
|
||||||
|
- Raw input disabled is not working properly [\#394](https://github.com/liyasthomas/postwoman/issues/394)
|
||||||
|
- Input area is not clearly to identify for users because the dark mode [\#393](https://github.com/liyasthomas/postwoman/issues/393)
|
||||||
|
- \[UX\] Setting to make sidebar buttons small [\#389](https://github.com/liyasthomas/postwoman/issues/389)
|
||||||
|
- \[UX\] Improve responsive breaking points [\#388](https://github.com/liyasthomas/postwoman/issues/388)
|
||||||
|
- \[UX\] Hide history/collections [\#387](https://github.com/liyasthomas/postwoman/issues/387)
|
||||||
|
- Clearing shortcut overrides browser default [\#374](https://github.com/liyasthomas/postwoman/issues/374)
|
||||||
|
- Proxy server default configuration [\#373](https://github.com/liyasthomas/postwoman/issues/373)
|
||||||
|
- Intent to translate [\#367](https://github.com/liyasthomas/postwoman/issues/367)
|
||||||
|
- \[request\]: CLI possibilities [\#363](https://github.com/liyasthomas/postwoman/issues/363)
|
||||||
|
- Feature request: OAuth header support/integration [\#358](https://github.com/liyasthomas/postwoman/issues/358)
|
||||||
|
- Static builds on releases [\#352](https://github.com/liyasthomas/postwoman/issues/352)
|
||||||
|
- fix:SSE onclose handle [\#349](https://github.com/liyasthomas/postwoman/issues/349)
|
||||||
|
- i18n support [\#348](https://github.com/liyasthomas/postwoman/issues/348)
|
||||||
|
- Separate layers in dockerfile to improve image build [\#339](https://github.com/liyasthomas/postwoman/issues/339)
|
||||||
|
- Internal server environment usage requirements [\#336](https://github.com/liyasthomas/postwoman/issues/336)
|
||||||
|
- Server Sent Events [\#329](https://github.com/liyasthomas/postwoman/issues/329)
|
||||||
|
- Generate API documentation [\#326](https://github.com/liyasthomas/postwoman/issues/326)
|
||||||
|
- \[Request\] Use responses for next request? [\#324](https://github.com/liyasthomas/postwoman/issues/324)
|
||||||
|
- Auth info on WebSocket connections [\#321](https://github.com/liyasthomas/postwoman/issues/321)
|
||||||
|
- Set response panel to fullscreen [\#320](https://github.com/liyasthomas/postwoman/issues/320)
|
||||||
|
- Graphql support [\#312](https://github.com/liyasthomas/postwoman/issues/312)
|
||||||
|
- Keyboard shortcuts [\#302](https://github.com/liyasthomas/postwoman/issues/302)
|
||||||
|
- File/binary request body support [\#298](https://github.com/liyasthomas/postwoman/issues/298)
|
||||||
|
- Make response body area expandable [\#294](https://github.com/liyasthomas/postwoman/issues/294)
|
||||||
|
- It's possible to tab into read only and non-form elements [\#287](https://github.com/liyasthomas/postwoman/issues/287)
|
||||||
|
- Change cursor to disabled on disabled inputs [\#286](https://github.com/liyasthomas/postwoman/issues/286)
|
||||||
|
- Hover Styling on Inputs [\#285](https://github.com/liyasthomas/postwoman/issues/285)
|
||||||
|
- Focus Styles on Buttons [\#284](https://github.com/liyasthomas/postwoman/issues/284)
|
||||||
|
- Mobile can't see console for request errors [\#283](https://github.com/liyasthomas/postwoman/issues/283)
|
||||||
|
- Missing Focus on Inputs [\#279](https://github.com/liyasthomas/postwoman/issues/279)
|
||||||
|
- Download the request result into a file. [\#278](https://github.com/liyasthomas/postwoman/issues/278)
|
||||||
|
- Improve UI Contrast [\#277](https://github.com/liyasthomas/postwoman/issues/277)
|
||||||
|
- Duplicated query string in generated code [\#272](https://github.com/liyasthomas/postwoman/issues/272)
|
||||||
|
- Query parameters are duplicated [\#271](https://github.com/liyasthomas/postwoman/issues/271)
|
||||||
|
- Generated code is incorrect [\#269](https://github.com/liyasthomas/postwoman/issues/269)
|
||||||
|
- \[UI\] \[UX\] Allow app to take width of browser [\#236](https://github.com/liyasthomas/postwoman/issues/236)
|
||||||
|
- Extend syntax highlighting with ACE for pre-request script textarea [\#235](https://github.com/liyasthomas/postwoman/issues/235)
|
||||||
|
- Store pre-request scripts in history [\#233](https://github.com/liyasthomas/postwoman/issues/233)
|
||||||
|
- Lacking documentation and wiki [\#232](https://github.com/liyasthomas/postwoman/issues/232)
|
||||||
|
- Store the time spent on fetching a response [\#225](https://github.com/liyasthomas/postwoman/issues/225)
|
||||||
|
- I can't send POST method [\#210](https://github.com/liyasthomas/postwoman/issues/210)
|
||||||
|
- Cache view [\#188](https://github.com/liyasthomas/postwoman/issues/188)
|
||||||
|
- Handling request failures when build number is obtained from GitHub [\#122](https://github.com/liyasthomas/postwoman/issues/122)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- ⬆️ Bump @nuxtjs/axios from 5.9.0 to 5.9.2 [\#472](https://github.com/liyasthomas/postwoman/pull/472) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Added variables to graphql page. [\#464](https://github.com/liyasthomas/postwoman/pull/464) ([pushrbx](https://github.com/pushrbx))
|
||||||
|
- i18n [\#463](https://github.com/liyasthomas/postwoman/pull/463) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- ⬆️ Bump cypress from 3.8.0 to 3.8.1 [\#460](https://github.com/liyasthomas/postwoman/pull/460) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- i18n\(de-DE\): improve some translations [\#459](https://github.com/liyasthomas/postwoman/pull/459) ([gabschne](https://github.com/gabschne))
|
||||||
|
- bn-BD i18n [\#455](https://github.com/liyasthomas/postwoman/pull/455) ([hmtanbir](https://github.com/hmtanbir))
|
||||||
|
- API documentation page [\#451](https://github.com/liyasthomas/postwoman/pull/451) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- ⬆️ Bump @nuxtjs/axios from 5.8.0 to 5.9.0 [\#450](https://github.com/liyasthomas/postwoman/pull/450) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump nuxt from 2.10.2 to 2.11.0 [\#449](https://github.com/liyasthomas/postwoman/pull/449) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Various UI tweaks [\#439](https://github.com/liyasthomas/postwoman/pull/439) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- i18n [\#438](https://github.com/liyasthomas/postwoman/pull/438) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Burmese translation added [\#437](https://github.com/liyasthomas/postwoman/pull/437) ([ZattWine](https://github.com/ZattWine))
|
||||||
|
- chore: stick to Vue.js best practices [\#432](https://github.com/liyasthomas/postwoman/pull/432) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- Styled select input [\#431](https://github.com/liyasthomas/postwoman/pull/431) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Bumped dependencies and Improved UI contrast [\#430](https://github.com/liyasthomas/postwoman/pull/430) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- ⬆️ Bump cypress from 3.7.0 to 3.8.0 [\#429](https://github.com/liyasthomas/postwoman/pull/429) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- I18n [\#428](https://github.com/liyasthomas/postwoman/pull/428) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- I18n Japanese translation added [\#427](https://github.com/liyasthomas/postwoman/pull/427) ([reefqi037](https://github.com/reefqi037))
|
||||||
|
- Even [\#424](https://github.com/liyasthomas/postwoman/pull/424) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- I18n [\#423](https://github.com/liyasthomas/postwoman/pull/423) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- I18n German translation added [\#422](https://github.com/liyasthomas/postwoman/pull/422) ([NJannasch](https://github.com/NJannasch))
|
||||||
|
- Header key autocompletion [\#421](https://github.com/liyasthomas/postwoman/pull/421) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Update id-ID.js [\#416](https://github.com/liyasthomas/postwoman/pull/416) ([williamsp](https://github.com/williamsp))
|
||||||
|
- Improving translation for id-ID [\#414](https://github.com/liyasthomas/postwoman/pull/414) ([williamsp](https://github.com/williamsp))
|
||||||
|
- Fixing bug on request saving [\#410](https://github.com/liyasthomas/postwoman/pull/410) ([adevr](https://github.com/adevr))
|
||||||
|
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.1 to 2.2.2 [\#407](https://github.com/liyasthomas/postwoman/pull/407) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump vue-virtual-scroll-list from 1.4.3 to 1.4.4 [\#406](https://github.com/liyasthomas/postwoman/pull/406) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump nuxt-i18n from 6.4.0 to 6.4.1 [\#405](https://github.com/liyasthomas/postwoman/pull/405) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- I18n [\#404](https://github.com/liyasthomas/postwoman/pull/404) ([yubathom](https://github.com/yubathom))
|
||||||
|
- Custom methods support [\#400](https://github.com/liyasthomas/postwoman/pull/400) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- App UI [\#391](https://github.com/liyasthomas/postwoman/pull/391) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- i18n [\#383](https://github.com/liyasthomas/postwoman/pull/383) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Added Turkish Language Support [\#382](https://github.com/liyasthomas/postwoman/pull/382) ([AliAnilKocak](https://github.com/AliAnilKocak))
|
||||||
|
- Translated new words to Farsi lang [\#380](https://github.com/liyasthomas/postwoman/pull/380) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||||
|
- Two Way Data Binding \(v-model\) to Ace Editor component [\#379](https://github.com/liyasthomas/postwoman/pull/379) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- fix: twitter summary card image url [\#378](https://github.com/liyasthomas/postwoman/pull/378) ([peterpeterparker](https://github.com/peterpeterparker))
|
||||||
|
- Added nav shortcuts to GraphQL query and response, updated GraphQL shortcut icons [\#377](https://github.com/liyasthomas/postwoman/pull/377) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Bump cypress from 3.6.1 to 3.7.0 [\#376](https://github.com/liyasthomas/postwoman/pull/376) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Bump vuex-persist from 2.1.1 to 2.2.0 [\#375](https://github.com/liyasthomas/postwoman/pull/375) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- I18n [\#372](https://github.com/liyasthomas/postwoman/pull/372) ([EdikWang](https://github.com/EdikWang))
|
||||||
|
- Use GraphQL logo for GraphQL tab [\#371](https://github.com/liyasthomas/postwoman/pull/371) ([NBTX](https://github.com/NBTX))
|
||||||
|
- Update i18n keywords for Bahasa Indonesia [\#369](https://github.com/liyasthomas/postwoman/pull/369) ([wahwahid](https://github.com/wahwahid))
|
||||||
|
- Intent to translate to Spanish on I18n [\#368](https://github.com/liyasthomas/postwoman/pull/368) ([adlpaf](https://github.com/adlpaf))
|
||||||
|
- I18n [\#366](https://github.com/liyasthomas/postwoman/pull/366) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Add translations for FR/EN catalogues [\#364](https://github.com/liyasthomas/postwoman/pull/364) ([LaurentBrieu](https://github.com/LaurentBrieu))
|
||||||
|
- Added Bahasa Indonesia language support [\#362](https://github.com/liyasthomas/postwoman/pull/362) ([wahwahid](https://github.com/wahwahid))
|
||||||
|
- i18n [\#361](https://github.com/liyasthomas/postwoman/pull/361) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Add Simplified Chinese language [\#360](https://github.com/liyasthomas/postwoman/pull/360) ([EdikWang](https://github.com/EdikWang))
|
||||||
|
- Added Brazilian Portuguese language support [\#359](https://github.com/liyasthomas/postwoman/pull/359) ([tetri](https://github.com/tetri))
|
||||||
|
- Added Farsi language support [\#357](https://github.com/liyasthomas/postwoman/pull/357) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||||
|
- Adding french language basic [\#355](https://github.com/liyasthomas/postwoman/pull/355) ([thomasbnt](https://github.com/thomasbnt))
|
||||||
|
- Basic i18n support [\#351](https://github.com/liyasthomas/postwoman/pull/351) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Undo header/param/body param deletion [\#350](https://github.com/liyasthomas/postwoman/pull/350) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Added ability to run GraphQL queries [\#346](https://github.com/liyasthomas/postwoman/pull/346) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Add Proxy URL option [\#345](https://github.com/liyasthomas/postwoman/pull/345) ([NBTX](https://github.com/NBTX))
|
||||||
|
- ♻️ Refactor Functions [\#344](https://github.com/liyasthomas/postwoman/pull/344) ([athul](https://github.com/athul))
|
||||||
|
- refactor: minor improvements [\#343](https://github.com/liyasthomas/postwoman/pull/343) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
|
||||||
|
## [v1.0.0](https://github.com/liyasthomas/postwoman/tree/v1.0.0) (2019-11-04)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/v0.1.0...v1.0.0)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- Bearer Token value still left even after being cleared [\#212](https://github.com/liyasthomas/postwoman/issues/212)
|
||||||
|
- All changes in input fields lost when you switch to another page [\#203](https://github.com/liyasthomas/postwoman/issues/203)
|
||||||
|
- POST request json bodies aren't sent [\#180](https://github.com/liyasthomas/postwoman/issues/180)
|
||||||
|
- Headers turn into 0 : \[Object object\] [\#166](https://github.com/liyasthomas/postwoman/issues/166)
|
||||||
|
- Send Again Button Constantly Flickering [\#157](https://github.com/liyasthomas/postwoman/issues/157)
|
||||||
|
- There are cross-domain problems [\#128](https://github.com/liyasthomas/postwoman/issues/128)
|
||||||
|
- Raw requests are not being sent [\#124](https://github.com/liyasthomas/postwoman/issues/124)
|
||||||
|
- Request Body Is Not Sent [\#113](https://github.com/liyasthomas/postwoman/issues/113)
|
||||||
|
- default menu option - 'Http' is not highlighted when launched from installed pwa app \(UI bug\) [\#100](https://github.com/liyasthomas/postwoman/issues/100)
|
||||||
|
- App is broken with old history in localStorage [\#74](https://github.com/liyasthomas/postwoman/issues/74)
|
||||||
|
- Last added history entry is removed automatically after refresh [\#66](https://github.com/liyasthomas/postwoman/issues/66)
|
||||||
|
- Cannot use localhost as base url [\#56](https://github.com/liyasthomas/postwoman/issues/56)
|
||||||
|
- \[CORS\] No 'Access-Control-Allow-Origin' header is present on the requested resource [\#2](https://github.com/liyasthomas/postwoman/issues/2)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Section labels don't display properly in Firefox [\#237](https://github.com/liyasthomas/postwoman/issues/237)
|
||||||
|
- Unsupported URLs \[BUG\]? [\#229](https://github.com/liyasthomas/postwoman/issues/229)
|
||||||
|
- Credentials are still being included in Permalink even when "Include in URL" is turned off [\#227](https://github.com/liyasthomas/postwoman/issues/227)
|
||||||
|
- Display sendRequest runtime errors in the console [\#206](https://github.com/liyasthomas/postwoman/issues/206)
|
||||||
|
- Chain requests. Execute a bunch of requests one by one and produce results [\#196](https://github.com/liyasthomas/postwoman/issues/196)
|
||||||
|
- Allow User to Choose Whether to Include Authentication in Permalink [\#178](https://github.com/liyasthomas/postwoman/issues/178)
|
||||||
|
- Allow HTTP \(not HTTPS\) on postwoman.io [\#175](https://github.com/liyasthomas/postwoman/issues/175)
|
||||||
|
- Docker-compose in development [\#168](https://github.com/liyasthomas/postwoman/issues/168)
|
||||||
|
- Add Docker [\#164](https://github.com/liyasthomas/postwoman/issues/164)
|
||||||
|
- Missing "Landing/start page" [\#162](https://github.com/liyasthomas/postwoman/issues/162)
|
||||||
|
- Response with content-type "application/hal+json" shows as \[Object object\] [\#158](https://github.com/liyasthomas/postwoman/issues/158)
|
||||||
|
- Clear Input [\#155](https://github.com/liyasthomas/postwoman/issues/155)
|
||||||
|
- A place to discuss [\#149](https://github.com/liyasthomas/postwoman/issues/149)
|
||||||
|
- Inconsistent version name [\#141](https://github.com/liyasthomas/postwoman/issues/141)
|
||||||
|
- introduce some script language to parse the response and pass environment variable as request parameter [\#139](https://github.com/liyasthomas/postwoman/issues/139)
|
||||||
|
- Add links to the footer version and commit sha [\#134](https://github.com/liyasthomas/postwoman/issues/134)
|
||||||
|
- Please add a label for each request. It will be helpful. [\#133](https://github.com/liyasthomas/postwoman/issues/133)
|
||||||
|
- Use 'icon buttons' instead of 'text buttons' [\#130](https://github.com/liyasthomas/postwoman/issues/130)
|
||||||
|
- Change .editorconfig [\#115](https://github.com/liyasthomas/postwoman/issues/115)
|
||||||
|
- \[UX\] Provide Focus State for Buttons, etc. [\#112](https://github.com/liyasthomas/postwoman/issues/112)
|
||||||
|
- Autoresize the textarea [\#102](https://github.com/liyasthomas/postwoman/issues/102)
|
||||||
|
- Content-Type revamping [\#99](https://github.com/liyasthomas/postwoman/issues/99)
|
||||||
|
- Add linter semistandard [\#98](https://github.com/liyasthomas/postwoman/issues/98)
|
||||||
|
- Add version number in footer [\#97](https://github.com/liyasthomas/postwoman/issues/97)
|
||||||
|
- Show "Send" button all over the page or enable hotkeys [\#94](https://github.com/liyasthomas/postwoman/issues/94)
|
||||||
|
- Import request from cURL [\#93](https://github.com/liyasthomas/postwoman/issues/93)
|
||||||
|
- Search on History [\#92](https://github.com/liyasthomas/postwoman/issues/92)
|
||||||
|
- Add support for "application/hal+json" Content-Type [\#88](https://github.com/liyasthomas/postwoman/issues/88)
|
||||||
|
- The query string is built incorrectly when the path contains a parameter [\#87](https://github.com/liyasthomas/postwoman/issues/87)
|
||||||
|
- The history doesn't show a date with the timestamp. [\#81](https://github.com/liyasthomas/postwoman/issues/81)
|
||||||
|
- Option to Copy request as Fetch or XHR Or CURL [\#76](https://github.com/liyasthomas/postwoman/issues/76)
|
||||||
|
- Not working on Brave Browser anymore [\#71](https://github.com/liyasthomas/postwoman/issues/71)
|
||||||
|
- Why da fuq is your name plastered all over the README? [\#70](https://github.com/liyasthomas/postwoman/issues/70)
|
||||||
|
- Comparison with Postman is missing [\#69](https://github.com/liyasthomas/postwoman/issues/69)
|
||||||
|
- Add Tests [\#65](https://github.com/liyasthomas/postwoman/issues/65)
|
||||||
|
- HTTP request with different library [\#61](https://github.com/liyasthomas/postwoman/issues/61)
|
||||||
|
- Editorconfig file [\#60](https://github.com/liyasthomas/postwoman/issues/60)
|
||||||
|
- 500 this.isValidURL is not a function [\#58](https://github.com/liyasthomas/postwoman/issues/58)
|
||||||
|
- Request Headers [\#57](https://github.com/liyasthomas/postwoman/issues/57)
|
||||||
|
- Colored response codes based on status code [\#46](https://github.com/liyasthomas/postwoman/issues/46)
|
||||||
|
- Improve SEO [\#45](https://github.com/liyasthomas/postwoman/issues/45)
|
||||||
|
- Add html preview to response section [\#41](https://github.com/liyasthomas/postwoman/issues/41)
|
||||||
|
- websocket support [\#40](https://github.com/liyasthomas/postwoman/issues/40)
|
||||||
|
- Styling with Tailwindcss [\#38](https://github.com/liyasthomas/postwoman/issues/38)
|
||||||
|
- Not Working in IE 11 [\#37](https://github.com/liyasthomas/postwoman/issues/37)
|
||||||
|
- Raw request body for POST requests and Authorization key/value in Header [\#36](https://github.com/liyasthomas/postwoman/issues/36)
|
||||||
|
- Code highlight on response body [\#33](https://github.com/liyasthomas/postwoman/issues/33)
|
||||||
|
- Template selector [\#32](https://github.com/liyasthomas/postwoman/issues/32)
|
||||||
|
- Vue template [\#31](https://github.com/liyasthomas/postwoman/issues/31)
|
||||||
|
- Add copy response to clipboard button [\#30](https://github.com/liyasthomas/postwoman/issues/30)
|
||||||
|
- Ability to store/share/create collections [\#29](https://github.com/liyasthomas/postwoman/issues/29)
|
||||||
|
- PWA not installable [\#19](https://github.com/liyasthomas/postwoman/issues/19)
|
||||||
|
- Send request on Enter Key press [\#17](https://github.com/liyasthomas/postwoman/issues/17)
|
||||||
|
- Simple Misspelling [\#8](https://github.com/liyasthomas/postwoman/issues/8)
|
||||||
|
- Readable [\#5](https://github.com/liyasthomas/postwoman/issues/5)
|
||||||
|
- Serialize a request into JSON? [\#4](https://github.com/liyasthomas/postwoman/issues/4)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- docs: add liyasthomas as a contributor [\#264](https://github.com/liyasthomas/postwoman/pull/264) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add jamesgeorge007 as a contributor [\#263](https://github.com/liyasthomas/postwoman/pull/263) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add NBTX as a contributor [\#262](https://github.com/liyasthomas/postwoman/pull/262) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- Fix .all-contributorsrc badge template. [\#260](https://github.com/liyasthomas/postwoman/pull/260) ([NBTX](https://github.com/NBTX))
|
||||||
|
- docs: add hosseinnedaee as a contributor [\#259](https://github.com/liyasthomas/postwoman/pull/259) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add nityanandagohain as a contributor [\#257](https://github.com/liyasthomas/postwoman/pull/257) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add JacobAnavisca as a contributor [\#256](https://github.com/liyasthomas/postwoman/pull/256) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add izerozlu as a contributor [\#255](https://github.com/liyasthomas/postwoman/pull/255) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add vlad0337187 as a contributor [\#254](https://github.com/liyasthomas/postwoman/pull/254) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add AndrewBastin as a contributor [\#253](https://github.com/liyasthomas/postwoman/pull/253) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add terranblake as a contributor [\#252](https://github.com/liyasthomas/postwoman/pull/252) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add nickpalenchar as a contributor [\#251](https://github.com/liyasthomas/postwoman/pull/251) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add yubathom as a contributor [\#250](https://github.com/liyasthomas/postwoman/pull/250) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add larouxn as a contributor [\#249](https://github.com/liyasthomas/postwoman/pull/249) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add NBTX as a contributor [\#248](https://github.com/liyasthomas/postwoman/pull/248) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- docs: add liyasthomas as a contributor [\#247](https://github.com/liyasthomas/postwoman/pull/247) ([allcontributors[bot]](https://github.com/apps/allcontributors))
|
||||||
|
- Make page changes more fluid [\#246](https://github.com/liyasthomas/postwoman/pull/246) ([NBTX](https://github.com/NBTX))
|
||||||
|
- Minor tweaks [\#245](https://github.com/liyasthomas/postwoman/pull/245) ([liyasthomas](https://github.com/liyasthomas))
|
||||||
|
- Add brand new logo to the project [\#244](https://github.com/liyasthomas/postwoman/pull/244) ([caneco](https://github.com/caneco))
|
||||||
|
- ⬆️ Bump @nuxtjs/google-tag-manager from 2.3.0 to 2.3.1 [\#243](https://github.com/liyasthomas/postwoman/pull/243) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump yargs-parser from 15.0.0 to 16.1.0 [\#242](https://github.com/liyasthomas/postwoman/pull/242) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump @nuxtjs/toast from 3.2.1 to 3.3.0 [\#241](https://github.com/liyasthomas/postwoman/pull/241) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump highlight.js from 9.15.10 to 9.16.2 [\#240](https://github.com/liyasthomas/postwoman/pull/240) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump cypress from 3.5.0 to 3.6.0 [\#239](https://github.com/liyasthomas/postwoman/pull/239) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Fix legend labels in Firefox, fix colored labels slider [\#238](https://github.com/liyasthomas/postwoman/pull/238) ([NBTX](https://github.com/NBTX))
|
||||||
|
- Feature/pre request script [\#231](https://github.com/liyasthomas/postwoman/pull/231) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||||
|
- Documentation Cleanup [\#230](https://github.com/liyasthomas/postwoman/pull/230) ([amitdash291](https://github.com/amitdash291))
|
||||||
|
- Fix \#227 Exclude credentials from permalink [\#228](https://github.com/liyasthomas/postwoman/pull/228) ([reefqi037](https://github.com/reefqi037))
|
||||||
|
- ⬆️ Bump cypress from 3.4.1 to 3.5.0 [\#224](https://github.com/liyasthomas/postwoman/pull/224) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump @nuxtjs/axios from 5.6.0 to 5.8.0 [\#223](https://github.com/liyasthomas/postwoman/pull/223) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump node-sass from 4.12.0 to 4.13.0 [\#222](https://github.com/liyasthomas/postwoman/pull/222) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump nuxt from 2.10.1 to 2.10.2 [\#221](https://github.com/liyasthomas/postwoman/pull/221) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump @nuxtjs/google-analytics from 2.2.0 to 2.2.1 [\#220](https://github.com/liyasthomas/postwoman/pull/220) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump vuex-persist from 2.1.0 to 2.1.1 [\#219](https://github.com/liyasthomas/postwoman/pull/219) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Add the ApolloTV proxy server [\#217](https://github.com/liyasthomas/postwoman/pull/217) ([NBTX](https://github.com/NBTX))
|
||||||
|
- Fixed frame colors toggle [\#216](https://github.com/liyasthomas/postwoman/pull/216) ([mateusppereira](https://github.com/mateusppereira))
|
||||||
|
- Re-order sections and add toggle for including authentication in URL [\#215](https://github.com/liyasthomas/postwoman/pull/215) ([NBTX](https://github.com/NBTX))
|
||||||
|
- chore: minor code refactor [\#214](https://github.com/liyasthomas/postwoman/pull/214) ([jamesgeorge007](https://github.com/jamesgeorge007))
|
||||||
|
- Fix \#212 Clear bearer token value [\#213](https://github.com/liyasthomas/postwoman/pull/213) ([reefqi037](https://github.com/reefqi037))
|
||||||
|
- bug: keeping information on page change [\#211](https://github.com/liyasthomas/postwoman/pull/211) ([breno-pereira](https://github.com/breno-pereira))
|
||||||
|
- Work in Progress: feature/allow-collections-importing [\#209](https://github.com/liyasthomas/postwoman/pull/209) ([vlad0337187](https://github.com/vlad0337187))
|
||||||
|
- fix: don't display 'Collection is empty' label if collection has any … [\#208](https://github.com/liyasthomas/postwoman/pull/208) ([vlad0337187](https://github.com/vlad0337187))
|
||||||
|
- Feature/log errors [\#207](https://github.com/liyasthomas/postwoman/pull/207) ([nickpalenchar](https://github.com/nickpalenchar))
|
||||||
|
- Use returned value from toggle component on change event [\#205](https://github.com/liyasthomas/postwoman/pull/205) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||||
|
- Fix proxy URL [\#201](https://github.com/liyasthomas/postwoman/pull/201) ([NBTX](https://github.com/NBTX))
|
||||||
|
- Fix CORS and Mixed-Content issue & Bug Fixes [\#200](https://github.com/liyasthomas/postwoman/pull/200) ([NBTX](https://github.com/NBTX))
|
||||||
|
- Fix CORS and mixed content issue [\#199](https://github.com/liyasthomas/postwoman/pull/199) ([hosseinnedaee](https://github.com/hosseinnedaee))
|
||||||
|
- ⬆️ Bump start-server-and-test from 1.10.5 to 1.10.6 [\#198](https://github.com/liyasthomas/postwoman/pull/198) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Added Tooltips [\#197](https://github.com/liyasthomas/postwoman/pull/197) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- ⬆️ Bump start-server-and-test from 1.10.3 to 1.10.5 [\#194](https://github.com/liyasthomas/postwoman/pull/194) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump @nuxtjs/google-tag-manager from 2.2.1 to 2.3.0 [\#193](https://github.com/liyasthomas/postwoman/pull/193) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump nuxt from 2.10.0 to 2.10.1 [\#192](https://github.com/liyasthomas/postwoman/pull/192) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- ⬆️ Bump yargs-parser from 14.0.0 to 15.0.0 [\#191](https://github.com/liyasthomas/postwoman/pull/191) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- Add quotation marks for generated code [\#187](https://github.com/liyasthomas/postwoman/pull/187) ([johnhenry](https://github.com/johnhenry))
|
||||||
|
- Added auto theme [\#185](https://github.com/liyasthomas/postwoman/pull/185) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
- Add Request name label for every requests [\#184](https://github.com/liyasthomas/postwoman/pull/184) ([sharath2106](https://github.com/sharath2106))
|
||||||
|
- updated threshold and rootMargin for IntersectionObserver [\#182](https://github.com/liyasthomas/postwoman/pull/182) ([edisonaugusthy](https://github.com/edisonaugusthy))
|
||||||
|
- Add basic e2e tests [\#181](https://github.com/liyasthomas/postwoman/pull/181) ([yubathom](https://github.com/yubathom))
|
||||||
|
- ⬆️ Bump nuxt from 2.9.2 to 2.10.0 [\#179](https://github.com/liyasthomas/postwoman/pull/179) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
||||||
|
- 🐛 Fixed sitemap configuration [\#177](https://github.com/liyasthomas/postwoman/pull/177) ([NicoPennec](https://github.com/NicoPennec))
|
||||||
|
- Collections [\#176](https://github.com/liyasthomas/postwoman/pull/176) ([TheHollidayInn](https://github.com/TheHollidayInn))
|
||||||
|
- Code Refactoring [\#173](https://github.com/liyasthomas/postwoman/pull/173) ([edisonaugusthy](https://github.com/edisonaugusthy))
|
||||||
|
- Added Black Theme [\#172](https://github.com/liyasthomas/postwoman/pull/172) ([AndrewBastin](https://github.com/AndrewBastin))
|
||||||
|
|
||||||
|
## [v0.1.0](https://github.com/liyasthomas/postwoman/tree/v0.1.0) (2019-08-22)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/liyasthomas/postwoman/compare/91c08a5e6305cc95a0df46a33fdd0013bf7339b4...v0.1.0)
|
||||||
|
|
||||||
|
\* _This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)_
|
||||||
|
|||||||
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022
|
Copyright (c) 2020
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
53
README.md
@@ -89,13 +89,13 @@
|
|||||||
- `TRACE` - Performs a message loop-back test along the path to the target resource
|
- `TRACE` - Performs a message loop-back test along the path to the target resource
|
||||||
- `<custom>` - Some APIs use custom request methods such as `LIST`. Type in your custom methods.
|
- `<custom>` - Some APIs use custom request methods such as `LIST`. Type in your custom methods.
|
||||||
|
|
||||||
🌈 **Make it yours:** Customizable combinations for background, foreground, and accent colors — [customize now](https://hoppscotch.io/settings).
|
🌈 **Make it yours:** Customizable combinations for background, foreground and accent colors — [customize now](https://hoppscotch.io/settings).
|
||||||
|
|
||||||
**Theming**
|
**Theming**
|
||||||
|
|
||||||
- Choose a theme: System (default), Light, Dark, and Black
|
- Choose theme: System (default), Light, Dark and Black
|
||||||
- Choose accent color: Green (default), Teal, Blue, Indigo, Purple, Yellow, Orange, Red, and Pink
|
- Choose accent color: Green (default), Teal, Blue, Indigo, Purple, Yellow, Orange, Red and Pink
|
||||||
- Distraction-free Zen mode
|
- Distraction free Zen mode
|
||||||
|
|
||||||
_Customized themes are synced with cloud / local session_
|
_Customized themes are synced with cloud / local session_
|
||||||
|
|
||||||
@@ -120,11 +120,11 @@ _Customized themes are synced with cloud / local session_
|
|||||||
|
|
||||||
🔌 **WebSocket:** Establish full-duplex communication channels over a single TCP connection.
|
🔌 **WebSocket:** Establish full-duplex communication channels over a single TCP connection.
|
||||||
|
|
||||||
📡 **Server-Sent Events:** Receive a stream of updates from a server over an HTTP connection without resorting to polling.
|
📡 **Server Sent Events:** Receive a stream of updates from a server over a HTTP connection without resorting to polling.
|
||||||
|
|
||||||
🌩 **Socket.IO:** Send and Receive data with SocketIO server.
|
🌩 **Socket.IO:** Send and Receive data with SocketIO server.
|
||||||
|
|
||||||
🦟 **MQTT:** Subscribe and Publish to topics of an MQTT Broker.
|
🦟 **MQTT:** Subscribe and Publish to topics of a MQTT Broker.
|
||||||
|
|
||||||
🔮 **GraphQL:** GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.
|
🔮 **GraphQL:** GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ _Customized themes are synced with cloud / local session_
|
|||||||
- Query schema
|
- Query schema
|
||||||
- Get query response
|
- Get query response
|
||||||
|
|
||||||
🔐 **Authorization:** Allows to identify the end-user.
|
🔐 **Authorization:** Allows to identify the end user.
|
||||||
|
|
||||||
- None
|
- None
|
||||||
- Basic
|
- Basic
|
||||||
@@ -149,10 +149,10 @@ _Customized themes are synced with cloud / local session_
|
|||||||
📃 **Request Body:** Used to send and receive data via the REST API.
|
📃 **Request Body:** Used to send and receive data via the REST API.
|
||||||
|
|
||||||
- Set `Content Type`
|
- Set `Content Type`
|
||||||
- FormData, JSON, and many more
|
- FormData, JSON and many more
|
||||||
- Toggle between key-value and RAW input parameter list
|
- Toggle between key-value and RAW input parameter list
|
||||||
|
|
||||||
👋 **Response:** Contains the status line, headers, and the message/response body.
|
👋 **Response:** Contains the status line, headers and the message/response body.
|
||||||
|
|
||||||
- Copy response to clipboard
|
- Copy response to clipboard
|
||||||
- Download response as a file
|
- Download response as a file
|
||||||
@@ -163,22 +163,22 @@ _Customized themes are synced with cloud / local session_
|
|||||||
|
|
||||||
📁 **Collections:** Keep your API requests organized with collections and folders. Reuse them with a single click.
|
📁 **Collections:** Keep your API requests organized with collections and folders. Reuse them with a single click.
|
||||||
|
|
||||||
- Unlimited collections, folders, and requests
|
- Unlimited collections, folders and requests
|
||||||
- Nested folders
|
- Nested folders
|
||||||
- Export and import as a file or GitHub gist
|
- Export and import as file or GitHub gist
|
||||||
|
|
||||||
_Collections are synced with cloud / local session storage_
|
_Collections are synced with cloud / local session storage_
|
||||||
|
|
||||||
🌐 **Proxy:** Enable Proxy Mode from Settings to access blocked APIs.
|
🌐 **Proxy:** Enable Proxy Mode from Settings to access blocked APIs.
|
||||||
|
|
||||||
- Hide your IP address
|
- Hide your IP address
|
||||||
- Fixes [`CORS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross-Origin Resource Sharing) issues
|
- Fixes [`CORS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross Origin Resource Sharing) issues
|
||||||
- Access APIs served in non-HTTPS (`http://`) endpoints
|
- Access APIs served in non-HTTPS (`http://`) endpoints
|
||||||
- Use your Proxy URL
|
- Use your own Proxy URL
|
||||||
|
|
||||||
_Official proxy server is hosted by Hoppscotch - **[GitHub](https://github.com/hoppscotch/proxyscotch)** - **[Privacy Policy](https://docs.hoppscotch.io/privacy)**_
|
_Official proxy server is hosted by Hoppscotch - **[GitHub](https://github.com/hoppscotch/proxyscotch)** - **[Privacy Policy](https://docs.hoppscotch.io/privacy)**_
|
||||||
|
|
||||||
📜 **Pre-Request Scripts β:** Snippets of code associated with a request that is executed before the request is sent.
|
📜 **Pre-Request Scripts β:** Snippets of code associated with a request that are executed before the request is sent.
|
||||||
|
|
||||||
- Set environment variables
|
- Set environment variables
|
||||||
- Include timestamp in the request headers
|
- Include timestamp in the request headers
|
||||||
@@ -195,7 +195,7 @@ _Official proxy server is hosted by Hoppscotch - **[GitHub](https://github.com/h
|
|||||||
|
|
||||||
> **[Read our documentation on Keyboard Shortcuts](https://docs.hoppscotch.io/features/shortcuts)**
|
> **[Read our documentation on Keyboard Shortcuts](https://docs.hoppscotch.io/features/shortcuts)**
|
||||||
|
|
||||||
🌎 **i18n:** Experience the app in your language.
|
🌎 **i18n:** Experience the app in your own language.
|
||||||
|
|
||||||
Help us to translate Hoppscotch. Please read [`TRANSLATIONS`](TRANSLATIONS.md) for details on our [`CODE OF CONDUCT`](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
Help us to translate Hoppscotch. Please read [`TRANSLATIONS`](TRANSLATIONS.md) for details on our [`CODE OF CONDUCT`](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
||||||
|
|
||||||
@@ -228,7 +228,7 @@ _Add-ons are developed and maintained under **[Hoppscotch Organization](https://
|
|||||||
- Environments
|
- Environments
|
||||||
- Settings
|
- Settings
|
||||||
|
|
||||||
✅ **Post-Request Tests β:** Write tests associated with a request that is executed after the request's response.
|
✅ **Post-Request Tests β:** Write tests associated with a request that are executed after the request response.
|
||||||
|
|
||||||
- Check the status code as an integer
|
- Check the status code as an integer
|
||||||
- Filter response headers
|
- Filter response headers
|
||||||
@@ -238,7 +238,7 @@ _Add-ons are developed and maintained under **[Hoppscotch Organization](https://
|
|||||||
🌱 **Environments** : Environment variables allow you to store and reuse values in your requests and scripts.
|
🌱 **Environments** : Environment variables allow you to store and reuse values in your requests and scripts.
|
||||||
|
|
||||||
- Unlimited environments and variables
|
- Unlimited environments and variables
|
||||||
- Initialize through the pre-request script
|
- Initialize through pre-request script
|
||||||
- Export as / import from GitHub gist
|
- Export as / import from GitHub gist
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -277,9 +277,10 @@ _Add-ons are developed and maintained under **[Hoppscotch Organization](https://
|
|||||||
|
|
||||||
## **Usage**
|
## **Usage**
|
||||||
|
|
||||||
1. Provide your API endpoint in the URL field
|
1. Choose `method`
|
||||||
2. CLick "Send" to simulate the request
|
2. Enter `URL`
|
||||||
3. View the response
|
3. Send request
|
||||||
|
4. Get response
|
||||||
|
|
||||||
## **Built with**
|
## **Built with**
|
||||||
|
|
||||||
@@ -294,9 +295,9 @@ _Add-ons are developed and maintained under **[Hoppscotch Organization](https://
|
|||||||
|
|
||||||
0. Update [`.env.example`](https://github.com/hoppscotch/hoppscotch/blob/main/packages/hoppscotch-app/.env.example) file found in `packages/hoppscotch-app` with your own keys and rename it to `.env`.
|
0. Update [`.env.example`](https://github.com/hoppscotch/hoppscotch/blob/main/packages/hoppscotch-app/.env.example) file found in `packages/hoppscotch-app` with your own keys and rename it to `.env`.
|
||||||
|
|
||||||
_Sample keys only work with the [production build](https://hoppscotch.io)._
|
_Sample keys only works with the [production build](https://hoppscotch.io)._
|
||||||
|
|
||||||
### Browser-based development environment
|
### Browser based development environment
|
||||||
|
|
||||||
- [GitHub codespace](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace)
|
- [GitHub codespace](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace)
|
||||||
- [Gitpod](https://gitpod.io/#https://github.com/hoppscotch/hoppscotch)
|
- [Gitpod](https://gitpod.io/#https://github.com/hoppscotch/hoppscotch)
|
||||||
@@ -307,13 +308,13 @@ _Sample keys only work with the [production build](https://hoppscotch.io)._
|
|||||||
2. Install pnpm using npm by running `npm install -g pnpm`.
|
2. Install pnpm using npm by running `npm install -g pnpm`.
|
||||||
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
||||||
4. Start the development server with `pnpm run dev`.
|
4. Start the development server with `pnpm run dev`.
|
||||||
5. Open the development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
5. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
||||||
|
|
||||||
### Docker compose
|
### Docker compose
|
||||||
|
|
||||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||||
2. Run `docker-compose up` within the directory that you cloned (probably `hoppscotch`).
|
2. Run `docker-compose up`
|
||||||
3. Open the development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
3. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
||||||
|
|
||||||
## **Docker**
|
## **Docker**
|
||||||
|
|
||||||
@@ -347,7 +348,7 @@ See the [`CHANGELOG`](CHANGELOG.md) file for details.
|
|||||||
|
|
||||||
## **Authors**
|
## **Authors**
|
||||||
|
|
||||||
This project exists thanks to all the people who contribute — [contribute](CONTRIBUTING.md).
|
This project exists thanks to all the people who contribute — [make a contribution](CONTRIBUTING.md).
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://github.com/hoppscotch/hoppscotch/graphs/contributors">
|
<a href="https://github.com/hoppscotch/hoppscotch/graphs/contributors">
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
service cloud.firestore {
|
service cloud.firestore {
|
||||||
match /databases/{database}/documents {
|
match /databases/{database}/documents {
|
||||||
// Make sure the uid of the requesting user matches name of the user
|
match /{document=**} {
|
||||||
|
allow read, write: if request.auth.uid != null;
|
||||||
|
}
|
||||||
|
// Make sure the uid of the requesting user matches the name of the user
|
||||||
// document. The wildcard expression {userId} makes the userId variable
|
// document. The wildcard expression {userId} makes the userId variable
|
||||||
// available in rules.
|
// available in rules.
|
||||||
match /users/{userId} {
|
match /users/{userId} {
|
||||||
allow read, write, create, update, delete: if request.auth.uid != null && request.auth.uid == userId;
|
allow read, update, delete: if request.auth.uid == userId;
|
||||||
}
|
allow create: if request.auth.uid != null;
|
||||||
match /users/{userId}/{document=**} {
|
|
||||||
allow read, write, create, update, delete: if request.auth.uid != null && request.auth.uid == userId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
[[redirects]]
|
[[redirects]]
|
||||||
from = "/careers"
|
from = "/careers"
|
||||||
to = "https://company.hoppscotch.io/careers"
|
to = "https://hoppscotch.notion.site/3b9d5d5239a043bfb91701faabf5b8f0"
|
||||||
status = 301
|
status = 301
|
||||||
force = true
|
force = true
|
||||||
|
|
||||||
@@ -54,9 +54,3 @@
|
|||||||
to = "https://github.com/hoppscotch/hoppscotch"
|
to = "https://github.com/hoppscotch/hoppscotch"
|
||||||
status = 301
|
status = 301
|
||||||
force = true
|
force = true
|
||||||
|
|
||||||
[[redirects]]
|
|
||||||
from = "/announcements"
|
|
||||||
to = "https://company.hoppscotch.io/announcements"
|
|
||||||
status = 301
|
|
||||||
force = true
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hoppscotch-app",
|
"name": "hoppscotch-app",
|
||||||
"version": "2.2.1",
|
"version": "2.1.0",
|
||||||
"description": "Open source API development ecosystem",
|
"description": "Open source API development ecosystem",
|
||||||
"author": "Hoppscotch (support@hoppscotch.io)",
|
"author": "Hoppscotch (support@hoppscotch.io)",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -21,11 +21,10 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"lint-staged": "^12.3.1"
|
"lint-staged": "^12.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^16.1.0",
|
"@commitlint/cli": "^15.0.0",
|
||||||
"@commitlint/config-conventional": "^16.0.0",
|
"@commitlint/config-conventional": "^15.0.0"
|
||||||
"@types/node": "^17.0.10"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "GraphQL language support for CodeMirror",
|
"description": "GraphQL language support for CodeMirror",
|
||||||
"author": "Hoppscotch (support@hoppscotch.io)",
|
"author": "Hoppscotch (support@hoppscotch.io)",
|
||||||
"license": "MIT",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "rollup -c"
|
"prepare": "rollup -c"
|
||||||
},
|
},
|
||||||
@@ -17,16 +16,17 @@
|
|||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/highlight": "^0.19.0",
|
"@codemirror/highlight": "^0.19.6",
|
||||||
"@codemirror/language": "^0.19.0",
|
"@codemirror/language": "^0.19.7",
|
||||||
"@lezer/lr": "^0.15.7"
|
"@lezer/lr": "^0.15.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lezer/generator": "^0.15.3",
|
"@lezer/generator": "^0.15.0",
|
||||||
"mocha": "^9.1.4",
|
"mocha": "^9.0.1",
|
||||||
"rollup": "^2.66.0",
|
"rollup": "^2.60.2",
|
||||||
"rollup-plugin-dts": "^4.1.0",
|
"rollup-plugin-dts": "^4.0.1",
|
||||||
"rollup-plugin-ts": "^2.0.5",
|
"rollup-plugin-ts": "^2.0.4",
|
||||||
"typescript": "^4.5.5"
|
"typescript": "^4.5.2"
|
||||||
}
|
},
|
||||||
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import typescript from "rollup-plugin-ts"
|
import typescript from "rollup-plugin-ts"
|
||||||
import { lezer } from "@lezer/generator/rollup"
|
import {lezer} from "@lezer/generator/rollup"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: "src/index.js",
|
input: "src/index.js",
|
||||||
external: (id) => id != "tslib" && !/^(\.?\/|\w:)/.test(id),
|
external: id => id != "tslib" && !/^(\.?\/|\w:)/.test(id),
|
||||||
output: [
|
output: [
|
||||||
{ file: "dist/index.cjs", format: "cjs" },
|
{file: "dist/index.cjs", format: "cjs"},
|
||||||
{ dir: "./dist", format: "es" },
|
{dir: "./dist", format: "es"}
|
||||||
],
|
],
|
||||||
plugins: [lezer(), typescript()],
|
plugins: [lezer(), typescript()]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,22 @@
|
|||||||
import { parser } from "./syntax.grammar"
|
import {parser} from "./syntax.grammar"
|
||||||
import {
|
import {LRLanguage, LanguageSupport, indentNodeProp, foldNodeProp, foldInside, delimitedIndent} from "@codemirror/language"
|
||||||
LRLanguage,
|
import {styleTags, tags as t} from "@codemirror/highlight"
|
||||||
LanguageSupport,
|
|
||||||
indentNodeProp,
|
|
||||||
foldNodeProp,
|
|
||||||
foldInside,
|
|
||||||
delimitedIndent,
|
|
||||||
} from "@codemirror/language"
|
|
||||||
import { styleTags, tags as t } from "@codemirror/highlight"
|
|
||||||
|
|
||||||
export const GQLLanguage = LRLanguage.define({
|
export const GQLLanguage = LRLanguage.define({
|
||||||
parser: parser.configure({
|
parser: parser.configure({
|
||||||
props: [
|
props: [
|
||||||
indentNodeProp.add({
|
indentNodeProp.add({
|
||||||
"SelectionSet FieldsDefinition ObjectValue SchemaDefinition RootTypeDef":
|
"SelectionSet FieldsDefinition ObjectValue SchemaDefinition RootTypeDef": delimitedIndent({ closing: "}", align: true }),
|
||||||
delimitedIndent({ closing: "}", align: true }),
|
|
||||||
}),
|
}),
|
||||||
foldNodeProp.add({
|
foldNodeProp.add({
|
||||||
Application: foldInside,
|
Application: foldInside,
|
||||||
"SelectionSet FieldsDefinition ObjectValue RootOperationTypeDefinition RootTypeDef":
|
"SelectionSet FieldsDefinition ObjectValue RootOperationTypeDefinition RootTypeDef": (node) => {
|
||||||
(node) => {
|
return {
|
||||||
return {
|
from: node.from,
|
||||||
from: node.from,
|
to: node.to
|
||||||
to: node.to,
|
}
|
||||||
}
|
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
styleTags({
|
styleTags({
|
||||||
Name: t.definition(t.variableName),
|
Name: t.definition(t.variableName),
|
||||||
@@ -37,13 +29,13 @@ export const GQLLanguage = LRLanguage.define({
|
|||||||
NullValue: t.null,
|
NullValue: t.null,
|
||||||
ObjectValue: t.brace,
|
ObjectValue: t.brace,
|
||||||
Comment: t.lineComment,
|
Comment: t.lineComment,
|
||||||
}),
|
})
|
||||||
],
|
]
|
||||||
}),
|
}),
|
||||||
languageData: {
|
languageData: {
|
||||||
commentTokens: { line: "#" },
|
commentTokens: { line: "#" },
|
||||||
closeBrackets: { brackets: ["(", "[", "{", '"', '"""'] },
|
closeBrackets: { brackets: ["(", "[", "{", '"', '"""'] }
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export function GQL() {
|
export function GQL() {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"newLine": "lf",
|
"newLine": "lf",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"allowJs": true
|
"allowJs": true,
|
||||||
},
|
},
|
||||||
"include": ["src/*"]
|
"include": ["src/*"]
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 253 B |
@@ -1 +0,0 @@
|
|||||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M12 0C5.417 0 0 5.417 0 12s5.417 12 12 12 12-5.417 12-12S18.583 0 12 0zm0 2.478c5.256 0 9.522 4.266 9.522 9.522S17.256 21.522 12 21.522 2.478 17.256 2.478 12c0-.885.12-1.741.347-2.554a4.76 4.76 0 0 0 3.925 2.066 4.764 4.764 0 0 0 4.762-4.762 4.758 4.758 0 0 0-2.067-3.925A9.526 9.526 0 0 1 12 2.478Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 398 B |
@@ -1 +0,0 @@
|
|||||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M13.527.099C6.955-.744.942 3.9.099 10.473c-.843 6.572 3.8 12.584 10.373 13.428 6.573.843 12.587-3.801 13.428-10.374C24.744 6.955 20.101.943 13.527.099zm2.471 7.485a.855.855 0 0 0-.593.25l-4.453 4.453-.307-.307-.643-.643c4.389-4.376 5.18-4.418 5.996-3.753zm-4.863 4.861 4.44-4.44a.62.62 0 1 1 .847.903l-4.699 4.125-.588-.588zm.33.694-1.1.238a.06.06 0 0 1-.067-.032.06.06 0 0 1 .01-.073l.645-.645.512.512zm-2.803-.459 1.172-1.172.879.878-1.979.426a.074.074 0 0 1-.085-.039.072.072 0 0 1 .013-.093zm-3.646 6.058a.076.076 0 0 1-.069-.083.077.077 0 0 1 .022-.046h.002l.946-.946 1.222 1.222-2.123-.147zm2.425-1.256a.228.228 0 0 0-.117.256l.203.865a.125.125 0 0 1-.211.117h-.003l-.934-.934-.294-.295 3.762-3.758 1.82-.393.874.874c-1.255 1.102-2.971 2.201-5.1 3.268zm5.279-3.428h-.002l-.839-.839 4.699-4.125a.952.952 0 0 0 .119-.127c-.148 1.345-2.029 3.245-3.977 5.091zm3.657-6.46-.003-.002a1.822 1.822 0 0 1 2.459-2.684l-1.61 1.613a.119.119 0 0 0 0 .169l1.247 1.247a1.817 1.817 0 0 1-2.093-.343zm2.578 0a1.714 1.714 0 0 1-.271.218h-.001l-1.207-1.207 1.533-1.533c.661.72.637 1.832-.054 2.522zm-.1-1.544a.143.143 0 0 0-.053.157.416.416 0 0 1-.053.45.14.14 0 0 0 .023.197.141.141 0 0 0 .084.03.14.14 0 0 0 .106-.05.691.691 0 0 0 .087-.751.138.138 0 0 0-.194-.033z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
|
|
||||||
<path d="M9 12l2 2 4-4"></path>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 286 B |
@@ -1,5 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path>
|
|
||||||
<polyline points="17 8 12 3 7 8"></polyline>
|
|
||||||
<line x1="12" y1="3" x2="12" y2="15"></line>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 342 B |
@@ -1,5 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M16 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"></path>
|
|
||||||
<circle cx="8.5" cy="7" r="4"></circle>
|
|
||||||
<line x1="23" y1="11" x2="17" y2="11"></line>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 338 B |
@@ -1,6 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M16 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"></path>
|
|
||||||
<circle cx="8.5" cy="7" r="4"></circle>
|
|
||||||
<line x1="18" y1="8" x2="23" y2="13"></line>
|
|
||||||
<line x1="23" y1="8" x2="18" y2="13"></line>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 384 B |
@@ -1,9 +1,7 @@
|
|||||||
* {
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
@apply backface-hidden;
|
@apply backface-hidden;
|
||||||
@apply before:backface-hidden;
|
|
||||||
@apply after:backface-hidden;
|
|
||||||
@apply selection:bg-accentDark;
|
|
||||||
@apply selection:text-accentContrast;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@@ -39,6 +37,11 @@
|
|||||||
@apply hidden;
|
@apply hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
@apply bg-accentDark;
|
||||||
|
@apply text-accentContrast;
|
||||||
|
}
|
||||||
|
|
||||||
input::placeholder,
|
input::placeholder,
|
||||||
textarea::placeholder {
|
textarea::placeholder {
|
||||||
@apply text-secondary;
|
@apply text-secondary;
|
||||||
@@ -61,10 +64,10 @@ body {
|
|||||||
@apply font-medium;
|
@apply font-medium;
|
||||||
@apply select-none;
|
@apply select-none;
|
||||||
@apply overflow-x-hidden;
|
@apply overflow-x-hidden;
|
||||||
@apply text-body;
|
|
||||||
@apply leading-body;
|
|
||||||
|
|
||||||
animation: fade 300ms forwards;
|
animation: fade 300ms forwards;
|
||||||
|
font-size: var(--body-font-size);
|
||||||
|
line-height: var(--body-line-height);
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
}
|
}
|
||||||
@@ -99,18 +102,16 @@ body {
|
|||||||
|
|
||||||
.material-icons {
|
.material-icons {
|
||||||
@apply flex-shrink-0;
|
@apply flex-shrink-0;
|
||||||
@apply overflow-hidden;
|
|
||||||
|
|
||||||
font-size: var(--line-height-body) !important;
|
font-size: var(--body-line-height) !important;
|
||||||
width: var(--line-height-body);
|
width: var(--body-line-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
.svg-icons {
|
.svg-icons {
|
||||||
@apply flex-shrink-0;
|
@apply flex-shrink-0;
|
||||||
@apply overflow-hidden;
|
|
||||||
|
|
||||||
height: var(--line-height-body);
|
height: var(--body-line-height);
|
||||||
width: var(--line-height-body);
|
width: var(--body-line-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -119,8 +120,9 @@ a {
|
|||||||
@apply no-underline;
|
@apply no-underline;
|
||||||
@apply outline-none;
|
@apply outline-none;
|
||||||
@apply transition;
|
@apply transition;
|
||||||
@apply text-body;
|
|
||||||
@apply leading-body;
|
font-size: var(--body-font-size);
|
||||||
|
line-height: var(--body-line-height);
|
||||||
|
|
||||||
&.link {
|
&.link {
|
||||||
@apply items-center;
|
@apply items-center;
|
||||||
@@ -134,43 +136,44 @@ a {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-theme {
|
.tippy-popper {
|
||||||
@apply bg-tooltip;
|
.tooltip-theme {
|
||||||
@apply text-primary;
|
@apply bg-tooltip;
|
||||||
@apply font-semibold;
|
@apply text-primary;
|
||||||
@apply py-1 px-2;
|
@apply font-semibold;
|
||||||
@apply rounded;
|
@apply py-1 px-2;
|
||||||
@apply truncate;
|
|
||||||
@apply shadow;
|
|
||||||
@apply leading-body;
|
|
||||||
|
|
||||||
font-size: 86%;
|
|
||||||
|
|
||||||
kbd {
|
|
||||||
@apply hidden;
|
|
||||||
@apply sm:inline-flex;
|
|
||||||
@apply font-sans;
|
|
||||||
@apply bg-gray-500;
|
|
||||||
@apply bg-opacity-45;
|
|
||||||
@apply text-primaryLight;
|
|
||||||
@apply rounded-sm;
|
|
||||||
@apply px-1;
|
|
||||||
@apply ml-1;
|
|
||||||
@apply truncate;
|
@apply truncate;
|
||||||
|
@apply shadow;
|
||||||
|
|
||||||
|
font-size: 88%;
|
||||||
|
line-height: var(--body-line-height);
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
@apply inline-flex;
|
||||||
|
@apply font-sans;
|
||||||
|
@apply bg-gray-500;
|
||||||
|
@apply bg-opacity-45;
|
||||||
|
@apply text-primaryLight;
|
||||||
|
@apply rounded-sm;
|
||||||
|
@apply px-1;
|
||||||
|
@apply ml-1;
|
||||||
|
@apply truncate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.popover-theme {
|
.popover-theme {
|
||||||
@apply bg-popover;
|
@apply bg-popover;
|
||||||
@apply text-secondary;
|
@apply text-secondary;
|
||||||
@apply p-2;
|
@apply p-2;
|
||||||
@apply shadow-lg;
|
@apply shadow-lg;
|
||||||
@apply text-body;
|
@apply focus:outline-none;
|
||||||
@apply leading-body;
|
|
||||||
@apply focus:outline-none;
|
|
||||||
|
|
||||||
.tippy-roundarrow svg {
|
font-size: var(--body-font-size);
|
||||||
@apply fill-popover;
|
line-height: var(--body-line-height);
|
||||||
|
|
||||||
|
.tippy-roundarrow svg {
|
||||||
|
@apply fill-popover;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,12 +217,13 @@ input,
|
|||||||
select,
|
select,
|
||||||
textarea,
|
textarea,
|
||||||
button {
|
button {
|
||||||
|
@apply focus:outline-none;
|
||||||
@apply truncate;
|
@apply truncate;
|
||||||
@apply transition;
|
@apply transition;
|
||||||
@apply text-body;
|
|
||||||
@apply leading-body;
|
|
||||||
@apply focus:outline-none;
|
|
||||||
@apply disabled:cursor-not-allowed;
|
@apply disabled:cursor-not-allowed;
|
||||||
|
|
||||||
|
font-size: var(--body-font-size);
|
||||||
|
line-height: var(--body-line-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input[type="file"],
|
.input[type="file"],
|
||||||
@@ -229,6 +233,7 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.floating-input ~ label {
|
.floating-input ~ label {
|
||||||
|
@apply font-medium;
|
||||||
@apply py-0.5;
|
@apply py-0.5;
|
||||||
@apply px-2;
|
@apply px-2;
|
||||||
@apply m-2;
|
@apply m-2;
|
||||||
@@ -244,7 +249,7 @@ button {
|
|||||||
@apply transform;
|
@apply transform;
|
||||||
@apply origin-top-left;
|
@apply origin-top-left;
|
||||||
@apply scale-75;
|
@apply scale-75;
|
||||||
@apply -translate-y-4;
|
@apply -translate-y-5;
|
||||||
@apply translate-x-1;
|
@apply translate-x-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,8 +324,9 @@ pre.ace_editor {
|
|||||||
@apply shadow;
|
@apply shadow;
|
||||||
@apply font-medium;
|
@apply font-medium;
|
||||||
@apply transition;
|
@apply transition;
|
||||||
@apply text-body;
|
|
||||||
@apply leading-body;
|
font-size: var(--body-font-size);
|
||||||
|
line-height: var(--body-line-height);
|
||||||
|
|
||||||
.action {
|
.action {
|
||||||
@apply bg-gray-500;
|
@apply bg-gray-500;
|
||||||
@@ -332,11 +338,12 @@ pre.ace_editor {
|
|||||||
@apply rounded;
|
@apply rounded;
|
||||||
@apply text-current;
|
@apply text-current;
|
||||||
@apply normal-case;
|
@apply normal-case;
|
||||||
@apply font-medium;
|
|
||||||
@apply text-body;
|
|
||||||
@apply leading-body;
|
|
||||||
@apply hover:bg-opacity-20;
|
@apply hover:bg-opacity-20;
|
||||||
@apply hover:no-underline;
|
@apply hover:no-underline;
|
||||||
|
@apply font-medium;
|
||||||
|
|
||||||
|
font-size: var(--body-font-size);
|
||||||
|
line-height: var(--body-line-height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,25 +451,6 @@ pre.ace_editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.shortcut-key {
|
|
||||||
@apply inline-flex;
|
|
||||||
@apply font-sans;
|
|
||||||
@apply text-tiny;
|
|
||||||
@apply bg-divider;
|
|
||||||
@apply rounded;
|
|
||||||
@apply ml-2;
|
|
||||||
@apply px-1;
|
|
||||||
@apply transition;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capitalize-first {
|
|
||||||
@apply first-letter:capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
details summary::-webkit-details-marker {
|
|
||||||
@apply hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
main {
|
main {
|
||||||
margin-bottom: env(safe-area-inset-bottom);
|
margin-bottom: env(safe-area-inset-bottom);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
--font-sans: "Inter", sans-serif;
|
--font-sans: "Inter", sans-serif;
|
||||||
--font-mono: "Roboto Mono", monospace;
|
--font-mono: "Roboto Mono", monospace;
|
||||||
--font-icon: "Material Icons";
|
--font-icon: "Material Icons";
|
||||||
--font-size-tiny: calc(var(--font-size-body) - 0.062rem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin dark-theme {
|
@mixin dark-theme {
|
||||||
@@ -28,7 +27,7 @@
|
|||||||
--secondary-color: theme("colors.true-gray.500");
|
--secondary-color: theme("colors.true-gray.500");
|
||||||
--secondary-light-color: theme("colors.true-gray.400");
|
--secondary-light-color: theme("colors.true-gray.400");
|
||||||
--secondary-dark-color: theme("colors.true-gray.900");
|
--secondary-dark-color: theme("colors.true-gray.900");
|
||||||
--divider-color: theme("colors.gray.100");
|
--divider-color: theme("colors.true-gray.200");
|
||||||
--divider-light-color: theme("colors.true-gray.100");
|
--divider-light-color: theme("colors.true-gray.100");
|
||||||
--divider-dark-color: theme("colors.true-gray.300");
|
--divider-dark-color: theme("colors.true-gray.300");
|
||||||
--error-color: theme("colors.yellow.100");
|
--error-color: theme("colors.yellow.100");
|
||||||
@@ -45,11 +44,11 @@
|
|||||||
--secondary-light-color: theme("colors.true-gray.500");
|
--secondary-light-color: theme("colors.true-gray.500");
|
||||||
--secondary-dark-color: theme("colors.true-gray.100");
|
--secondary-dark-color: theme("colors.true-gray.100");
|
||||||
--divider-color: theme("colors.true-gray.800");
|
--divider-color: theme("colors.true-gray.800");
|
||||||
--divider-light-color: theme("colors.dark.800");
|
--divider-light-color: theme("colors.dark.700");
|
||||||
--divider-dark-color: theme("colors.dark.300");
|
--divider-dark-color: theme("colors.dark.300");
|
||||||
--error-color: theme("colors.warm-gray.900");
|
--error-color: theme("colors.warm-gray.900");
|
||||||
--tooltip-color: theme("colors.true-gray.100");
|
--tooltip-color: theme("colors.true-gray.100");
|
||||||
--popover-color: theme("colors.dark.600");
|
--popover-color: theme("colors.dark.700");
|
||||||
--editor-theme: "twilight";
|
--editor-theme: "twilight";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,8 +243,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mixin font-small {
|
@mixin font-small {
|
||||||
--font-size-body: 0.75rem;
|
--body-font-size: 0.75rem;
|
||||||
--line-height-body: 1rem;
|
--body-line-height: 1rem;
|
||||||
--upper-primary-sticky-fold: 4.125rem;
|
--upper-primary-sticky-fold: 4.125rem;
|
||||||
--upper-secondary-sticky-fold: 6.125rem;
|
--upper-secondary-sticky-fold: 6.125rem;
|
||||||
--upper-tertiary-sticky-fold: 8.188rem;
|
--upper-tertiary-sticky-fold: 8.188rem;
|
||||||
@@ -256,8 +255,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mixin font-medium {
|
@mixin font-medium {
|
||||||
--font-size-body: 0.875rem;
|
--body-font-size: 0.875rem;
|
||||||
--line-height-body: 1.25rem;
|
--body-line-height: 1.25rem;
|
||||||
--upper-primary-sticky-fold: 4.375rem;
|
--upper-primary-sticky-fold: 4.375rem;
|
||||||
--upper-secondary-sticky-fold: 6.625rem;
|
--upper-secondary-sticky-fold: 6.625rem;
|
||||||
--upper-tertiary-sticky-fold: 8.813rem;
|
--upper-tertiary-sticky-fold: 8.813rem;
|
||||||
@@ -268,8 +267,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mixin font-large {
|
@mixin font-large {
|
||||||
--font-size-body: 1rem;
|
--body-font-size: 1rem;
|
||||||
--line-height-body: 1.5rem;
|
--body-line-height: 1.5rem;
|
||||||
--upper-primary-sticky-fold: 4.625rem;
|
--upper-primary-sticky-fold: 4.625rem;
|
||||||
--upper-secondary-sticky-fold: 7.125rem;
|
--upper-secondary-sticky-fold: 7.125rem;
|
||||||
--upper-tertiary-sticky-fold: 9.5rem;
|
--upper-tertiary-sticky-fold: 9.5rem;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="bg-error flex justify-between">
|
||||||
class="relative flex items-center px-4 py-2 transition bg-error text-tiny group"
|
<span
|
||||||
>
|
class="flex py-2 px-4 transition justify-center group relative items-center"
|
||||||
<i class="mr-2 material-icons">info_outline</i>
|
>
|
||||||
<span class="text-secondaryDark">
|
<i class="mr-2 material-icons">info_outline</i>
|
||||||
<span class="md:hidden">
|
<span class="text-secondaryDark">
|
||||||
{{ t("helpers.offline_short") }}
|
<span class="md:hidden">
|
||||||
</span>
|
{{ t("helpers.offline_short") }}
|
||||||
<span class="<md:hidden">
|
</span>
|
||||||
{{ t("helpers.offline") }}
|
<span class="hidden md:inline">
|
||||||
|
{{ t("helpers.offline") }}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,132 +22,97 @@
|
|||||||
}"
|
}"
|
||||||
@click.native="ZEN_MODE = !ZEN_MODE"
|
@click.native="ZEN_MODE = !ZEN_MODE"
|
||||||
/>
|
/>
|
||||||
<tippy
|
|
||||||
ref="interceptorOptions"
|
|
||||||
interactive
|
|
||||||
trigger="click"
|
|
||||||
theme="popover"
|
|
||||||
arrow
|
|
||||||
>
|
|
||||||
<template #trigger>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('settings.interceptor')"
|
|
||||||
svg="shield-check"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<AppInterceptor />
|
|
||||||
</tippy>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<tippy
|
<span>
|
||||||
ref="options"
|
<tippy
|
||||||
interactive
|
ref="options"
|
||||||
trigger="click"
|
interactive
|
||||||
theme="popover"
|
trigger="click"
|
||||||
arrow
|
theme="popover"
|
||||||
:on-shown="() => tippyActions.focus()"
|
arrow
|
||||||
>
|
|
||||||
<template #trigger>
|
|
||||||
<ButtonSecondary
|
|
||||||
svg="help-circle"
|
|
||||||
class="!rounded-none"
|
|
||||||
:label="`${t('app.help')}`"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<div
|
|
||||||
ref="tippyActions"
|
|
||||||
class="flex flex-col focus:outline-none"
|
|
||||||
tabindex="0"
|
|
||||||
@keyup.d="documentation.$el.click()"
|
|
||||||
@keyup.s="shortcuts.$el.click()"
|
|
||||||
@keyup.c="chat.$el.click()"
|
|
||||||
@keyup.escape="options.tippy().hide()"
|
|
||||||
>
|
>
|
||||||
<SmartItem
|
<template #trigger>
|
||||||
ref="documentation"
|
<ButtonSecondary
|
||||||
svg="book"
|
svg="help-circle"
|
||||||
:label="`${t('app.documentation')}`"
|
class="!rounded-none"
|
||||||
to="https://docs.hoppscotch.io"
|
:label="`${t('app.help')}`"
|
||||||
blank
|
/>
|
||||||
:shortcut="['D']"
|
</template>
|
||||||
@click.native="options.tippy().hide()"
|
<div class="flex flex-col">
|
||||||
/>
|
<SmartItem
|
||||||
<SmartItem
|
svg="book"
|
||||||
ref="shortcuts"
|
:label="`${t('app.documentation')}`"
|
||||||
svg="zap"
|
to="https://docs.hoppscotch.io"
|
||||||
:label="`${t('app.keyboard_shortcuts')}`"
|
blank
|
||||||
:shortcut="['S']"
|
@click.native="$refs.options.tippy().hide()"
|
||||||
@click.native="
|
/>
|
||||||
() => {
|
<SmartItem
|
||||||
showShortcuts = true
|
svg="zap"
|
||||||
options.tippy().hide()
|
:label="`${t('app.keyboard_shortcuts')}`"
|
||||||
}
|
@click.native="
|
||||||
"
|
() => {
|
||||||
/>
|
showShortcuts = true
|
||||||
<SmartItem
|
$refs.options.tippy().hide()
|
||||||
ref="chat"
|
}
|
||||||
svg="message-circle"
|
"
|
||||||
:label="`${t('app.chat_with_us')}`"
|
/>
|
||||||
:shortcut="['C']"
|
<SmartItem
|
||||||
@click.native="
|
svg="gift"
|
||||||
() => {
|
:label="`${t('app.whats_new')}`"
|
||||||
chatWithUs()
|
to="https://docs.hoppscotch.io/changelog"
|
||||||
options.tippy().hide()
|
blank
|
||||||
}
|
@click.native="$refs.options.tippy().hide()"
|
||||||
"
|
/>
|
||||||
/>
|
<SmartItem
|
||||||
<SmartItem
|
svg="message-circle"
|
||||||
svg="gift"
|
:label="`${t('app.chat_with_us')}`"
|
||||||
:label="`${t('app.whats_new')}`"
|
@click.native="
|
||||||
to="https://docs.hoppscotch.io/changelog"
|
() => {
|
||||||
blank
|
chatWithUs()
|
||||||
@click.native="options.tippy().hide()"
|
$refs.options.tippy().hide()
|
||||||
/>
|
}
|
||||||
<SmartItem
|
"
|
||||||
svg="activity"
|
/>
|
||||||
:label="t('app.status')"
|
<hr />
|
||||||
to="https://status.hoppscotch.io"
|
<SmartItem
|
||||||
blank
|
svg="github"
|
||||||
@click.native="options.tippy().hide()"
|
:label="`${t('app.github')}`"
|
||||||
/>
|
to="https://github.com/hoppscotch/hoppscotch"
|
||||||
<hr />
|
blank
|
||||||
<SmartItem
|
@click.native="$refs.options.tippy().hide()"
|
||||||
svg="github"
|
/>
|
||||||
:label="`${t('app.github')}`"
|
<SmartItem
|
||||||
to="https://github.com/hoppscotch/hoppscotch"
|
svg="twitter"
|
||||||
blank
|
:label="`${t('app.twitter')}`"
|
||||||
@click.native="options.tippy().hide()"
|
to="https://hoppscotch.io/twitter"
|
||||||
/>
|
blank
|
||||||
<SmartItem
|
@click.native="$refs.options.tippy().hide()"
|
||||||
svg="twitter"
|
/>
|
||||||
:label="`${t('app.twitter')}`"
|
<SmartItem
|
||||||
to="https://hoppscotch.io/twitter"
|
svg="user-plus"
|
||||||
blank
|
:label="`${t('app.invite')}`"
|
||||||
@click.native="options.tippy().hide()"
|
@click.native="
|
||||||
/>
|
() => {
|
||||||
<SmartItem
|
showShare = true
|
||||||
svg="user-plus"
|
$refs.options.tippy().hide()
|
||||||
:label="`${t('app.invite')}`"
|
}
|
||||||
@click.native="
|
"
|
||||||
() => {
|
/>
|
||||||
showShare = true
|
<SmartItem
|
||||||
options.tippy().hide()
|
svg="lock"
|
||||||
}
|
:label="`${t('app.terms_and_privacy')}`"
|
||||||
"
|
to="https://docs.hoppscotch.io/privacy"
|
||||||
/>
|
blank
|
||||||
<SmartItem
|
@click.native="$refs.options.tippy().hide()"
|
||||||
svg="lock"
|
/>
|
||||||
:label="`${t('app.terms_and_privacy')}`"
|
<!-- <SmartItem :label="t('app.status')" /> -->
|
||||||
to="https://docs.hoppscotch.io/privacy"
|
<div class="flex opacity-50 py-2 px-4">
|
||||||
blank
|
{{ `${t("app.name")} ${t("app.version")}` }}
|
||||||
@click.native="options.tippy().hide()"
|
</div>
|
||||||
/>
|
|
||||||
<div class="flex px-4 py-2 opacity-50">
|
|
||||||
{{ `${t("app.name")} v${$config.clientVersion}` }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</tippy>
|
||||||
</tippy>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
svg="zap"
|
svg="zap"
|
||||||
@@ -170,7 +135,7 @@
|
|||||||
@click.native="COLUMN_LAYOUT = !COLUMN_LAYOUT"
|
@click.native="COLUMN_LAYOUT = !COLUMN_LAYOUT"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="transition transform"
|
class="transform transition"
|
||||||
:class="{
|
:class="{
|
||||||
'rotate-180': SIDEBAR_ON_LEFT,
|
'rotate-180': SIDEBAR_ON_LEFT,
|
||||||
}"
|
}"
|
||||||
@@ -188,7 +153,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<AppShortcuts :show="showShortcuts" @close="showShortcuts = false" />
|
<AppShortcuts :show="showShortcuts" @close="showShortcuts = false" />
|
||||||
<AppShare :show="showShare" @hide-modal="showShare = false" />
|
<AppShare :show="showShare" @hide-modal="showShare = false" />
|
||||||
<AppPowerSearch :show="showSearch" @hide-modal="showSearch = false" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -202,7 +166,6 @@ import { useI18n } from "~/helpers/utils/composables"
|
|||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const showShortcuts = ref(false)
|
const showShortcuts = ref(false)
|
||||||
const showShare = ref(false)
|
const showShare = ref(false)
|
||||||
const showSearch = ref(false)
|
|
||||||
|
|
||||||
defineActionHandler("flyouts.keybinds.toggle", () => {
|
defineActionHandler("flyouts.keybinds.toggle", () => {
|
||||||
showShortcuts.value = !showShortcuts.value
|
showShortcuts.value = !showShortcuts.value
|
||||||
@@ -212,10 +175,6 @@ defineActionHandler("modals.share.toggle", () => {
|
|||||||
showShare.value = !showShare.value
|
showShare.value = !showShare.value
|
||||||
})
|
})
|
||||||
|
|
||||||
defineActionHandler("modals.search.toggle", () => {
|
|
||||||
showSearch.value = !showSearch.value
|
|
||||||
})
|
|
||||||
|
|
||||||
const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
|
const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
|
||||||
const SIDEBAR = useSetting("SIDEBAR")
|
const SIDEBAR = useSetting("SIDEBAR")
|
||||||
const ZEN_MODE = useSetting("ZEN_MODE")
|
const ZEN_MODE = useSetting("ZEN_MODE")
|
||||||
@@ -249,11 +208,4 @@ const nativeShare = () => {
|
|||||||
const chatWithUs = () => {
|
const chatWithUs = () => {
|
||||||
showChat()
|
showChat()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template refs
|
|
||||||
const tippyActions = ref<any | null>(null)
|
|
||||||
const documentation = ref<any | null>(null)
|
|
||||||
const shortcuts = ref<any | null>(null)
|
|
||||||
const chat = ref<any | null>(null)
|
|
||||||
const options = ref<any | null>(null)
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -11,10 +11,10 @@
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="searchResults.length === 0"
|
v-if="searchResults.length === 0"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<i class="pb-2 opacity-75 material-icons">manage_search</i>
|
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
||||||
<span class="my-2 text-center">
|
<span class="text-center">
|
||||||
{{ t("state.nothing_found") }} "{{ search }}"
|
{{ t("state.nothing_found") }} "{{ search }}"
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
title="Star Hoppscotch"
|
title="Star Hoppscotch"
|
||||||
href="https://github.com/hoppscotch/hoppscotch"
|
href="https://github.com/hoppscotch/hoppscotch"
|
||||||
:data-color-scheme="
|
:data-color-scheme="
|
||||||
colorMode.value != 'light'
|
$colorMode.value != 'light'
|
||||||
? colorMode.value == 'black'
|
? $colorMode.value == 'black'
|
||||||
? 'dark'
|
? 'dark'
|
||||||
: 'dark_dimmed'
|
: 'dark_dimmed'
|
||||||
: 'light'
|
: 'light'
|
||||||
@@ -20,9 +20,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import GithubButton from "vue-github-button"
|
import GithubButton from "vue-github-button"
|
||||||
import { useColorMode } from "~/helpers/utils/composables"
|
|
||||||
|
|
||||||
const colorMode = useColorMode()
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
size: {
|
size: {
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<header
|
<header
|
||||||
class="flex items-center justify-between flex-1 px-2 py-2 space-x-2"
|
class="flex space-x-2 flex-1 py-2 px-2 items-center justify-between"
|
||||||
>
|
>
|
||||||
<div class="inline-flex items-center space-x-2">
|
<div class="space-x-2 inline-flex items-center">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
class="tracking-wide !font-bold !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
|
class="tracking-wide !font-bold !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
|
||||||
label="HOPPSCOTCH"
|
label="HOPPSCOTCH"
|
||||||
to="/"
|
to="/"
|
||||||
/>
|
/>
|
||||||
<AppGitHubStarButton class="mt-1.5 transition <sm:hidden" />
|
<AppGitHubStarButton class="mt-1.5 transition hidden sm:flex" />
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-flex items-center space-x-2">
|
<div class="space-x-2 inline-flex items-center">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
id="installPWA"
|
id="installPWA"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
:title="`${t('app.search')} <kbd>/</kbd>`"
|
:title="`${t('app.search')} <kbd>/</kbd>`"
|
||||||
svg="search"
|
svg="search"
|
||||||
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
|
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
|
||||||
@click.native="invokeAction('modals.search.toggle')"
|
@click.native="showSearch = true"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
:label="t('header.login')"
|
:label="t('header.login')"
|
||||||
@click.native="showLogin = true"
|
@click.native="showLogin = true"
|
||||||
/>
|
/>
|
||||||
<div v-else class="inline-flex items-center space-x-2">
|
<div v-else class="space-x-2 inline-flex items-center">
|
||||||
<ButtonPrimary
|
<ButtonPrimary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('team.invite_tooltip')"
|
:title="t('team.invite_tooltip')"
|
||||||
@@ -57,14 +57,7 @@
|
|||||||
@click.native="showTeamsModal = true"
|
@click.native="showTeamsModal = true"
|
||||||
/>
|
/>
|
||||||
<span class="px-2">
|
<span class="px-2">
|
||||||
<tippy
|
<tippy ref="user" interactive trigger="click" theme="popover" arrow>
|
||||||
ref="options"
|
|
||||||
interactive
|
|
||||||
trigger="click"
|
|
||||||
theme="popover"
|
|
||||||
arrow
|
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ProfilePicture
|
<ProfilePicture
|
||||||
v-if="currentUser.photoURL"
|
v-if="currentUser.photoURL"
|
||||||
@@ -85,46 +78,19 @@
|
|||||||
svg="user"
|
svg="user"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex flex-col px-2 text-tiny">
|
<SmartItem
|
||||||
<span class="inline-flex font-semibold truncate">
|
to="/profile"
|
||||||
{{ currentUser.displayName }}
|
svg="user"
|
||||||
</span>
|
:label="t('navigation.profile')"
|
||||||
<span class="inline-flex truncate text-secondaryLight">
|
@click.native="$refs.user.tippy().hide()"
|
||||||
{{ currentUser.email }}
|
/>
|
||||||
</span>
|
<SmartItem
|
||||||
</div>
|
to="/settings"
|
||||||
<hr />
|
svg="settings"
|
||||||
<div
|
:label="t('navigation.settings')"
|
||||||
ref="tippyActions"
|
@click.native="$refs.user.tippy().hide()"
|
||||||
class="flex flex-col focus:outline-none"
|
/>
|
||||||
tabindex="0"
|
<FirebaseLogout @confirm-logout="$refs.user.tippy().hide()" />
|
||||||
@keyup.enter="profile.$el.click()"
|
|
||||||
@keyup.s="settings.$el.click()"
|
|
||||||
@keyup.l="logout.$el.click()"
|
|
||||||
@keyup.escape="options.tippy().hide()"
|
|
||||||
>
|
|
||||||
<SmartItem
|
|
||||||
ref="profile"
|
|
||||||
to="/profile"
|
|
||||||
svg="user"
|
|
||||||
:label="t('navigation.profile')"
|
|
||||||
:shortcut="['↩']"
|
|
||||||
@click.native="options.tippy().hide()"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="settings"
|
|
||||||
to="/settings"
|
|
||||||
svg="settings"
|
|
||||||
:label="t('navigation.settings')"
|
|
||||||
:shortcut="['S']"
|
|
||||||
@click.native="options.tippy().hide()"
|
|
||||||
/>
|
|
||||||
<FirebaseLogout
|
|
||||||
ref="logout"
|
|
||||||
:shortcut="['L']"
|
|
||||||
@confirm-logout="options.tippy().hide()"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,6 +99,7 @@
|
|||||||
<AppAnnouncement v-if="!isOnLine" />
|
<AppAnnouncement v-if="!isOnLine" />
|
||||||
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
||||||
<AppSupport :show="showSupport" @hide-modal="showSupport = false" />
|
<AppSupport :show="showSupport" @hide-modal="showSupport = false" />
|
||||||
|
<AppPowerSearch :show="showSearch" @hide-modal="showSearch = false" />
|
||||||
<TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" />
|
<TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -147,7 +114,7 @@ import {
|
|||||||
useI18n,
|
useI18n,
|
||||||
useToast,
|
useToast,
|
||||||
} from "~/helpers/utils/composables"
|
} from "~/helpers/utils/composables"
|
||||||
import { defineActionHandler, invokeAction } from "~/helpers/actions"
|
import { defineActionHandler } from "~/helpers/actions"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -161,6 +128,7 @@ const toast = useToast()
|
|||||||
const showInstallPrompt = ref(() => Promise.resolve()) // Async no-op till it is initialized
|
const showInstallPrompt = ref(() => Promise.resolve()) // Async no-op till it is initialized
|
||||||
|
|
||||||
const showSupport = ref(false)
|
const showSupport = ref(false)
|
||||||
|
const showSearch = ref(false)
|
||||||
const showLogin = ref(false)
|
const showLogin = ref(false)
|
||||||
const showTeamsModal = ref(false)
|
const showTeamsModal = ref(false)
|
||||||
|
|
||||||
@@ -171,6 +139,9 @@ const currentUser = useReadonlyStream(probableUser$, null)
|
|||||||
defineActionHandler("modals.support.toggle", () => {
|
defineActionHandler("modals.support.toggle", () => {
|
||||||
showSupport.value = !showSupport.value
|
showSupport.value = !showSupport.value
|
||||||
})
|
})
|
||||||
|
defineActionHandler("modals.search.toggle", () => {
|
||||||
|
showSearch.value = !showSearch.value
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener("online", () => {
|
window.addEventListener("online", () => {
|
||||||
@@ -208,11 +179,4 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Template refs
|
|
||||||
const tippyActions = ref<any | null>(null)
|
|
||||||
const profile = ref<any | null>(null)
|
|
||||||
const settings = ref<any | null>(null)
|
|
||||||
const logout = ref<any | null>(null)
|
|
||||||
const options = ref<any | null>(null)
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,112 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col p-4 space-y-4">
|
<div class="flex flex-col space-y-4 p-4 items-start">
|
||||||
<div class="flex flex-col">
|
<SmartToggle
|
||||||
<h2 class="inline-flex pb-1 font-semibold text-secondaryDark">
|
:on="PROXY_ENABLED"
|
||||||
{{ t("settings.interceptor") }}
|
@change="toggleSettingKey('PROXY_ENABLED')"
|
||||||
</h2>
|
|
||||||
<p class="inline-flex text-tiny">
|
|
||||||
{{ t("settings.interceptor_description") }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<SmartRadioGroup
|
|
||||||
:radios="interceptors"
|
|
||||||
:selected="interceptorSelection"
|
|
||||||
@change="toggleSettingKey"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="interceptorSelection == 'EXTENSIONS_ENABLED' && !extensionVersion"
|
|
||||||
class="flex space-x-2"
|
|
||||||
>
|
>
|
||||||
<ButtonSecondary
|
{{ t("settings.proxy") }}
|
||||||
to="https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
|
</SmartToggle>
|
||||||
blank
|
<SmartToggle
|
||||||
svg="brands/chrome"
|
:on="EXTENSIONS_ENABLED"
|
||||||
label="Chrome"
|
@change="toggleSettingKey('EXTENSIONS_ENABLED')"
|
||||||
outline
|
>
|
||||||
class="!flex-1"
|
{{ t("settings.extensions") }}:
|
||||||
/>
|
{{
|
||||||
<ButtonSecondary
|
extensionVersion != null
|
||||||
to="https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
|
? `v${extensionVersion.major}.${extensionVersion.minor}`
|
||||||
blank
|
: t("settings.extension_ver_not_reported")
|
||||||
svg="brands/firefox"
|
}}
|
||||||
label="Firefox"
|
</SmartToggle>
|
||||||
outline
|
|
||||||
class="!flex-1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watchEffect } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { KeysMatching } from "~/types/ts-utils"
|
import { KeysMatching } from "~/types/ts-utils"
|
||||||
import {
|
import { SettingsType, toggleSetting, useSetting } from "~/newstore/settings"
|
||||||
applySetting,
|
|
||||||
SettingsType,
|
|
||||||
toggleSetting,
|
|
||||||
useSetting,
|
|
||||||
} from "~/newstore/settings"
|
|
||||||
import { hasExtensionInstalled } from "~/helpers/strategies/ExtensionStrategy"
|
import { hasExtensionInstalled } from "~/helpers/strategies/ExtensionStrategy"
|
||||||
import { useI18n, usePolled } from "~/helpers/utils/composables"
|
import { useI18n } from "~/helpers/utils/composables"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
const PROXY_ENABLED = useSetting("PROXY_ENABLED")
|
const PROXY_ENABLED = useSetting("PROXY_ENABLED")
|
||||||
const EXTENSIONS_ENABLED = useSetting("EXTENSIONS_ENABLED")
|
const EXTENSIONS_ENABLED = useSetting("EXTENSIONS_ENABLED")
|
||||||
|
|
||||||
const toggleSettingKey = <
|
const toggleSettingKey = <K extends KeysMatching<SettingsType, boolean>>(
|
||||||
K extends KeysMatching<SettingsType | "BROWSER_ENABLED", boolean>
|
|
||||||
>(
|
|
||||||
key: K
|
key: K
|
||||||
) => {
|
) => {
|
||||||
interceptorSelection.value = key
|
if (key === "EXTENSIONS_ENABLED" && PROXY_ENABLED.value) {
|
||||||
if (key === "EXTENSIONS_ENABLED") {
|
toggleSetting("PROXY_ENABLED")
|
||||||
applySetting("EXTENSIONS_ENABLED", true)
|
|
||||||
if (PROXY_ENABLED.value) toggleSetting("PROXY_ENABLED")
|
|
||||||
}
|
}
|
||||||
if (key === "PROXY_ENABLED") {
|
if (key === "PROXY_ENABLED" && EXTENSIONS_ENABLED.value) {
|
||||||
applySetting("PROXY_ENABLED", true)
|
toggleSetting("EXTENSIONS_ENABLED")
|
||||||
if (EXTENSIONS_ENABLED.value) toggleSetting("EXTENSIONS_ENABLED")
|
|
||||||
}
|
|
||||||
if (key === "BROWSER_ENABLED") {
|
|
||||||
applySetting("PROXY_ENABLED", false)
|
|
||||||
applySetting("EXTENSIONS_ENABLED", false)
|
|
||||||
}
|
}
|
||||||
|
toggleSetting(key)
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
const extensionVersion = usePolled(5000, (stopPolling) => {
|
<script lang="ts">
|
||||||
const result = hasExtensionInstalled()
|
export default defineComponent({
|
||||||
? window.__POSTWOMAN_EXTENSION_HOOK__.getVersion()
|
data() {
|
||||||
: null
|
return {
|
||||||
|
extensionVersion: hasExtensionInstalled()
|
||||||
// We don't need to poll anymore after we get value
|
? window.__POSTWOMAN_EXTENSION_HOOK__.getVersion()
|
||||||
if (result) stopPolling()
|
: null,
|
||||||
|
}
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
const interceptors = computed(() => [
|
|
||||||
{ value: "BROWSER_ENABLED", label: t("state.none") },
|
|
||||||
{ value: "PROXY_ENABLED", label: t("settings.proxy") },
|
|
||||||
{
|
|
||||||
value: "EXTENSIONS_ENABLED",
|
|
||||||
label:
|
|
||||||
`${t("settings.extensions")}: ` +
|
|
||||||
(extensionVersion.value !== null
|
|
||||||
? `v${extensionVersion.value.major}.${extensionVersion.value.minor}`
|
|
||||||
: t("settings.extension_ver_not_reported")),
|
|
||||||
},
|
},
|
||||||
])
|
|
||||||
|
|
||||||
const interceptorSelection = ref("")
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (PROXY_ENABLED.value) {
|
|
||||||
interceptorSelection.value = "PROXY_ENABLED"
|
|
||||||
} else if (EXTENSIONS_ENABLED.value) {
|
|
||||||
interceptorSelection.value = "EXTENSIONS_ENABLED"
|
|
||||||
} else {
|
|
||||||
interceptorSelection.value = "BROWSER_ENABLED"
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,39 +6,16 @@
|
|||||||
@close="$emit('hide-modal')"
|
@close="$emit('hide-modal')"
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex transition flex-col border-b border-dividerLight">
|
<input
|
||||||
<input
|
id="command"
|
||||||
id="command"
|
v-model="search"
|
||||||
v-model="search"
|
v-focus
|
||||||
v-focus
|
type="text"
|
||||||
type="text"
|
autocomplete="off"
|
||||||
autocomplete="off"
|
name="command"
|
||||||
name="command"
|
:placeholder="`${t('app.type_a_command_search')}`"
|
||||||
:placeholder="`${t('app.type_a_command_search')}`"
|
class="bg-transparent border-b border-dividerLight flex flex-shrink-0 text-base text-secondaryDark p-6"
|
||||||
class="flex flex-shrink-0 p-6 text-base bg-transparent text-secondaryDark"
|
/>
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="flex flex-shrink-0 text-tiny text-secondaryLight px-4 pb-4 justify-between whitespace-nowrap overflow-auto hide-scrollbar <sm:hidden"
|
|
||||||
>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<kbd class="shortcut-key">↑</kbd>
|
|
||||||
<kbd class="shortcut-key">↓</kbd>
|
|
||||||
<span class="ml-2 truncate">
|
|
||||||
{{ t("action.to_navigate") }}
|
|
||||||
</span>
|
|
||||||
<kbd class="shortcut-key">↩</kbd>
|
|
||||||
<span class="ml-2 truncate">
|
|
||||||
{{ t("action.to_select") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<kbd class="shortcut-key">ESC</kbd>
|
|
||||||
<span class="ml-2 truncate">
|
|
||||||
{{ t("action.to_close") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<AppFuse
|
<AppFuse
|
||||||
v-if="search && show"
|
v-if="search && show"
|
||||||
:input="fuse"
|
:input="fuse"
|
||||||
@@ -47,14 +24,14 @@
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="flex flex-col flex-1 space-y-4 overflow-auto divide-y divide-dividerLight hide-scrollbar"
|
class="divide-dividerLight divide-y flex flex-col space-y-4 flex-1 overflow-auto hide-scrollbar"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(map, mapIndex) in mappings"
|
v-for="(map, mapIndex) in mappings"
|
||||||
:key="`map-${mapIndex}`"
|
:key="`map-${mapIndex}`"
|
||||||
class="flex flex-col"
|
class="flex flex-col"
|
||||||
>
|
>
|
||||||
<h5 class="px-6 py-2 my-2 text-secondaryLight">
|
<h5 class="my-2 text-secondaryLight py-2 px-6">
|
||||||
{{ t(map.section) }}
|
{{ t(map.section) }}
|
||||||
</h5>
|
</h5>
|
||||||
<AppPowerSearchEntry
|
<AppPowerSearchEntry
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
class="flex items-center flex-1 px-6 py-3 font-medium transition cursor-pointer search-entry focus:outline-none"
|
class="cursor-pointer flex flex-1 py-2 px-6 transition items-center search-entry focus:outline-none"
|
||||||
:class="{ active: active }"
|
:class="{ active: active }"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
@click="$emit('action', shortcut.action)"
|
@click="$emit('action', shortcut.action)"
|
||||||
@keydown.enter="$emit('action', shortcut.action)"
|
@keydown.enter="$emit('action', shortcut.action)"
|
||||||
>
|
>
|
||||||
<SmartIcon
|
<SmartIcon
|
||||||
class="mr-4 transition opacity-50 svg-icons"
|
class="mr-4 opacity-50 transition svg-icons"
|
||||||
:class="{ 'opacity-100 text-secondaryDark': active }"
|
:class="{ 'opacity-100 text-secondaryDark': active }"
|
||||||
:name="shortcut.icon"
|
:name="shortcut.icon"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="flex flex-1 mr-4 transition"
|
class="flex font-medium flex-1 mr-4 transition"
|
||||||
:class="{ 'text-secondaryDark': active }"
|
:class="{ 'text-secondaryDark': active }"
|
||||||
>
|
>
|
||||||
{{ t(shortcut.label) }}
|
{{ t(shortcut.label) }}
|
||||||
@@ -33,12 +33,7 @@ import { useI18n } from "~/helpers/utils/composables"
|
|||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
shortcut: {
|
shortcut: Object
|
||||||
label: string
|
|
||||||
keys: string[]
|
|
||||||
action: string
|
|
||||||
icon: string
|
|
||||||
}
|
|
||||||
active: Boolean
|
active: Boolean
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
@@ -69,4 +64,13 @@ defineProps<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shortcut-key {
|
||||||
|
@apply bg-dividerLight;
|
||||||
|
@apply rounded;
|
||||||
|
@apply ml-2;
|
||||||
|
@apply py-1;
|
||||||
|
@apply px-2;
|
||||||
|
@apply inline-flex;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
14
packages/hoppscotch-app/components/app/Section.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<section :id="label.toLowerCase()" class="flex flex-col flex-1 relative">
|
||||||
|
<slot></slot>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: "Section",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -5,11 +5,11 @@
|
|||||||
@close="hideModal"
|
@close="hideModal"
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<p class="px-2 mb-8 text-secondaryLight">
|
<p class="text-secondaryLight mb-8 px-2">
|
||||||
{{ t("app.invite_description") }}
|
{{ t("app.invite_description") }}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-col px-2 space-y-2">
|
<div class="flex flex-col space-y-2 px-2">
|
||||||
<div class="grid grid-cols-3 gap-4">
|
<div class="grid gap-4 grid-cols-3">
|
||||||
<a
|
<a
|
||||||
v-for="(platform, index) in platforms"
|
v-for="(platform, index) in platforms"
|
||||||
:key="`platform-${index}`"
|
:key="`platform-${index}`"
|
||||||
@@ -17,13 +17,13 @@
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
class="share-link"
|
class="share-link"
|
||||||
>
|
>
|
||||||
<SmartIcon :name="platform.icon" class="w-6 h-6" />
|
<SmartIcon :name="platform.icon" class="h-6 w-6" />
|
||||||
<span class="mt-3">
|
<span class="mt-3">
|
||||||
{{ platform.name }}
|
{{ platform.name }}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<button class="share-link" @click="copyAppLink">
|
<button class="share-link" @click="copyAppLink">
|
||||||
<SmartIcon class="w-6 h-6 text-xl" :name="copyIcon" />
|
<SmartIcon class="h-6 text-xl w-6" :name="copyIcon" />
|
||||||
<span class="mt-3">
|
<span class="mt-3">
|
||||||
{{ t("app.copy") }}
|
{{ t("app.copy") }}
|
||||||
</span>
|
</span>
|
||||||
@@ -56,7 +56,7 @@ const text = "Hoppscotch - Open source API development ecosystem."
|
|||||||
const description =
|
const description =
|
||||||
"Helps you create requests faster, saving precious time on development."
|
"Helps you create requests faster, saving precious time on development."
|
||||||
const subject = "Checkout Hoppscotch - an open source API development ecosystem"
|
const subject = "Checkout Hoppscotch - an open source API development ecosystem"
|
||||||
const summary = `Hi there!%0D%0A%0D%0AI thought you'll like this new platform that I joined called Hoppscotch - https://hoppscotch.io.%0D%0AIt is a simple and intuitive interface for creating and managing your APIs. You can build, test, document, and share your APIs.%0D%0A%0D%0AThe best part about Hoppscotch is that it is open source and free to get started.%0D%0A%0D%0A`
|
const summary = `Hi there!%0D%0A%0D%0AI thought you’ll like this new platform that I joined called Hoppscotch - https://hoppscotch.io.%0D%0AIt is a simple and intuitive interface for creating and managing your APIs. You can build, test, document, and share your APIs.%0D%0A%0D%0AThe best part about Hoppscotch is that it is open source and free to get started.%0D%0A%0D%0A`
|
||||||
const twitter = "hoppscotch_io"
|
const twitter = "hoppscotch_io"
|
||||||
|
|
||||||
const copyIcon = ref("copy")
|
const copyIcon = ref("copy")
|
||||||
|
|||||||
@@ -1,28 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<AppSlideOver :show="show" @close="close()">
|
<AppSlideOver :show="show" @close="close()">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="sticky top-0 z-10 flex flex-col bg-primary">
|
<div
|
||||||
<div
|
class="bg-primary border-b border-dividerLight flex p-2 top-0 z-10 sticky items-center justify-between"
|
||||||
class="flex items-center justify-between p-2 border-b border-dividerLight"
|
>
|
||||||
>
|
<h3 class="ml-4 heading">{{ t("app.shortcuts") }}</h3>
|
||||||
<h3 class="ml-4 heading">{{ t("app.shortcuts") }}</h3>
|
<div class="flex">
|
||||||
<ButtonSecondary svg="x" @click.native="close()" />
|
<ButtonSecondary svg="x" @click.native="close()" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col px-6 py-4 border-b border-dividerLight">
|
</div>
|
||||||
|
<div class="bg-primary border-b border-dividerLight">
|
||||||
|
<div class="flex flex-col my-4 mx-6">
|
||||||
<input
|
<input
|
||||||
v-model="filterText"
|
v-model="filterText"
|
||||||
type="search"
|
type="search"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="flex px-4 py-2 border rounded bg-primaryLight border-dividerLight focus-visible:border-divider"
|
class="bg-primaryLight border border-dividerLight rounded flex w-full py-2 px-4 focus-visible:border-divider"
|
||||||
:placeholder="`${t('action.search')}`"
|
:placeholder="`${t('action.search')}`"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="filterText" class="flex flex-col divide-y divide-dividerLight">
|
<div
|
||||||
|
v-if="filterText"
|
||||||
|
class="divide-dividerLight divide-y flex flex-col flex-1 overflow-auto hide-scrollbar"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(map, mapIndex) in searchResults"
|
v-for="(map, mapIndex) in searchResults"
|
||||||
:key="`map-${mapIndex}`"
|
:key="`map-${mapIndex}`"
|
||||||
class="px-6 py-4 space-y-4"
|
class="space-y-4 py-4 px-6"
|
||||||
>
|
>
|
||||||
<h1 class="font-semibold text-secondaryDark">
|
<h1 class="font-semibold text-secondaryDark">
|
||||||
{{ t(map.item.section) }}
|
{{ t(map.item.section) }}
|
||||||
@@ -35,19 +40,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="searchResults.length === 0"
|
v-if="searchResults.length === 0"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<i class="pb-2 opacity-75 material-icons">manage_search</i>
|
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
||||||
<span class="my-2 text-center">
|
<span class="text-center">
|
||||||
{{ t("state.nothing_found") }} "{{ filterText }}"
|
{{ t("state.nothing_found") }} "{{ filterText }}"
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex flex-col divide-y divide-dividerLight">
|
<div
|
||||||
|
v-else
|
||||||
|
class="divide-dividerLight divide-y flex flex-col flex-1 overflow-auto hide-scrollbar"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(map, mapIndex) in mappings"
|
v-for="(map, mapIndex) in mappings"
|
||||||
:key="`map-${mapIndex}`"
|
:key="`map-${mapIndex}`"
|
||||||
class="px-6 py-4 space-y-4"
|
class="space-y-4 py-4 px-6"
|
||||||
>
|
>
|
||||||
<h1 class="font-semibold text-secondaryDark">
|
<h1 class="font-semibold text-secondaryDark">
|
||||||
{{ t(map.section) }}
|
{{ t(map.section) }}
|
||||||
@@ -94,3 +102,14 @@ const close = () => {
|
|||||||
emit("close")
|
emit("close")
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.shortcut-key {
|
||||||
|
@apply bg-dividerLight;
|
||||||
|
@apply rounded;
|
||||||
|
@apply ml-2;
|
||||||
|
@apply py-1;
|
||||||
|
@apply px-2;
|
||||||
|
@apply inline-flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center py-1">
|
<div class="flex items-center">
|
||||||
<span class="flex flex-1 mr-4">
|
<span class="flex flex-1 mr-4">
|
||||||
{{ t(shortcut.label) }}
|
{{ t(shortcut.label) }}
|
||||||
</span>
|
</span>
|
||||||
@@ -19,9 +19,17 @@ import { useI18n } from "~/helpers/utils/composables"
|
|||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
shortcut: {
|
shortcut: Object
|
||||||
label: string
|
|
||||||
keys: string[]
|
|
||||||
}
|
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.shortcut-key {
|
||||||
|
@apply bg-dividerLight;
|
||||||
|
@apply rounded;
|
||||||
|
@apply ml-2;
|
||||||
|
@apply py-1;
|
||||||
|
@apply px-2;
|
||||||
|
@apply inline-flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<aside class="flex justify-between h-full md:flex-col">
|
<aside class="flex h-full justify-between md:flex-col">
|
||||||
<nav class="flex flex-1 flex-nowrap md:flex-col md:flex-none">
|
<nav class="flex flex-nowrap flex-1 md:flex-col md:flex-none">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-for="(navigation, index) in primaryNavigation"
|
v-for="(navigation, index) in primaryNavigation"
|
||||||
:key="`navigation-${index}`"
|
:key="`navigation-${index}`"
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
class="nav-link"
|
class="nav-link"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
|
<i v-if="navigation.icon" class="material-icons">
|
||||||
|
{{ navigation.icon }}
|
||||||
|
</i>
|
||||||
<div v-if="navigation.svg">
|
<div v-if="navigation.svg">
|
||||||
<SmartIcon :name="navigation.svg" class="svg-icons" />
|
<SmartIcon :name="navigation.svg" class="svg-icons" />
|
||||||
</div>
|
</div>
|
||||||
@@ -100,7 +103,9 @@ const primaryNavigation = [
|
|||||||
|
|
||||||
span {
|
span {
|
||||||
@apply mt-2;
|
@apply mt-2;
|
||||||
@apply text-tiny;
|
@apply font-font-medium;
|
||||||
|
|
||||||
|
font-size: calc(var(--body-font-size) - 0.062rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.exact-active-link {
|
&.exact-active-link {
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<transition v-if="show" name="fade" appear>
|
<transition v-if="show" name="fade" appear>
|
||||||
<div class="fixed inset-0 z-20 transition-opacity" @keydown.esc="close()">
|
<div class="inset-0 transition-opacity z-20 fixed" @keydown.esc="close()">
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-primaryLight opacity-90"
|
class="bg-primaryLight opacity-90 inset-0 absolute"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click="close()"
|
@click="close()"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<aside
|
<aside
|
||||||
class="fixed top-0 right-0 z-30 flex flex-col h-full max-w-full overflow-auto transition duration-300 ease-in-out transform bg-primary w-96 hide-scrollbar"
|
class="bg-primary flex flex-col h-full max-w-full transform transition top-0 ease-in-out right-0 w-96 z-30 duration-300 fixed overflow-auto"
|
||||||
:class="show ? 'shadow-xl translate-x-0' : 'translate-x-full'"
|
:class="show ? 'shadow-xl translate-x-0' : 'translate-x-full'"
|
||||||
>
|
>
|
||||||
<slot name="content"></slot>
|
<slot name="content"></slot>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
|
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
|
||||||
:exact="exact"
|
:exact="exact"
|
||||||
:blank="blank"
|
:blank="blank"
|
||||||
class="inline-flex items-center justify-center py-2 font-bold transition focus:outline-none focus-visible:bg-accentDark"
|
class="font-bold py-2 transition inline-flex items-center justify-center focus:outline-none focus-visible:bg-accentDark"
|
||||||
:class="[
|
:class="[
|
||||||
color
|
color
|
||||||
? `text-${color}-800 bg-${color}-200 hover:(text-${color}-900 bg-${color}-300) focus-visible:(text-${color}-900 bg-${color}-300)`
|
? `text-${color}-800 bg-${color}-200 hover:(text-${color}-900 bg-${color}-300) focus-visible:(text-${color}-900 bg-${color}-300)`
|
||||||
@@ -52,11 +52,11 @@
|
|||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
<div v-if="shortcut.length" class="ml-2 <sm:hidden">
|
<div v-if="shortcut.length" class="ml-2">
|
||||||
<kbd
|
<kbd
|
||||||
v-for="(key, index) in shortcut"
|
v-for="(key, index) in shortcut"
|
||||||
:key="`key-${index}`"
|
:key="`key-${index}`"
|
||||||
class="shortcut-key !bg-accentLight"
|
class="bg-accentLight rounded ml-1 px-1 inline-flex"
|
||||||
>
|
>
|
||||||
{{ key }}
|
{{ key }}
|
||||||
</kbd>
|
</kbd>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
|
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
|
||||||
:exact="exact"
|
:exact="exact"
|
||||||
:blank="blank"
|
:blank="blank"
|
||||||
class="inline-flex items-center justify-center py-2 font-semibold transition whitespace-nowrap focus:outline-none"
|
class="font-semibold py-2 transition inline-flex items-center justify-center whitespace-nowrap focus:outline-none"
|
||||||
:class="[
|
:class="[
|
||||||
color
|
color
|
||||||
? `text-${color}-500 hover:text-${color}-600 focus-visible:text-${color}-600`
|
? `text-${color}-500 hover:text-${color}-600 focus-visible:text-${color}-600`
|
||||||
@@ -51,11 +51,11 @@
|
|||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
<div v-if="shortcut.length" class="ml-2 <sm:hidden">
|
<div v-if="shortcut.length" class="ml-2">
|
||||||
<kbd
|
<kbd
|
||||||
v-for="(key, index) in shortcut"
|
v-for="(key, index) in shortcut"
|
||||||
:key="`key-${index}`"
|
:key="`key-${index}`"
|
||||||
class="shortcut-key"
|
class="bg-dividerLight rounded text-secondaryLight ml-1 px-1 inline-flex"
|
||||||
>
|
>
|
||||||
{{ key }}
|
{{ key }}
|
||||||
</kbd>
|
</kbd>
|
||||||
|
|||||||
@@ -1,142 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<SmartModal
|
<SmartModal
|
||||||
v-if="show"
|
v-if="show"
|
||||||
:title="`${t('modal.collections')}`"
|
:title="`${$t('modal.import_export')} ${$t('modal.collections')}`"
|
||||||
max-width="sm:max-w-md"
|
max-width="sm:max-w-md"
|
||||||
@close="hideModal"
|
@close="hideModal"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="importerType !== null"
|
v-if="mode == 'import_from_my_collections'"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.go_back')"
|
:title="$t('action.go_back')"
|
||||||
svg="arrow-left"
|
svg="arrow-left"
|
||||||
@click.native="resetImport"
|
@click.native="
|
||||||
|
() => {
|
||||||
|
mode = 'import_export'
|
||||||
|
mySelectedCollectionID = undefined
|
||||||
|
}
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</template>
|
<span>
|
||||||
<template #body>
|
<tippy
|
||||||
<div v-if="importerType !== null" class="flex flex-col">
|
v-if="
|
||||||
<div class="flex pb-6 flex-col px-2">
|
mode == 'import_export' && collectionsType.type == 'my-collections'
|
||||||
<div
|
"
|
||||||
v-for="(step, index) in importerSteps"
|
ref="options"
|
||||||
:key="`step-${index}`"
|
interactive
|
||||||
class="flex flex-col space-y-8"
|
trigger="click"
|
||||||
>
|
theme="popover"
|
||||||
<div v-if="step.name === 'FILE_IMPORT'" class="space-y-4">
|
arrow
|
||||||
<p class="flex items-center">
|
>
|
||||||
<span
|
<template #trigger>
|
||||||
class="inline-flex border-4 border-primary items-center justify-center flex-shrink-0 mr-4 rounded-full text-dividerDark"
|
<ButtonSecondary
|
||||||
:class="{
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
'!text-green-500': hasFile,
|
:title="$t('action.more')"
|
||||||
}"
|
svg="more-vertical"
|
||||||
>
|
|
||||||
<i class="material-icons">check_circle</i>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{{ t(`${step.metadata.caption}`) }}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p class="flex flex-col ml-10">
|
|
||||||
<input
|
|
||||||
id="inputChooseFileToImportFrom"
|
|
||||||
ref="inputChooseFileToImportFrom"
|
|
||||||
name="inputChooseFileToImportFrom"
|
|
||||||
type="file"
|
|
||||||
class="transition cursor-pointer file:transition file:cursor-pointer text-secondary hover:text-secondaryDark file:mr-2 file:py-2 file:px-4 file:rounded file:border-0 file:text-secondary hover:file:text-secondaryDark file:bg-primaryLight hover:file:bg-primaryDark"
|
|
||||||
:accept="step.metadata.acceptedFileTypes"
|
|
||||||
@change="onFileChange"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="step.name === 'URL_IMPORT'" class="space-y-4">
|
|
||||||
<p class="flex items-center">
|
|
||||||
<span
|
|
||||||
class="inline-flex border-4 border-primary items-center justify-center flex-shrink-0 mr-4 rounded-full text-dividerDark"
|
|
||||||
:class="{
|
|
||||||
'!text-green-500': hasGist,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<i class="material-icons">check_circle</i>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{{ t(`${step.metadata.caption}`) }}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p class="flex flex-col ml-10">
|
|
||||||
<input
|
|
||||||
v-model="inputChooseGistToImportFrom"
|
|
||||||
type="url"
|
|
||||||
class="input"
|
|
||||||
:placeholder="`${$t('import.gist_url')}`"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="step.name === 'TARGET_MY_COLLECTION'"
|
|
||||||
class="flex flex-col px-2"
|
|
||||||
>
|
|
||||||
<div class="select-wrapper">
|
|
||||||
<select
|
|
||||||
v-model="mySelectedCollectionID"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off"
|
|
||||||
class="select"
|
|
||||||
autofocus
|
|
||||||
>
|
|
||||||
<option :key="undefined" :value="undefined" disabled selected>
|
|
||||||
{{ t("collection.select") }}
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
v-for="(collection, collectionIndex) in myCollections"
|
|
||||||
:key="`collection-${collectionIndex}`"
|
|
||||||
:value="collectionIndex"
|
|
||||||
>
|
|
||||||
{{ collection.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ButtonPrimary
|
|
||||||
:label="t('import.title')"
|
|
||||||
:disabled="enableImportButton"
|
|
||||||
class="mx-2"
|
|
||||||
:loading="importingMyCollections"
|
|
||||||
@click.native="finishImport"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else class="flex flex-col px-2">
|
|
||||||
<SmartExpand>
|
|
||||||
<template #body>
|
|
||||||
<SmartItem
|
|
||||||
v-for="(importer, index) in importerModules"
|
|
||||||
:key="`importer-${index}`"
|
|
||||||
:svg="importer.icon"
|
|
||||||
:label="t(`${importer.name}`)"
|
|
||||||
@click.native="importerType = index"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</SmartExpand>
|
|
||||||
<hr />
|
|
||||||
<div class="flex flex-col space-y-2">
|
|
||||||
<SmartItem
|
<SmartItem
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
icon="assignment_returned"
|
||||||
:title="t('action.download_file')"
|
:label="$t('import.from_gist')"
|
||||||
svg="download"
|
@click.native="
|
||||||
:label="t('export.as_json')"
|
() => {
|
||||||
@click.native="exportJSON"
|
readCollectionGist
|
||||||
|
$refs.options.tippy().hide()
|
||||||
|
}
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="
|
:title="
|
||||||
!currentUser
|
!currentUser
|
||||||
? `${t('export.require_github')}`
|
? $t('export.require_github')
|
||||||
: currentUser.provider !== 'github.com'
|
: currentUser.provider !== 'github.com'
|
||||||
? `${t('export.require_github')}`
|
? $t('export.require_github')
|
||||||
: undefined
|
: null
|
||||||
"
|
"
|
||||||
class="flex"
|
|
||||||
>
|
>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
:disabled="
|
:disabled="
|
||||||
@@ -146,276 +64,491 @@
|
|||||||
? true
|
? true
|
||||||
: false
|
: false
|
||||||
"
|
"
|
||||||
svg="github"
|
icon="assignment_turned_in"
|
||||||
:label="t('export.create_secret_gist')"
|
:label="$t('export.create_secret_gist')"
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
() => {
|
||||||
createCollectionGist()
|
createCollectionGist()
|
||||||
|
$refs.options.tippy().hide()
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
</tippy>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<div v-if="mode == 'import_export'" class="flex flex-col space-y-2">
|
||||||
|
<SmartItem
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.replace_current')"
|
||||||
|
svg="file"
|
||||||
|
:label="$t('action.replace_json')"
|
||||||
|
@click.native="openDialogChooseFileToReplaceWith"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
ref="inputChooseFileToReplaceWith"
|
||||||
|
class="input"
|
||||||
|
type="file"
|
||||||
|
style="display: none"
|
||||||
|
accept="application/json"
|
||||||
|
@change="replaceWithJSON"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.preserve_current')"
|
||||||
|
svg="folder-plus"
|
||||||
|
:label="$t('import.json')"
|
||||||
|
@click.native="openDialogChooseFileToImportFrom"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
ref="inputChooseFileToImportFrom"
|
||||||
|
class="input"
|
||||||
|
type="file"
|
||||||
|
style="display: none"
|
||||||
|
accept="application/json"
|
||||||
|
@change="importFromJSON"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
v-if="collectionsType.type == 'team-collections'"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.preserve_current')"
|
||||||
|
svg="user"
|
||||||
|
:label="$t('import.from_my_collections')"
|
||||||
|
@click.native="mode = 'import_from_my_collections'"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.download_file')"
|
||||||
|
svg="download"
|
||||||
|
:label="$t('export.as_json')"
|
||||||
|
@click.native="exportJSON"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="mode == 'import_from_my_collections'"
|
||||||
|
class="flex flex-col px-2"
|
||||||
|
>
|
||||||
|
<div class="select-wrapper">
|
||||||
|
<select
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
class="select"
|
||||||
|
autofocus
|
||||||
|
@change="
|
||||||
|
($event) => {
|
||||||
|
mySelectedCollectionID = $event.target.value
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
:key="undefined"
|
||||||
|
:value="undefined"
|
||||||
|
hidden
|
||||||
|
disabled
|
||||||
|
selected
|
||||||
|
>
|
||||||
|
Select Collection
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="(collection, index) in myCollections"
|
||||||
|
:key="`collection-${index}`"
|
||||||
|
:value="index"
|
||||||
|
>
|
||||||
|
{{ collection.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<div v-if="mode == 'import_from_my_collections'">
|
||||||
|
<span>
|
||||||
|
<ButtonPrimary
|
||||||
|
:disabled="mySelectedCollectionID == undefined"
|
||||||
|
svg="folder-plus"
|
||||||
|
:label="$t('import.title')"
|
||||||
|
@click.native="importFromMyCollections"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</SmartModal>
|
</SmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script>
|
||||||
import { computed, ref, watch } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import * as E from "fp-ts/Either"
|
|
||||||
import { HoppRESTRequest, HoppCollection } from "@hoppscotch/data"
|
|
||||||
import { apolloClient } from "~/helpers/apollo"
|
|
||||||
import {
|
|
||||||
useAxios,
|
|
||||||
useI18n,
|
|
||||||
useReadonlyStream,
|
|
||||||
useToast,
|
|
||||||
} from "~/helpers/utils/composables"
|
|
||||||
import { currentUser$ } from "~/helpers/fb/auth"
|
import { currentUser$ } from "~/helpers/fb/auth"
|
||||||
import * as teamUtils from "~/helpers/teams/utils"
|
import * as teamUtils from "~/helpers/teams/utils"
|
||||||
import { appendRESTCollections, restCollections$ } from "~/newstore/collections"
|
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||||
import { RESTCollectionImporters } from "~/helpers/import-export/import/importers"
|
import {
|
||||||
import { StepReturnValue } from "~/helpers/import-export/steps"
|
restCollections$,
|
||||||
|
setRESTCollections,
|
||||||
|
appendRESTCollections,
|
||||||
|
} from "~/newstore/collections"
|
||||||
|
|
||||||
const props = defineProps<{
|
export default defineComponent({
|
||||||
show: boolean
|
props: {
|
||||||
collectionsType:
|
show: Boolean,
|
||||||
| {
|
collectionsType: { type: Object, default: () => {} },
|
||||||
type: "team-collections"
|
},
|
||||||
selectedTeam: {
|
setup() {
|
||||||
id: string
|
return {
|
||||||
|
myCollections: useReadonlyStream(restCollections$, []),
|
||||||
|
currentUser: useReadonlyStream(currentUser$, null),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showJsonCode: false,
|
||||||
|
mode: "import_export",
|
||||||
|
mySelectedCollectionID: undefined,
|
||||||
|
collectionJson: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async createCollectionGist() {
|
||||||
|
this.getJSONCollection()
|
||||||
|
await this.$axios
|
||||||
|
.$post(
|
||||||
|
"https://api.github.com/gists",
|
||||||
|
{
|
||||||
|
files: {
|
||||||
|
"hoppscotch-collections.json": {
|
||||||
|
content: this.collectionJson,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${this.currentUser.accessToken}`,
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
this.$toast.success(this.$t("export.gist_created"))
|
||||||
|
window.open(res.html_url)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
this.$toast.error(this.$t("error.something_went_wrong"))
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async readCollectionGist() {
|
||||||
|
const gist = prompt(this.$t("import.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 }) => {
|
||||||
|
const collections = JSON.parse(Object.values(files)[0].content)
|
||||||
|
setRESTCollections(collections)
|
||||||
|
this.fileImported()
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
this.failedImport()
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
hideModal() {
|
||||||
|
this.mode = "import_export"
|
||||||
|
this.mySelectedCollectionID = undefined
|
||||||
|
this.$emit("hide-modal")
|
||||||
|
},
|
||||||
|
openDialogChooseFileToReplaceWith() {
|
||||||
|
this.$refs.inputChooseFileToReplaceWith.click()
|
||||||
|
},
|
||||||
|
openDialogChooseFileToImportFrom() {
|
||||||
|
this.$refs.inputChooseFileToImportFrom.click()
|
||||||
|
},
|
||||||
|
replaceWithJSON() {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = ({ target }) => {
|
||||||
|
const content = target.result
|
||||||
|
let collections = JSON.parse(content)
|
||||||
|
if (collections[0]) {
|
||||||
|
const [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()
|
||||||
|
}
|
||||||
|
if (this.collectionsType.type === "team-collections") {
|
||||||
|
teamUtils
|
||||||
|
.replaceWithJSON(
|
||||||
|
this.$apollo,
|
||||||
|
collections,
|
||||||
|
this.collectionsType.selectedTeam.id
|
||||||
|
)
|
||||||
|
.then((status) => {
|
||||||
|
if (status) {
|
||||||
|
this.fileImported()
|
||||||
|
} else {
|
||||||
|
this.failedImport()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
this.failedImport()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setRESTCollections(collections)
|
||||||
|
this.fileImported()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| { type: "my-collections" }
|
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
|
||||||
}>()
|
this.$refs.inputChooseFileToReplaceWith.value = ""
|
||||||
|
},
|
||||||
const emit = defineEmits<{
|
importFromJSON() {
|
||||||
(e: "hide-modal"): void
|
const reader = new FileReader()
|
||||||
(e: "update-team-collections"): void
|
reader.onload = ({ target }) => {
|
||||||
}>()
|
const content = target.result
|
||||||
|
let collections = JSON.parse(content)
|
||||||
const axios = useAxios()
|
if (collections[0]) {
|
||||||
const toast = useToast()
|
const [name, folders, requests] = Object.keys(collections[0])
|
||||||
const t = useI18n()
|
if (
|
||||||
const myCollections = useReadonlyStream(restCollections$, [])
|
name === "name" &&
|
||||||
const currentUser = useReadonlyStream(currentUser$, null)
|
folders === "folders" &&
|
||||||
|
requests === "requests"
|
||||||
// Template refs
|
) {
|
||||||
const mode = ref("import_export")
|
// Do nothing
|
||||||
const mySelectedCollectionID = ref<undefined | number>(undefined)
|
}
|
||||||
const collectionJson = ref("")
|
} else if (
|
||||||
const inputChooseFileToImportFrom = ref<HTMLInputElement | any>()
|
collections.info &&
|
||||||
const inputChooseGistToImportFrom = ref<string>("")
|
collections.info.schema.includes("v2.1.0")
|
||||||
|
) {
|
||||||
const getJSONCollection = async () => {
|
// replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
|
||||||
if (props.collectionsType.type === "my-collections") {
|
collections = JSON.parse(
|
||||||
collectionJson.value = JSON.stringify(myCollections.value, null, 2)
|
content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>")
|
||||||
} else {
|
)
|
||||||
collectionJson.value = await teamUtils.exportAsJSON(
|
collections = [this.parsePostmanCollection(collections)]
|
||||||
apolloClient,
|
} else {
|
||||||
props.collectionsType.selectedTeam.id
|
this.failedImport()
|
||||||
)
|
return
|
||||||
}
|
}
|
||||||
return collectionJson.value
|
if (this.collectionsType.type === "team-collections") {
|
||||||
}
|
teamUtils
|
||||||
|
.importFromJSON(
|
||||||
const createCollectionGist = async () => {
|
this.$apollo,
|
||||||
if (!currentUser.value) {
|
collections,
|
||||||
toast.error(t("profile.no_permission").toString())
|
this.collectionsType.selectedTeam.id
|
||||||
|
)
|
||||||
return
|
.then((status) => {
|
||||||
}
|
if (status) {
|
||||||
|
this.$emit("update-team-collections")
|
||||||
getJSONCollection()
|
this.fileImported()
|
||||||
|
} else {
|
||||||
try {
|
this.failedImport()
|
||||||
const res = await axios.$post(
|
}
|
||||||
"https://api.github.com/gists",
|
})
|
||||||
{
|
.catch((e) => {
|
||||||
files: {
|
console.error(e)
|
||||||
"hoppscotch-collections.json": {
|
this.failedImport()
|
||||||
content: collectionJson.value,
|
})
|
||||||
},
|
} else {
|
||||||
},
|
appendRESTCollections(collections)
|
||||||
},
|
this.fileImported()
|
||||||
{
|
}
|
||||||
headers: {
|
|
||||||
Authorization: `token ${currentUser.value.accessToken}`,
|
|
||||||
Accept: "application/vnd.github.v3+json",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
||||||
|
this.$refs.inputChooseFileToImportFrom.value = ""
|
||||||
toast.success(t("export.gist_created").toString())
|
},
|
||||||
window.open(res.html_url)
|
importFromMyCollections() {
|
||||||
} catch (e) {
|
teamUtils
|
||||||
toast.error(t("error.something_went_wrong").toString())
|
.importFromMyCollections(
|
||||||
console.error(e)
|
this.$apollo,
|
||||||
}
|
this.mySelectedCollectionID,
|
||||||
}
|
this.collectionsType.selectedTeam.id
|
||||||
|
)
|
||||||
const fileImported = () => {
|
.then((success) => {
|
||||||
toast.success(t("state.file_imported").toString())
|
if (success) {
|
||||||
hideModal()
|
this.fileImported()
|
||||||
}
|
this.$emit("update-team-collections")
|
||||||
|
} else {
|
||||||
const failedImport = () => {
|
this.failedImport()
|
||||||
toast.error(t("import.failed").toString())
|
}
|
||||||
}
|
})
|
||||||
|
.catch((e) => {
|
||||||
const hideModal = () => {
|
console.error(e)
|
||||||
mode.value = "import_export"
|
this.failedImport()
|
||||||
mySelectedCollectionID.value = undefined
|
})
|
||||||
resetImport()
|
},
|
||||||
emit("hide-modal")
|
async getJSONCollection() {
|
||||||
}
|
if (this.collectionsType.type === "my-collections") {
|
||||||
|
this.collectionJson = JSON.stringify(this.myCollections, null, 2)
|
||||||
const stepResults = ref<StepReturnValue[]>([])
|
|
||||||
|
|
||||||
watch(mySelectedCollectionID, (newValue) => {
|
|
||||||
if (newValue === undefined) return
|
|
||||||
stepResults.value = []
|
|
||||||
stepResults.value.push(newValue)
|
|
||||||
})
|
|
||||||
|
|
||||||
const importingMyCollections = ref(false)
|
|
||||||
|
|
||||||
const importToTeams = async (content: HoppCollection<HoppRESTRequest>) => {
|
|
||||||
importingMyCollections.value = true
|
|
||||||
if (props.collectionsType.type !== "team-collections") return
|
|
||||||
await teamUtils
|
|
||||||
.importFromJSON(
|
|
||||||
apolloClient,
|
|
||||||
content,
|
|
||||||
props.collectionsType.selectedTeam.id
|
|
||||||
)
|
|
||||||
.then((status) => {
|
|
||||||
if (status) {
|
|
||||||
emit("update-team-collections")
|
|
||||||
} else {
|
} else {
|
||||||
console.error(status)
|
this.collectionJson = await teamUtils.exportAsJSON(
|
||||||
|
this.$apollo,
|
||||||
|
this.collectionsType.selectedTeam.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return this.collectionJson
|
||||||
|
},
|
||||||
|
exportJSON() {
|
||||||
|
this.getJSONCollection()
|
||||||
|
const dataToWrite = this.collectionJson
|
||||||
|
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
// TODO get uri from meta
|
||||||
|
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
this.$toast.success(this.$t("state.download_started"))
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
fileImported() {
|
||||||
|
this.$toast.success(this.$t("state.file_imported"))
|
||||||
|
},
|
||||||
|
failedImport() {
|
||||||
|
this.$toast.error(this.$t("import.failed"))
|
||||||
|
},
|
||||||
|
parsePostmanCollection({ info, name, item }) {
|
||||||
|
const hoppscotchCollection = {
|
||||||
|
name: "",
|
||||||
|
folders: [],
|
||||||
|
requests: [],
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
importingMyCollections.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const exportJSON = () => {
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
getJSONCollection()
|
|
||||||
|
|
||||||
const dataToWrite = collectionJson.value
|
if (item && item.length > 0) {
|
||||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
for (const collectionItem of item) {
|
||||||
const a = document.createElement("a")
|
if (collectionItem.request) {
|
||||||
const url = URL.createObjectURL(file)
|
if (
|
||||||
a.href = url
|
Object.prototype.hasOwnProperty.call(
|
||||||
|
hoppscotchCollection,
|
||||||
|
"folders"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
|
hoppscotchCollection.requests.push(
|
||||||
|
this.parsePostmanRequest(collectionItem)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
hoppscotchCollection.name = name || ""
|
||||||
|
hoppscotchCollection.requests.push(
|
||||||
|
this.parsePostmanRequest(collectionItem)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (this.hasFolder(collectionItem)) {
|
||||||
|
hoppscotchCollection.folders.push(
|
||||||
|
this.parsePostmanCollection(collectionItem)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
hoppscotchCollection.requests.push(
|
||||||
|
this.parsePostmanRequest(collectionItem)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hoppscotchCollection
|
||||||
|
},
|
||||||
|
parsePostmanRequest({ name, request }) {
|
||||||
|
const pwRequest = {
|
||||||
|
url: "",
|
||||||
|
path: "",
|
||||||
|
method: "",
|
||||||
|
auth: "",
|
||||||
|
httpUser: "",
|
||||||
|
httpPassword: "",
|
||||||
|
passwordFieldType: "password",
|
||||||
|
bearerToken: "",
|
||||||
|
headers: [],
|
||||||
|
params: [],
|
||||||
|
bodyParams: [],
|
||||||
|
rawParams: "",
|
||||||
|
rawInput: false,
|
||||||
|
contentType: "",
|
||||||
|
requestType: "",
|
||||||
|
name: "",
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: get uri from meta
|
pwRequest.name = name
|
||||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
if (request.url) {
|
||||||
document.body.appendChild(a)
|
const requestObjectUrl = request.url.raw.match(
|
||||||
a.click()
|
/^(.+:\/\/[^/]+|{[^/]+})(\/[^?]+|).*$/
|
||||||
toast.success(t("state.download_started").toString())
|
)
|
||||||
setTimeout(() => {
|
if (requestObjectUrl) {
|
||||||
document.body.removeChild(a)
|
pwRequest.url = requestObjectUrl[1]
|
||||||
URL.revokeObjectURL(url)
|
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
|
||||||
}, 1000)
|
}
|
||||||
}
|
}
|
||||||
|
pwRequest.method = request.method
|
||||||
const importerModules = computed(() =>
|
const itemAuth = request.auth ? request.auth : ""
|
||||||
RESTCollectionImporters.filter(
|
const authType = itemAuth ? itemAuth.type : ""
|
||||||
(i) => i.applicableTo?.includes(props.collectionsType.type) ?? true
|
if (authType === "basic") {
|
||||||
)
|
pwRequest.auth = "Basic Auth"
|
||||||
)
|
pwRequest.httpUser =
|
||||||
|
itemAuth.basic[0].key === "username"
|
||||||
const importerType = ref<number | null>(null)
|
? itemAuth.basic[0].value
|
||||||
|
: itemAuth.basic[1].value
|
||||||
const importerModule = computed(() =>
|
pwRequest.httpPassword =
|
||||||
importerType.value !== null ? importerModules.value[importerType.value] : null
|
itemAuth.basic[0].key === "password"
|
||||||
)
|
? itemAuth.basic[0].value
|
||||||
|
: itemAuth.basic[1].value
|
||||||
const importerSteps = computed(() => importerModule.value?.steps ?? null)
|
} else if (authType === "oauth2") {
|
||||||
|
pwRequest.auth = "OAuth 2.0"
|
||||||
const finishImport = async () => {
|
pwRequest.bearerToken =
|
||||||
await importerAction(stepResults.value)
|
itemAuth.oauth2[0].key === "accessToken"
|
||||||
}
|
? itemAuth.oauth2[0].value
|
||||||
|
: itemAuth.oauth2[1].value
|
||||||
const importerAction = async (stepResults: any[]) => {
|
} else if (authType === "bearer") {
|
||||||
if (!importerModule.value) return
|
pwRequest.auth = "Bearer Token"
|
||||||
const result = await importerModule.value?.importer(stepResults as any)()
|
pwRequest.bearerToken = itemAuth.bearer[0].value
|
||||||
if (E.isLeft(result)) {
|
}
|
||||||
failedImport()
|
const requestObjectHeaders = request.header
|
||||||
console.error("error", result.left)
|
if (requestObjectHeaders) {
|
||||||
} else if (E.isRight(result)) {
|
pwRequest.headers = requestObjectHeaders
|
||||||
if (props.collectionsType.type === "team-collections") {
|
for (const header of pwRequest.headers) {
|
||||||
importToTeams(result.right)
|
delete header.name
|
||||||
fileImported()
|
delete header.type
|
||||||
} else {
|
}
|
||||||
appendRESTCollections(result.right)
|
}
|
||||||
fileImported()
|
if (request.url) {
|
||||||
}
|
const requestObjectParams = request.url.query
|
||||||
}
|
if (requestObjectParams) {
|
||||||
}
|
pwRequest.params = requestObjectParams
|
||||||
|
for (const param of pwRequest.params) {
|
||||||
const hasFile = ref(false)
|
delete param.disabled
|
||||||
const hasGist = ref(false)
|
}
|
||||||
|
}
|
||||||
watch(inputChooseGistToImportFrom, (v) => {
|
}
|
||||||
stepResults.value = []
|
if (request.body) {
|
||||||
if (v === "") {
|
if (request.body.mode === "urlencoded") {
|
||||||
hasGist.value = false
|
const params = request.body.urlencoded
|
||||||
} else {
|
pwRequest.bodyParams = params || []
|
||||||
hasGist.value = true
|
for (const param of pwRequest.bodyParams) {
|
||||||
stepResults.value.push(inputChooseGistToImportFrom.value)
|
delete param.type
|
||||||
}
|
}
|
||||||
|
} else if (request.body.mode === "raw") {
|
||||||
|
pwRequest.rawInput = true
|
||||||
|
pwRequest.rawParams = request.body.raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pwRequest
|
||||||
|
},
|
||||||
|
hasFolder(item) {
|
||||||
|
return Object.prototype.hasOwnProperty.call(item, "item")
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const onFileChange = () => {
|
|
||||||
stepResults.value = []
|
|
||||||
if (!inputChooseFileToImportFrom.value[0]) {
|
|
||||||
hasFile.value = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!inputChooseFileToImportFrom.value[0].files ||
|
|
||||||
inputChooseFileToImportFrom.value[0].files.length === 0
|
|
||||||
) {
|
|
||||||
inputChooseFileToImportFrom.value[0].value = ""
|
|
||||||
hasFile.value = false
|
|
||||||
toast.show(t("action.choose_file").toString())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.onload = ({ target }) => {
|
|
||||||
const content = target!.result as string | null
|
|
||||||
if (!content) {
|
|
||||||
hasFile.value = false
|
|
||||||
toast.show(t("action.choose_file").toString())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
stepResults.value.push(content)
|
|
||||||
hasFile.value = !!content?.length
|
|
||||||
}
|
|
||||||
reader.readAsText(inputChooseFileToImportFrom.value[0].files[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
const enableImportButton = computed(
|
|
||||||
() => !(stepResults.value.length === importerSteps.value?.length)
|
|
||||||
)
|
|
||||||
|
|
||||||
const resetImport = () => {
|
|
||||||
importerType.value = null
|
|
||||||
stepResults.value = []
|
|
||||||
inputChooseFileToImportFrom.value = ""
|
|
||||||
hasFile.value = false
|
|
||||||
inputChooseGistToImportFrom.value = ""
|
|
||||||
hasGist.value = false
|
|
||||||
mySelectedCollectionID.value = undefined
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { HoppGQLRequest, makeCollection } from "@hoppscotch/data"
|
import { HoppGQLRequest } from "@hoppscotch/data"
|
||||||
import { addGraphqlCollection } from "~/newstore/collections"
|
import { addGraphqlCollection, makeCollection } from "~/newstore/collections"
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
@drop="dragging = false"
|
@drop="dragging = false"
|
||||||
@dragleave="dragging = false"
|
@dragleave="dragging = false"
|
||||||
@dragend="dragging = false"
|
@dragend="dragging = false"
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-4 items-center justify-center"
|
class="cursor-pointer flex px-4 items-center justify-center"
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
>
|
>
|
||||||
<SmartIcon
|
<SmartIcon
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
:name="getCollectionIcon"
|
:name="getCollectionIcon"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -24,9 +24,7 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
@click="toggleShowChildren()"
|
@click="toggleShowChildren()"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate"> {{ collection.name }} </span>
|
||||||
{{ collection.name }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -47,7 +45,6 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -56,54 +53,39 @@
|
|||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
svg="folder-plus"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="`${$t('folder.new')}`"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.n="folderAction.$el.click()"
|
() => {
|
||||||
@keyup.e="edit.$el.click()"
|
$emit('add-folder', {
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
path: `${collectionIndex}`,
|
||||||
@keyup.escape="options.tippy().hide()"
|
})
|
||||||
>
|
$refs.options.tippy().hide()
|
||||||
<SmartItem
|
}
|
||||||
ref="folderAction"
|
"
|
||||||
svg="folder-plus"
|
/>
|
||||||
:label="`${$t('folder.new')}`"
|
<SmartItem
|
||||||
:shortcut="['N']"
|
svg="edit"
|
||||||
@click.native="
|
:label="`${$t('action.edit')}`"
|
||||||
() => {
|
@click.native="
|
||||||
$emit('add-folder', {
|
() => {
|
||||||
path: `${collectionIndex}`,
|
$emit('edit-collection')
|
||||||
})
|
$refs.options.tippy().hide()
|
||||||
options.tippy().hide()
|
}
|
||||||
}
|
"
|
||||||
"
|
/>
|
||||||
/>
|
<SmartItem
|
||||||
<SmartItem
|
svg="trash-2"
|
||||||
ref="edit"
|
color="red"
|
||||||
svg="edit"
|
:label="`${$t('action.delete')}`"
|
||||||
:label="`${$t('action.edit')}`"
|
@click.native="
|
||||||
:shortcut="['E']"
|
() => {
|
||||||
@click.native="
|
confirmRemove = true
|
||||||
() => {
|
$refs.options.tippy().hide()
|
||||||
$emit('edit-collection')
|
}
|
||||||
options.tippy().hide()
|
"
|
||||||
}
|
/>
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="`${$t('action.delete')}`"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -157,7 +139,7 @@
|
|||||||
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.collection')}`"
|
:alt="$t('empty.collection')"
|
||||||
/>
|
/>
|
||||||
<span class="text-center">
|
<span class="text-center">
|
||||||
{{ $t("empty.collection") }}
|
{{ $t("empty.collection") }}
|
||||||
@@ -175,7 +157,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import {
|
import {
|
||||||
removeGraphqlCollection,
|
removeGraphqlCollection,
|
||||||
moveGraphqlRequest,
|
moveGraphqlRequest,
|
||||||
@@ -191,15 +173,6 @@ export default defineComponent({
|
|||||||
doc: Boolean,
|
doc: Boolean,
|
||||||
isFiltered: Boolean,
|
isFiltered: Boolean,
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
return {
|
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
folderAction: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showChildren: false,
|
showChildren: false,
|
||||||
@@ -253,8 +226,10 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
dropEvent({ dataTransfer }: any) {
|
dropEvent({ dataTransfer }: any) {
|
||||||
this.dragging = !this.dragging
|
this.dragging = !this.dragging
|
||||||
|
|
||||||
const folderPath = dataTransfer.getData("folderPath")
|
const folderPath = dataTransfer.getData("folderPath")
|
||||||
const requestIndex = dataTransfer.getData("requestIndex")
|
const requestIndex = dataTransfer.getData("requestIndex")
|
||||||
|
|
||||||
moveGraphqlRequest(folderPath, requestIndex, `${this.collectionIndex}`)
|
moveGraphqlRequest(folderPath, requestIndex, `${this.collectionIndex}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
@drop="dragging = false"
|
@drop="dragging = false"
|
||||||
@dragleave="dragging = false"
|
@dragleave="dragging = false"
|
||||||
@dragend="dragging = false"
|
@dragend="dragging = false"
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-4 items-center justify-center"
|
class="cursor-pointer flex px-4 items-center justify-center"
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
>
|
>
|
||||||
<SmartIcon
|
<SmartIcon
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
:name="getCollectionIcon"
|
:name="getCollectionIcon"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
@click="toggleShowChildren()"
|
@click="toggleShowChildren()"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate">
|
||||||
{{ folder.name ? folder.name : folder.title }}
|
{{ folder.name ? folder.name : folder.title }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -43,7 +43,6 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -52,52 +51,37 @@
|
|||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
svg="folder-plus"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="`${$t('folder.new')}`"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.n="folderAction.$el.click()"
|
() => {
|
||||||
@keyup.e="edit.$el.click()"
|
$emit('add-folder', { folder, path: folderPath })
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
$refs.options.tippy().hide()
|
||||||
@keyup.escape="options.tippy().hide()"
|
}
|
||||||
>
|
"
|
||||||
<SmartItem
|
/>
|
||||||
ref="folderAction"
|
<SmartItem
|
||||||
svg="folder-plus"
|
svg="edit"
|
||||||
:label="`${$t('folder.new')}`"
|
:label="`${$t('action.edit')}`"
|
||||||
:shortcut="['N']"
|
@click.native="
|
||||||
@click.native="
|
() => {
|
||||||
() => {
|
$emit('edit-folder', { folder, folderPath })
|
||||||
$emit('add-folder', { folder, path: folderPath })
|
$refs.options.tippy().hide()
|
||||||
options.tippy().hide()
|
}
|
||||||
}
|
"
|
||||||
"
|
/>
|
||||||
/>
|
<SmartItem
|
||||||
<SmartItem
|
svg="trash-2"
|
||||||
ref="edit"
|
color="red"
|
||||||
svg="edit"
|
:label="`${$t('action.delete')}`"
|
||||||
:label="`${$t('action.edit')}`"
|
@click.native="
|
||||||
:shortcut="['E']"
|
() => {
|
||||||
@click.native="
|
confirmRemove = true
|
||||||
() => {
|
$refs.options.tippy().hide()
|
||||||
$emit('edit-folder', { folder, folderPath })
|
}
|
||||||
options.tippy().hide()
|
"
|
||||||
}
|
/>
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="`${$t('action.delete')}`"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,7 +138,7 @@
|
|||||||
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.folder')}`"
|
:alt="$t('empty.folder')"
|
||||||
/>
|
/>
|
||||||
<span class="text-center">
|
<span class="text-center">
|
||||||
{{ $t("empty.folder") }}
|
{{ $t("empty.folder") }}
|
||||||
@@ -172,7 +156,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { removeGraphqlFolder, moveGraphqlRequest } from "~/newstore/collections"
|
import { removeGraphqlFolder, moveGraphqlRequest } from "~/newstore/collections"
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -188,15 +172,6 @@ export default defineComponent({
|
|||||||
doc: Boolean,
|
doc: Boolean,
|
||||||
isFiltered: Boolean,
|
isFiltered: Boolean,
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
return {
|
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
folderAction: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showChildren: false,
|
showChildren: false,
|
||||||
@@ -252,6 +227,7 @@ export default defineComponent({
|
|||||||
this.dragging = !this.dragging
|
this.dragging = !this.dragging
|
||||||
const folderPath = dataTransfer.getData("folderPath")
|
const folderPath = dataTransfer.getData("folderPath")
|
||||||
const requestIndex = dataTransfer.getData("requestIndex")
|
const requestIndex = dataTransfer.getData("requestIndex")
|
||||||
|
|
||||||
moveGraphqlRequest(folderPath, requestIndex, this.folderPath)
|
moveGraphqlRequest(folderPath, requestIndex, this.folderPath)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<SmartModal
|
<SmartModal
|
||||||
v-if="show"
|
v-if="show"
|
||||||
:title="`${t('modal.collections')}`"
|
:title="`${$t('modal.import_export')} ${$t('modal.collections')}`"
|
||||||
max-width="sm:max-w-md"
|
max-width="sm:max-w-md"
|
||||||
@close="hideModal"
|
@close="hideModal"
|
||||||
>
|
>
|
||||||
@@ -11,28 +11,26 @@
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.more')"
|
:title="$t('action.more')"
|
||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
icon="assignment_returned"
|
icon="assignment_returned"
|
||||||
:label="t('import.from_gist')"
|
:label="$t('import.from_gist')"
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
readCollectionGist
|
||||||
readCollectionGist()
|
$refs.options.tippy().hide()
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="
|
:title="
|
||||||
!currentUser
|
!currentUser
|
||||||
? `${t('export.require_github')}`
|
? $t('export.require_github')
|
||||||
: currentUser.provider !== 'github.com'
|
: currentUser.provider !== 'github.com'
|
||||||
? `${t('export.require_github')}`
|
? $t('export.require_github')
|
||||||
: undefined
|
: null
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
@@ -44,12 +42,10 @@
|
|||||||
: false
|
: false
|
||||||
"
|
"
|
||||||
icon="assignment_turned_in"
|
icon="assignment_turned_in"
|
||||||
:label="t('export.create_secret_gist')"
|
:label="$t('export.create_secret_gist')"
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
createCollectionGist()
|
||||||
createCollectionGist()
|
$refs.options.tippy().hide()
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -57,10 +53,26 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex flex-col space-y-2 px-2">
|
<div class="flex flex-col space-y-2">
|
||||||
<SmartItem
|
<SmartItem
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.replace_current')"
|
||||||
|
svg="file"
|
||||||
|
:label="$t('action.replace_json')"
|
||||||
|
@click.native="openDialogChooseFileToReplaceWith"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
ref="inputChooseFileToReplaceWith"
|
||||||
|
class="input"
|
||||||
|
type="file"
|
||||||
|
accept="application/json"
|
||||||
|
@change="replaceWithJSON"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.preserve_current')"
|
||||||
svg="folder-plus"
|
svg="folder-plus"
|
||||||
:label="t('import.from_json')"
|
:label="$t('import.json')"
|
||||||
@click.native="openDialogChooseFileToImportFrom"
|
@click.native="openDialogChooseFileToImportFrom"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
@@ -70,12 +82,11 @@
|
|||||||
accept="application/json"
|
accept="application/json"
|
||||||
@change="importFromJSON"
|
@change="importFromJSON"
|
||||||
/>
|
/>
|
||||||
<hr />
|
|
||||||
<SmartItem
|
<SmartItem
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.download_file')"
|
:title="$t('action.download_file')"
|
||||||
svg="download"
|
svg="download"
|
||||||
:label="t('export.as_json')"
|
:label="$t('export.as_json')"
|
||||||
@click.native="exportJSON"
|
@click.native="exportJSON"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,175 +94,295 @@
|
|||||||
</SmartModal>
|
</SmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script>
|
||||||
import { computed, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { currentUser$ } from "~/helpers/fb/auth"
|
import { currentUser$ } from "~/helpers/fb/auth"
|
||||||
import {
|
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||||
useAxios,
|
|
||||||
useI18n,
|
|
||||||
useReadonlyStream,
|
|
||||||
useToast,
|
|
||||||
} from "~/helpers/utils/composables"
|
|
||||||
import {
|
import {
|
||||||
graphqlCollections$,
|
graphqlCollections$,
|
||||||
setGraphqlCollections,
|
setGraphqlCollections,
|
||||||
appendGraphqlCollections,
|
appendGraphqlCollections,
|
||||||
} from "~/newstore/collections"
|
} from "~/newstore/collections"
|
||||||
|
|
||||||
defineProps<{
|
export default defineComponent({
|
||||||
show: boolean
|
props: {
|
||||||
}>()
|
show: Boolean,
|
||||||
|
},
|
||||||
const emit = defineEmits<{
|
setup() {
|
||||||
(e: "hide-modal"): void
|
return {
|
||||||
}>()
|
collections: useReadonlyStream(graphqlCollections$, []),
|
||||||
|
currentUser: useReadonlyStream(currentUser$, null),
|
||||||
const axios = useAxios()
|
}
|
||||||
const toast = useToast()
|
},
|
||||||
const t = useI18n()
|
computed: {
|
||||||
const collections = useReadonlyStream(graphqlCollections$, [])
|
collectionJson() {
|
||||||
const currentUser = useReadonlyStream(currentUser$, null)
|
return JSON.stringify(this.collections, null, 2)
|
||||||
|
},
|
||||||
// Template refs
|
},
|
||||||
const options = ref<any>()
|
methods: {
|
||||||
const inputChooseFileToImportFrom = ref<HTMLInputElement>()
|
async createCollectionGist() {
|
||||||
|
await this.$axios
|
||||||
const collectionJson = computed(() => {
|
.$post(
|
||||||
return JSON.stringify(collections.value, null, 2)
|
"https://api.github.com/gists",
|
||||||
})
|
{
|
||||||
|
files: {
|
||||||
const createCollectionGist = async () => {
|
"hoppscotch-collections.json": {
|
||||||
if (!currentUser.value) {
|
content: this.collectionJson,
|
||||||
toast.error(t("profile.no_permission").toString())
|
},
|
||||||
|
},
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await axios.$post(
|
|
||||||
"https://api.github.com/gists",
|
|
||||||
{
|
|
||||||
files: {
|
|
||||||
"hoppscotch-collections.json": {
|
|
||||||
content: collectionJson.value,
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
},
|
headers: {
|
||||||
{
|
Authorization: `token ${this.currentUser.accessToken}`,
|
||||||
headers: {
|
Accept: "application/vnd.github.v3+json",
|
||||||
Authorization: `token ${currentUser.value.accessToken}`,
|
},
|
||||||
Accept: "application/vnd.github.v3+json",
|
}
|
||||||
},
|
)
|
||||||
|
.then((res) => {
|
||||||
|
this.$toast.success(this.$t("export.gist_created"))
|
||||||
|
window.open(res.html_url)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
this.$toast.error(this.$t("error.something_went_wrong"))
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async readCollectionGist() {
|
||||||
|
const gist = prompt(this.$t("import.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 }) => {
|
||||||
|
const collections = JSON.parse(Object.values(files)[0].content)
|
||||||
|
setGraphqlCollections(collections)
|
||||||
|
this.fileImported()
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
this.failedImport()
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
hideModal() {
|
||||||
|
this.$emit("hide-modal")
|
||||||
|
},
|
||||||
|
openDialogChooseFileToReplaceWith() {
|
||||||
|
this.$refs.inputChooseFileToReplaceWith.click()
|
||||||
|
},
|
||||||
|
openDialogChooseFileToImportFrom() {
|
||||||
|
this.$refs.inputChooseFileToImportFrom.click()
|
||||||
|
},
|
||||||
|
replaceWithJSON() {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = ({ target }) => {
|
||||||
|
const content = target.result
|
||||||
|
let collections = JSON.parse(content)
|
||||||
|
if (collections[0]) {
|
||||||
|
const [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
|
||||||
|
}
|
||||||
|
setGraphqlCollections(collections)
|
||||||
|
this.fileImported()
|
||||||
}
|
}
|
||||||
)
|
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
|
||||||
|
this.$refs.inputChooseFileToReplaceWith.value = ""
|
||||||
toast.success(t("export.gist_created").toString())
|
},
|
||||||
window.open(res.html_url)
|
importFromJSON() {
|
||||||
} catch (e) {
|
const reader = new FileReader()
|
||||||
toast.error(t("error.something_went_wrong").toString())
|
reader.onload = ({ target }) => {
|
||||||
console.error(e)
|
const content = target.result
|
||||||
}
|
let collections = JSON.parse(content)
|
||||||
}
|
if (collections[0]) {
|
||||||
|
const [name, folders, requests] = Object.keys(collections[0])
|
||||||
const fileImported = () => {
|
if (
|
||||||
toast.success(t("state.file_imported").toString())
|
name === "name" &&
|
||||||
}
|
folders === "folders" &&
|
||||||
|
requests === "requests"
|
||||||
const failedImport = () => {
|
) {
|
||||||
toast.error(t("import.failed").toString())
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
const readCollectionGist = async () => {
|
collections.info &&
|
||||||
const gist = prompt(t("import.gist_url").toString())
|
collections.info.schema.includes("v2.1.0")
|
||||||
if (!gist) return
|
) {
|
||||||
|
// replace the variables, postman uses {{var}}, Hoppscotch uses <<var>>
|
||||||
try {
|
collections = JSON.parse(
|
||||||
const { files } = (await axios.$get(
|
content.replaceAll(/{{([a-z]+)}}/gi, "<<$1>>")
|
||||||
`https://api.github.com/gists/${gist.split("/").pop()}`,
|
)
|
||||||
{
|
collections = [this.parsePostmanCollection(collections)]
|
||||||
headers: {
|
} else {
|
||||||
Accept: "application/vnd.github.v3+json",
|
this.failedImport()
|
||||||
},
|
return
|
||||||
|
}
|
||||||
|
appendGraphqlCollections(collections)
|
||||||
|
this.fileImported()
|
||||||
}
|
}
|
||||||
)) as {
|
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
||||||
files: {
|
this.$refs.inputChooseFileToImportFrom.value = ""
|
||||||
[fileName: string]: {
|
},
|
||||||
content: any
|
exportJSON() {
|
||||||
|
const dataToWrite = this.collectionJson
|
||||||
|
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
// TODO get uri from meta
|
||||||
|
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}.json`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
this.$toast.success(this.$t("state.download_started"))
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
fileImported() {
|
||||||
|
this.$toast.success(this.$t("state.file_imported"))
|
||||||
|
},
|
||||||
|
failedImport() {
|
||||||
|
this.$toast.error(this.$t("import.failed"))
|
||||||
|
},
|
||||||
|
parsePostmanCollection({ info, name, item }) {
|
||||||
|
const hoppscotchCollection = {
|
||||||
|
name: "",
|
||||||
|
folders: [],
|
||||||
|
requests: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
|
|
||||||
|
if (item && item.length > 0) {
|
||||||
|
for (const collectionItem of item) {
|
||||||
|
if (collectionItem.request) {
|
||||||
|
if (
|
||||||
|
Object.prototype.hasOwnProperty.call(
|
||||||
|
hoppscotchCollection,
|
||||||
|
"folders"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
hoppscotchCollection.name = info ? info.name : name
|
||||||
|
hoppscotchCollection.requests.push(
|
||||||
|
this.parsePostmanRequest(collectionItem)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
hoppscotchCollection.name = name || ""
|
||||||
|
hoppscotchCollection.requests.push(
|
||||||
|
this.parsePostmanRequest(collectionItem)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (this.hasFolder(collectionItem)) {
|
||||||
|
hoppscotchCollection.folders.push(
|
||||||
|
this.parsePostmanCollection(collectionItem)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
hoppscotchCollection.requests.push(
|
||||||
|
this.parsePostmanRequest(collectionItem)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return hoppscotchCollection
|
||||||
|
},
|
||||||
const collections = JSON.parse(Object.values(files)[0].content)
|
parsePostmanRequest({ name, request }) {
|
||||||
setGraphqlCollections(collections)
|
const pwRequest = {
|
||||||
fileImported()
|
url: "",
|
||||||
} catch (e) {
|
path: "",
|
||||||
failedImport()
|
method: "",
|
||||||
console.error(e)
|
auth: "",
|
||||||
}
|
httpUser: "",
|
||||||
}
|
httpPassword: "",
|
||||||
|
passwordFieldType: "password",
|
||||||
const hideModal = () => {
|
bearerToken: "",
|
||||||
emit("hide-modal")
|
headers: [],
|
||||||
}
|
params: [],
|
||||||
|
bodyParams: [],
|
||||||
const openDialogChooseFileToImportFrom = () => {
|
rawParams: "",
|
||||||
if (inputChooseFileToImportFrom.value)
|
rawInput: false,
|
||||||
inputChooseFileToImportFrom.value.click()
|
contentType: "",
|
||||||
}
|
requestType: "",
|
||||||
|
name: "",
|
||||||
const importFromJSON = () => {
|
|
||||||
if (!inputChooseFileToImportFrom.value) return
|
|
||||||
|
|
||||||
if (
|
|
||||||
!inputChooseFileToImportFrom.value.files ||
|
|
||||||
inputChooseFileToImportFrom.value.files.length === 0
|
|
||||||
) {
|
|
||||||
toast.show(t("action.choose_file").toString())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = new FileReader()
|
|
||||||
|
|
||||||
reader.onload = ({ target }) => {
|
|
||||||
const content = target!.result as string | null
|
|
||||||
|
|
||||||
if (!content) {
|
|
||||||
toast.show(t("action.choose_file").toString())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const collections = JSON.parse(content)
|
|
||||||
if (collections[0]) {
|
|
||||||
const [name, folders, requests] = Object.keys(collections[0])
|
|
||||||
if (name === "name" && folders === "folders" && requests === "requests") {
|
|
||||||
// Do nothing
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
failedImport()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
appendGraphqlCollections(collections)
|
|
||||||
fileImported()
|
|
||||||
}
|
|
||||||
reader.readAsText(inputChooseFileToImportFrom.value.files[0])
|
|
||||||
inputChooseFileToImportFrom.value.value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
const exportJSON = () => {
|
pwRequest.name = name
|
||||||
const dataToWrite = collectionJson.value
|
const requestObjectUrl = request.url.raw.match(
|
||||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
/^(.+:\/\/[^/]+|{[^/]+})(\/[^?]+|).*$/
|
||||||
const a = document.createElement("a")
|
)
|
||||||
const url = URL.createObjectURL(file)
|
if (requestObjectUrl) {
|
||||||
a.href = url
|
pwRequest.url = requestObjectUrl[1]
|
||||||
|
pwRequest.path = requestObjectUrl[2] ? requestObjectUrl[2] : ""
|
||||||
// TODO: get uri from meta
|
}
|
||||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
pwRequest.method = request.method
|
||||||
document.body.appendChild(a)
|
const itemAuth = request.auth ? request.auth : ""
|
||||||
a.click()
|
const authType = itemAuth ? itemAuth.type : ""
|
||||||
toast.success(t("state.download_started").toString())
|
if (authType === "basic") {
|
||||||
setTimeout(() => {
|
pwRequest.auth = "Basic Auth"
|
||||||
document.body.removeChild(a)
|
pwRequest.httpUser =
|
||||||
URL.revokeObjectURL(url)
|
itemAuth.basic[0].key === "username"
|
||||||
}, 1000)
|
? 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
|
||||||
|
}
|
||||||
|
const requestObjectHeaders = request.header
|
||||||
|
if (requestObjectHeaders) {
|
||||||
|
pwRequest.headers = requestObjectHeaders
|
||||||
|
for (const header of pwRequest.headers) {
|
||||||
|
delete header.name
|
||||||
|
delete header.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const requestObjectParams = request.url.query
|
||||||
|
if (requestObjectParams) {
|
||||||
|
pwRequest.params = requestObjectParams
|
||||||
|
for (const param of pwRequest.params) {
|
||||||
|
delete param.disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (request.body) {
|
||||||
|
if (request.body.mode === "urlencoded") {
|
||||||
|
const params = request.body.urlencoded
|
||||||
|
pwRequest.bodyParams = params || []
|
||||||
|
for (const 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 Object.prototype.hasOwnProperty.call(item, "item")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
@dragover.stop
|
@dragover.stop
|
||||||
@dragleave="dragging = false"
|
@dragleave="dragging = false"
|
||||||
@dragend="dragging = false"
|
@dragend="dragging = false"
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-2 w-16 items-center justify-center truncate"
|
class="cursor-pointer flex px-2 w-16 items-center justify-center truncate"
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
>
|
>
|
||||||
<SmartIcon
|
<SmartIcon
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
:name="isSelected ? 'check-circle' : 'file'"
|
:name="isSelected ? 'check-circle' : 'file'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -23,9 +23,7 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
@click="!doc ? selectRequest() : {}"
|
@click="!doc ? selectRequest() : {}"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate"> {{ request.name }} </span>
|
||||||
{{ request.name }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -43,7 +41,6 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -52,60 +49,45 @@
|
|||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
svg="edit"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="`${$t('action.edit')}`"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.e="edit.$el.click()"
|
() => {
|
||||||
@keyup.d="duplicate.$el.click()"
|
$emit('edit-request', {
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
request,
|
||||||
@keyup.escape="options.tippy().hide()"
|
requestIndex,
|
||||||
>
|
folderPath,
|
||||||
<SmartItem
|
})
|
||||||
ref="edit"
|
$refs.options.tippy().hide()
|
||||||
svg="edit"
|
}
|
||||||
:label="`${$t('action.edit')}`"
|
"
|
||||||
:shortcut="['E']"
|
/>
|
||||||
@click.native="
|
<SmartItem
|
||||||
() => {
|
svg="copy"
|
||||||
$emit('edit-request', {
|
:label="`${$t('action.duplicate')}`"
|
||||||
request,
|
@click.native="
|
||||||
requestIndex,
|
() => {
|
||||||
folderPath,
|
$emit('duplicate-request', {
|
||||||
})
|
request,
|
||||||
options.tippy().hide()
|
requestIndex,
|
||||||
}
|
folderPath,
|
||||||
"
|
})
|
||||||
/>
|
$refs.options.tippy().hide()
|
||||||
<SmartItem
|
}
|
||||||
ref="duplicate"
|
"
|
||||||
svg="copy"
|
/>
|
||||||
:label="`${$t('action.duplicate')}`"
|
<SmartItem
|
||||||
:shortcut="['D']"
|
svg="trash-2"
|
||||||
@click.native="
|
color="red"
|
||||||
() => {
|
:label="`${$t('action.delete')}`"
|
||||||
$emit('duplicate-request', {
|
@click.native="
|
||||||
request,
|
() => {
|
||||||
requestIndex,
|
confirmRemove = true
|
||||||
folderPath,
|
$refs.options.tippy().hide()
|
||||||
})
|
}
|
||||||
options.tippy().hide()
|
"
|
||||||
}
|
/>
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="`${$t('action.delete')}`"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,7 +102,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType, ref } from "@nuxtjs/composition-api"
|
import { defineComponent, PropType } from "@nuxtjs/composition-api"
|
||||||
import { HoppGQLRequest, makeGQLRequest } from "@hoppscotch/data"
|
import { HoppGQLRequest, makeGQLRequest } from "@hoppscotch/data"
|
||||||
import { removeGraphqlRequest } from "~/newstore/collections"
|
import { removeGraphqlRequest } from "~/newstore/collections"
|
||||||
import { setGQLSession } from "~/newstore/GQLSession"
|
import { setGQLSession } from "~/newstore/GQLSession"
|
||||||
@@ -136,15 +118,6 @@ export default defineComponent({
|
|||||||
requestIndex: { type: Number, default: null },
|
requestIndex: { type: Number, default: null },
|
||||||
doc: Boolean,
|
doc: Boolean,
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
return {
|
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
duplicate: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dragging: false,
|
dragging: false,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{ 'rounded border border-divider': savingMode }">
|
<AppSection
|
||||||
|
label="collections"
|
||||||
|
:class="{ 'rounded border border-divider': savingMode }"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="divide-dividerLight divide-y border-b border-dividerLight flex flex-col top-0 z-10 sticky"
|
class="divide-dividerLight divide-y border-b border-dividerLight flex flex-col top-0 z-10 sticky"
|
||||||
:class="{ 'bg-primary': !savingMode }"
|
:class="{ 'bg-primary': !savingMode }"
|
||||||
@@ -10,7 +13,7 @@
|
|||||||
type="search"
|
type="search"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:placeholder="$t('action.search')"
|
:placeholder="$t('action.search')"
|
||||||
class="bg-transparent flex py-2 px-4"
|
class="bg-transparent flex w-full py-2 px-4"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-1 justify-between">
|
<div class="flex flex-1 justify-between">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -81,7 +84,7 @@
|
|||||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
||||||
<span class="my-2 text-center">
|
<span class="text-center">
|
||||||
{{ $t("state.nothing_found") }} "{{ filterText }}"
|
{{ $t("state.nothing_found") }} "{{ filterText }}"
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -123,12 +126,11 @@
|
|||||||
:show="showModalImportExport"
|
:show="showModalImportExport"
|
||||||
@hide-modal="displayModalImportExport(false)"
|
@hide-modal="displayModalImportExport(false)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import cloneDeep from "lodash/cloneDeep"
|
|
||||||
import clone from "lodash/clone"
|
import clone from "lodash/clone"
|
||||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{ 'rounded border border-divider': saveRequest }">
|
<AppSection
|
||||||
|
label="collections"
|
||||||
|
:class="{ 'rounded border border-divider': saveRequest }"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="divide-dividerLight divide-y bg-primary border-b border-dividerLight rounded-t flex flex-col z-10 sticky"
|
class="divide-dividerLight divide-y bg-primary border-b border-dividerLight rounded-t flex flex-col z-10 sticky"
|
||||||
:style="saveRequest ? 'top: calc(-1 * var(--font-size-body))' : 'top: 0'"
|
:style="saveRequest ? 'top: calc(-1 * var(--body-font-size))' : 'top: 0'"
|
||||||
>
|
>
|
||||||
<div v-if="!saveRequest" class="flex flex-col">
|
<div v-if="!saveRequest" class="search-wrappe">
|
||||||
<input
|
<input
|
||||||
v-model="filterText"
|
v-model="filterText"
|
||||||
type="search"
|
type="search"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:placeholder="$t('action.search')"
|
:placeholder="$t('action.search')"
|
||||||
class="bg-transparent py-2 pr-2 pl-4"
|
class="bg-transparent flex w-full py-2 pr-2 pl-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<CollectionsChooseType
|
<CollectionsChooseType
|
||||||
@@ -132,7 +135,7 @@
|
|||||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
||||||
<span class="my-2 text-center">
|
<span class="text-center">
|
||||||
{{ $t("state.nothing_found") }} "{{ filterText }}"
|
{{ $t("state.nothing_found") }} "{{ filterText }}"
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -178,7 +181,7 @@
|
|||||||
@hide-modal="displayModalImportExport(false)"
|
@hide-modal="displayModalImportExport(false)"
|
||||||
@update-team-collections="updateTeamCollections"
|
@update-team-collections="updateTeamCollections"
|
||||||
/>
|
/>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -647,26 +650,11 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
duplicateRequest({ folderPath, request, collectionID }) {
|
duplicateRequest({ folderPath, request }) {
|
||||||
if (this.collectionsType.type === "team-collections") {
|
saveRESTRequestAs(folderPath, {
|
||||||
const newReq = {
|
...cloneDeep(request),
|
||||||
...cloneDeep(request),
|
name: `${request.name} - ${this.$t("action.duplicate")}`,
|
||||||
name: `${request.name} - ${this.$t("action.duplicate")}`,
|
})
|
||||||
}
|
|
||||||
|
|
||||||
teamUtils.saveRequestAsTeams(
|
|
||||||
this.$apollo,
|
|
||||||
JSON.stringify(newReq),
|
|
||||||
`${request.name} - ${this.$t("action.duplicate")}`,
|
|
||||||
this.collectionsType.selectedTeam.id,
|
|
||||||
collectionID
|
|
||||||
)
|
|
||||||
} else if (this.collectionsType.type === "my-collections") {
|
|
||||||
saveRESTRequestAs(folderPath, {
|
|
||||||
...cloneDeep(request),
|
|
||||||
name: `${request.name} - ${this.$t("action.duplicate")}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
@drop="dragging = false"
|
@drop="dragging = false"
|
||||||
@dragleave="dragging = false"
|
@dragleave="dragging = false"
|
||||||
@dragend="dragging = false"
|
@dragend="dragging = false"
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-4 items-center justify-center"
|
class="cursor-pointer flex px-4 items-center justify-center"
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
>
|
>
|
||||||
<SmartIcon
|
<SmartIcon
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
:name="getCollectionIcon"
|
:name="getCollectionIcon"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -24,9 +24,7 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
@click="toggleShowChildren()"
|
@click="toggleShowChildren()"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate"> {{ collection.name }} </span>
|
||||||
{{ collection.name }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -65,7 +63,6 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -74,55 +71,40 @@
|
|||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
svg="folder-plus"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="$t('folder.new')"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.n="folderAction.$el.click()"
|
() => {
|
||||||
@keyup.e="edit.$el.click()"
|
$emit('add-folder', {
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
folder: collection,
|
||||||
@keyup.escape="options.tippy().hide()"
|
path: `${collectionIndex}`,
|
||||||
>
|
})
|
||||||
<SmartItem
|
$refs.options.tippy().hide()
|
||||||
ref="folderAction"
|
}
|
||||||
svg="folder-plus"
|
"
|
||||||
:label="$t('folder.new')"
|
/>
|
||||||
:shortcut="['N']"
|
<SmartItem
|
||||||
@click.native="
|
svg="edit"
|
||||||
() => {
|
:label="$t('action.edit')"
|
||||||
$emit('add-folder', {
|
@click.native="
|
||||||
folder: collection,
|
() => {
|
||||||
path: `${collectionIndex}`,
|
$emit('edit-collection')
|
||||||
})
|
$refs.options.tippy().hide()
|
||||||
options.tippy().hide()
|
}
|
||||||
}
|
"
|
||||||
"
|
/>
|
||||||
/>
|
<SmartItem
|
||||||
<SmartItem
|
svg="trash-2"
|
||||||
ref="edit"
|
color="red"
|
||||||
svg="edit"
|
:label="$t('action.delete')"
|
||||||
:label="$t('action.edit')"
|
@click.native="
|
||||||
:shortcut="['E']"
|
() => {
|
||||||
@click.native="
|
confirmRemove = true
|
||||||
() => {
|
$refs.options.tippy().hide()
|
||||||
$emit('edit-collection')
|
}
|
||||||
options.tippy().hide()
|
"
|
||||||
}
|
/>
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="$t('action.delete')"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,7 +165,7 @@
|
|||||||
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.collection')}`"
|
:alt="$t('empty.collection')"
|
||||||
/>
|
/>
|
||||||
<span class="text-center">
|
<span class="text-center">
|
||||||
{{ $t("empty.collection") }}
|
{{ $t("empty.collection") }}
|
||||||
@@ -200,8 +182,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { moveRESTRequest } from "~/newstore/collections"
|
import { moveRESTRequest } from "~/newstore/collections"
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -215,15 +197,6 @@ export default defineComponent({
|
|||||||
collectionsType: { type: Object, default: () => {} },
|
collectionsType: { type: Object, default: () => {} },
|
||||||
picked: { type: Object, default: () => {} },
|
picked: { type: Object, default: () => {} },
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
return {
|
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
folderAction: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showChildren: false,
|
showChildren: false,
|
||||||
@@ -236,7 +209,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isSelected(): boolean {
|
isSelected() {
|
||||||
return (
|
return (
|
||||||
this.picked &&
|
this.picked &&
|
||||||
this.picked.pickedType === "my-collection" &&
|
this.picked.pickedType === "my-collection" &&
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
@drop="dragging = false"
|
@drop="dragging = false"
|
||||||
@dragleave="dragging = false"
|
@dragleave="dragging = false"
|
||||||
@dragend="dragging = false"
|
@dragend="dragging = false"
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-4 items-center justify-center"
|
class="cursor-pointer flex px-4 items-center justify-center"
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
>
|
>
|
||||||
<SmartIcon
|
<SmartIcon
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
:name="getCollectionIcon"
|
:name="getCollectionIcon"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
@click="toggleShowChildren()"
|
@click="toggleShowChildren()"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate">
|
||||||
{{ folder.name ? folder.name : folder.title }}
|
{{ folder.name ? folder.name : folder.title }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
svg="folder-plus"
|
svg="folder-plus"
|
||||||
:title="t('folder.new')"
|
:title="$t('folder.new')"
|
||||||
class="hidden group-hover:inline-flex"
|
class="hidden group-hover:inline-flex"
|
||||||
@click.native="$emit('add-folder', { folder, path: folderPath })"
|
@click.native="$emit('add-folder', { folder, path: folderPath })"
|
||||||
/>
|
/>
|
||||||
@@ -43,66 +43,50 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.more')"
|
:title="$t('action.more')"
|
||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
svg="folder-plus"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="$t('folder.new')"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.n="folderAction.$el.click()"
|
() => {
|
||||||
@keyup.e="edit.$el.click()"
|
$emit('add-folder', { folder, path: folderPath })
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
$refs.options.tippy().hide()
|
||||||
@keyup.escape="options.tippy().hide()"
|
}
|
||||||
>
|
"
|
||||||
<SmartItem
|
/>
|
||||||
ref="folderAction"
|
<SmartItem
|
||||||
svg="folder-plus"
|
svg="edit"
|
||||||
:label="t('folder.new')"
|
:label="$t('action.edit')"
|
||||||
:shortcut="['N']"
|
@click.native="
|
||||||
@click.native="
|
() => {
|
||||||
() => {
|
$emit('edit-folder', {
|
||||||
$emit('add-folder', { folder, path: folderPath })
|
folder,
|
||||||
options.tippy().hide()
|
folderIndex,
|
||||||
}
|
collectionIndex,
|
||||||
"
|
folderPath,
|
||||||
/>
|
})
|
||||||
<SmartItem
|
$refs.options.tippy().hide()
|
||||||
ref="edit"
|
}
|
||||||
svg="edit"
|
"
|
||||||
:label="t('action.edit')"
|
/>
|
||||||
:shortcut="['E']"
|
<SmartItem
|
||||||
@click.native="
|
svg="trash-2"
|
||||||
() => {
|
color="red"
|
||||||
$emit('edit-folder', {
|
:label="$t('action.delete')"
|
||||||
folder,
|
@click.native="
|
||||||
folderIndex,
|
() => {
|
||||||
collectionIndex,
|
confirmRemove = true
|
||||||
folderPath,
|
$refs.options.tippy().hide()
|
||||||
})
|
}
|
||||||
options.tippy().hide()
|
"
|
||||||
}
|
/>
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="t('action.delete')"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -163,26 +147,25 @@
|
|||||||
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
||||||
:alt="`${t('empty.folder')}`"
|
:alt="$t('empty.folder')"
|
||||||
/>
|
/>
|
||||||
<span class="text-center">
|
<span class="text-center">
|
||||||
{{ t("empty.folder") }}
|
{{ $t("empty.folder") }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SmartConfirmModal
|
<SmartConfirmModal
|
||||||
:show="confirmRemove"
|
:show="confirmRemove"
|
||||||
:title="t('confirm.remove_folder')"
|
:title="$t('confirm.remove_folder')"
|
||||||
@hide-modal="confirmRemove = false"
|
@hide-modal="confirmRemove = false"
|
||||||
@resolve="removeFolder"
|
@resolve="removeFolder"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { useI18n } from "~/helpers/utils/composables"
|
|
||||||
import {
|
import {
|
||||||
removeRESTFolder,
|
removeRESTFolder,
|
||||||
removeRESTRequest,
|
removeRESTRequest,
|
||||||
@@ -202,18 +185,6 @@ export default defineComponent({
|
|||||||
collectionsType: { type: Object, default: () => {} },
|
collectionsType: { type: Object, default: () => {} },
|
||||||
picked: { type: Object, default: () => {} },
|
picked: { type: Object, default: () => {} },
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
const t = useI18n()
|
|
||||||
|
|
||||||
return {
|
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
folderAction: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
t,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showChildren: false,
|
showChildren: false,
|
||||||
@@ -224,7 +195,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isSelected(): boolean {
|
isSelected() {
|
||||||
return (
|
return (
|
||||||
this.picked &&
|
this.picked &&
|
||||||
this.picked.pickedType === "my-folder" &&
|
this.picked.pickedType === "my-folder" &&
|
||||||
@@ -262,7 +233,7 @@ export default defineComponent({
|
|||||||
this.$emit("select", { picked: null })
|
this.$emit("select", { picked: null })
|
||||||
}
|
}
|
||||||
removeRESTFolder(this.folderPath)
|
removeRESTFolder(this.folderPath)
|
||||||
this.$toast.success(`${this.$t("state.deleted")}`)
|
this.$toast.success(this.$t("state.deleted"))
|
||||||
},
|
},
|
||||||
dropEvent({ dataTransfer }) {
|
dropEvent({ dataTransfer }) {
|
||||||
this.dragging = !this.dragging
|
this.dragging = !this.dragging
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
@dragover.stop
|
@dragover.stop
|
||||||
@dragleave="dragging = false"
|
@dragleave="dragging = false"
|
||||||
@dragend="dragging = false"
|
@dragend="dragging = false"
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-2 w-16 items-center justify-center truncate"
|
class="cursor-pointer flex px-2 w-16 items-center justify-center truncate"
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<SmartIcon
|
<SmartIcon
|
||||||
v-if="isSelected"
|
v-if="isSelected"
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
name="check-circle"
|
name="check-circle"
|
||||||
/>
|
/>
|
||||||
<span v-else class="truncate">
|
<span v-else class="truncate">
|
||||||
@@ -28,9 +28,7 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition items-center group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition items-center group-hover:text-secondaryDark"
|
||||||
@click="!doc ? selectRequest() : {}"
|
@click="!doc ? selectRequest() : {}"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate"> {{ request.name }} </span>
|
||||||
{{ request.name }}
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
v-if="
|
v-if="
|
||||||
active &&
|
active &&
|
||||||
@@ -38,18 +36,8 @@
|
|||||||
active.folderPath === folderPath &&
|
active.folderPath === folderPath &&
|
||||||
active.requestIndex === requestIndex
|
active.requestIndex === requestIndex
|
||||||
"
|
"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
class="rounded-full bg-green-500 flex-shrink-0 h-1.5 mx-3 w-1.5"
|
||||||
class="relative h-1.5 w-1.5 flex flex-shrink-0 mx-3"
|
></span>
|
||||||
:title="`${$t('collection.request_in_use')}`"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="absolute animate-ping inline-flex flex-shrink-0 h-full w-full rounded-full bg-green-500 opacity-75"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="relative inline-flex flex-shrink-0 rounded-full h-1.5 w-1.5 bg-green-500"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -67,7 +55,6 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -76,66 +63,45 @@
|
|||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
svg="edit"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="$t('action.edit')"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.e="edit.$el.click()"
|
$emit('edit-request', {
|
||||||
@keyup.d="duplicate.$el.click()"
|
collectionIndex,
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
folderIndex,
|
||||||
@keyup.escape="options.tippy().hide()"
|
folderName,
|
||||||
>
|
request,
|
||||||
<SmartItem
|
requestIndex,
|
||||||
ref="edit"
|
folderPath,
|
||||||
svg="edit"
|
})
|
||||||
:label="$t('action.edit')"
|
$refs.options.tippy().hide()
|
||||||
:shortcut="['E']"
|
"
|
||||||
@click.native="
|
/>
|
||||||
() => {
|
<SmartItem
|
||||||
$emit('edit-request', {
|
svg="copy"
|
||||||
collectionIndex,
|
:label="$t('action.duplicate')"
|
||||||
folderIndex,
|
@click.native="
|
||||||
folderName,
|
$emit('duplicate-request', {
|
||||||
request,
|
collectionIndex,
|
||||||
requestIndex,
|
folderIndex,
|
||||||
folderPath,
|
folderName,
|
||||||
})
|
request,
|
||||||
options.tippy().hide()
|
requestIndex,
|
||||||
}
|
folderPath,
|
||||||
"
|
})
|
||||||
/>
|
$refs.options.tippy().hide()
|
||||||
<SmartItem
|
"
|
||||||
ref="duplicate"
|
/>
|
||||||
svg="copy"
|
<SmartItem
|
||||||
:label="$t('action.duplicate')"
|
svg="trash-2"
|
||||||
:shortcut="['D']"
|
color="red"
|
||||||
@click.native="
|
:label="$t('action.delete')"
|
||||||
() => {
|
@click.native="
|
||||||
$emit('duplicate-request', {
|
confirmRemove = true
|
||||||
collectionIndex,
|
$refs.options.tippy().hide()
|
||||||
folderIndex,
|
"
|
||||||
folderName,
|
/>
|
||||||
request,
|
|
||||||
requestIndex,
|
|
||||||
folderPath,
|
|
||||||
})
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="$t('action.delete')"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -149,15 +115,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import {
|
import { translateToNewRequest } from "@hoppscotch/data"
|
||||||
safelyExtractRESTRequest,
|
|
||||||
translateToNewRequest,
|
|
||||||
} from "@hoppscotch/data"
|
|
||||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||||
import {
|
import {
|
||||||
getDefaultRESTRequest,
|
|
||||||
restSaveContext$,
|
restSaveContext$,
|
||||||
setRESTRequest,
|
setRESTRequest,
|
||||||
setRESTSaveContext,
|
setRESTSaveContext,
|
||||||
@@ -181,11 +143,6 @@ export default defineComponent({
|
|||||||
const active = useReadonlyStream(restSaveContext$, null)
|
const active = useReadonlyStream(restSaveContext$, null)
|
||||||
return {
|
return {
|
||||||
active,
|
active,
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
duplicate: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -202,7 +159,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isSelected(): boolean {
|
isSelected() {
|
||||||
return (
|
return (
|
||||||
this.picked &&
|
this.picked &&
|
||||||
this.picked.pickedType === "my-request" &&
|
this.picked.pickedType === "my-request" &&
|
||||||
@@ -233,17 +190,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
else {
|
else {
|
||||||
setRESTRequest(
|
setRESTRequest(translateToNewRequest(this.request), {
|
||||||
safelyExtractRESTRequest(
|
originLocation: "user-collection",
|
||||||
translateToNewRequest(this.request),
|
folderPath: this.folderPath,
|
||||||
getDefaultRESTRequest()
|
requestIndex: this.requestIndex,
|
||||||
),
|
})
|
||||||
{
|
|
||||||
originLocation: "user-collection",
|
|
||||||
folderPath: this.folderPath,
|
|
||||||
requestIndex: this.requestIndex,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dragStart({ dataTransfer }) {
|
dragStart({ dataTransfer }) {
|
||||||
@@ -259,7 +210,7 @@ export default defineComponent({
|
|||||||
requestIndex: this.$props.requestIndex,
|
requestIndex: this.$props.requestIndex,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getRequestLabelColor(method: string): string {
|
getRequestLabelColor(method) {
|
||||||
return (
|
return (
|
||||||
this.requestMethodLabels[method.toLowerCase()] ||
|
this.requestMethodLabels[method.toLowerCase()] ||
|
||||||
this.requestMethodLabels.default
|
this.requestMethodLabels.default
|
||||||
|
|||||||
@@ -2,13 +2,7 @@
|
|||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div
|
<div
|
||||||
class="flex items-stretch group"
|
class="flex items-stretch group"
|
||||||
@dragover.prevent
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
@drop.prevent="dropEvent"
|
|
||||||
@dragover="dragging = true"
|
|
||||||
@drop="dragging = false"
|
|
||||||
@dragleave="dragging = false"
|
|
||||||
@dragend="dragging = false"
|
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-4 items-center justify-center"
|
class="cursor-pointer flex px-4 items-center justify-center"
|
||||||
@@ -16,7 +10,7 @@
|
|||||||
>
|
>
|
||||||
<SmartIcon
|
<SmartIcon
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
:name="getCollectionIcon"
|
:name="getCollectionIcon"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -24,15 +18,13 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
@click="toggleShowChildren()"
|
@click="toggleShowChildren()"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate"> {{ collection.title }} </span>
|
||||||
{{ collection.title }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="doc && !selected"
|
v-if="doc && !selected"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('import.title')"
|
:title="$t('import.title')"
|
||||||
svg="circle"
|
svg="circle"
|
||||||
color="green"
|
color="green"
|
||||||
@click.native="$emit('select-collection')"
|
@click.native="$emit('select-collection')"
|
||||||
@@ -40,7 +32,7 @@
|
|||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="doc && selected"
|
v-if="doc && selected"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.remove')"
|
:title="$t('action.remove')"
|
||||||
svg="check-circle"
|
svg="check-circle"
|
||||||
color="green"
|
color="green"
|
||||||
@click.native="$emit('unselect-collection')"
|
@click.native="$emit('unselect-collection')"
|
||||||
@@ -49,7 +41,7 @@
|
|||||||
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
svg="folder-plus"
|
svg="folder-plus"
|
||||||
:title="t('folder.new')"
|
:title="$t('folder.new')"
|
||||||
class="hidden group-hover:inline-flex"
|
class="hidden group-hover:inline-flex"
|
||||||
@click.native="
|
@click.native="
|
||||||
$emit('add-folder', {
|
$emit('add-folder', {
|
||||||
@@ -66,64 +58,51 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.more')"
|
:title="$t('action.more')"
|
||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
class="flex flex-col focus:outline-none"
|
svg="folder-plus"
|
||||||
tabindex="0"
|
:label="$t('folder.new')"
|
||||||
@keyup.n="folderAction.$el.click()"
|
@click.native="
|
||||||
@keyup.e="edit.$el.click()"
|
() => {
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
$emit('add-folder', {
|
||||||
@keyup.escape="options.tippy().hide()"
|
folder: collection,
|
||||||
>
|
path: `${collectionIndex}`,
|
||||||
<SmartItem
|
})
|
||||||
ref="folderAction"
|
$refs.options.tippy().hide()
|
||||||
svg="folder-plus"
|
}
|
||||||
:label="t('folder.new')"
|
"
|
||||||
:shortcut="['N']"
|
/>
|
||||||
@click.native="
|
<SmartItem
|
||||||
() => {
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
$emit('add-folder', {
|
svg="edit"
|
||||||
folder: collection,
|
:label="$t('action.edit')"
|
||||||
path: `${collectionIndex}`,
|
@click.native="
|
||||||
})
|
() => {
|
||||||
options.tippy().hide()
|
$emit('edit-collection')
|
||||||
}
|
$refs.options.tippy().hide()
|
||||||
"
|
}
|
||||||
/>
|
"
|
||||||
<SmartItem
|
/>
|
||||||
ref="edit"
|
<SmartItem
|
||||||
svg="edit"
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
:label="t('action.edit')"
|
svg="trash-2"
|
||||||
:shortcut="['E']"
|
color="red"
|
||||||
@click.native="
|
:label="$t('action.delete')"
|
||||||
() => {
|
@click.native="
|
||||||
$emit('edit-collection')
|
() => {
|
||||||
options.tippy().hide()
|
confirmRemove = true
|
||||||
}
|
$refs.options.tippy().hide()
|
||||||
"
|
}
|
||||||
/>
|
"
|
||||||
<SmartItem
|
/>
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="t('action.delete')"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,7 +131,6 @@
|
|||||||
@select="$emit('select', $event)"
|
@select="$emit('select', $event)"
|
||||||
@expand-collection="expandCollection"
|
@expand-collection="expandCollection"
|
||||||
@remove-request="removeRequest"
|
@remove-request="removeRequest"
|
||||||
@duplicate-request="$emit('duplicate-request', $event)"
|
|
||||||
/>
|
/>
|
||||||
<CollectionsTeamsRequest
|
<CollectionsTeamsRequest
|
||||||
v-for="(request, index) in collection.requests"
|
v-for="(request, index) in collection.requests"
|
||||||
@@ -164,13 +142,11 @@
|
|||||||
:request-index="request.id"
|
:request-index="request.id"
|
||||||
:doc="doc"
|
:doc="doc"
|
||||||
:save-request="saveRequest"
|
:save-request="saveRequest"
|
||||||
:collection-i-d="collection.id"
|
|
||||||
:collections-type="collectionsType"
|
:collections-type="collectionsType"
|
||||||
:picked="picked"
|
:picked="picked"
|
||||||
@edit-request="editRequest($event)"
|
@edit-request="editRequest($event)"
|
||||||
@select="$emit('select', $event)"
|
@select="$emit('select', $event)"
|
||||||
@remove-request="removeRequest"
|
@remove-request="removeRequest"
|
||||||
@duplicate-request="$emit('duplicate-request', $event)"
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
@@ -185,28 +161,25 @@
|
|||||||
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
||||||
:alt="`${t('empty.collection')}`"
|
:alt="$t('empty.collection')"
|
||||||
/>
|
/>
|
||||||
<span class="text-center">
|
<span class="text-center">
|
||||||
{{ t("empty.collection") }}
|
{{ $t("empty.collection") }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SmartConfirmModal
|
<SmartConfirmModal
|
||||||
:show="confirmRemove"
|
:show="confirmRemove"
|
||||||
:title="t('confirm.remove_collection')"
|
:title="$t('confirm.remove_collection')"
|
||||||
@hide-modal="confirmRemove = false"
|
@hide-modal="confirmRemove = false"
|
||||||
@resolve="removeCollection"
|
@resolve="removeCollection"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import * as E from "fp-ts/Either"
|
|
||||||
import { moveRESTTeamRequest } from "~/helpers/backend/mutations/TeamRequest"
|
|
||||||
import { useI18n } from "~/helpers/utils/composables"
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -219,22 +192,9 @@ export default defineComponent({
|
|||||||
collectionsType: { type: Object, default: () => {} },
|
collectionsType: { type: Object, default: () => {} },
|
||||||
picked: { type: Object, default: () => {} },
|
picked: { type: Object, default: () => {} },
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
const t = useI18n()
|
|
||||||
|
|
||||||
return {
|
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
folderAction: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
t,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showChildren: false,
|
showChildren: false,
|
||||||
dragging: false,
|
|
||||||
selectedFolder: {},
|
selectedFolder: {},
|
||||||
confirmRemove: false,
|
confirmRemove: false,
|
||||||
prevCursor: "",
|
prevCursor: "",
|
||||||
@@ -243,7 +203,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isSelected(): boolean {
|
isSelected() {
|
||||||
return (
|
return (
|
||||||
this.picked &&
|
this.picked &&
|
||||||
this.picked.pickedType === "teams-collection" &&
|
this.picked.pickedType === "teams-collection" &&
|
||||||
@@ -290,16 +250,6 @@ export default defineComponent({
|
|||||||
expandCollection(collectionID) {
|
expandCollection(collectionID) {
|
||||||
this.$emit("expand-collection", collectionID)
|
this.$emit("expand-collection", collectionID)
|
||||||
},
|
},
|
||||||
async dropEvent({ dataTransfer }) {
|
|
||||||
this.dragging = !this.dragging
|
|
||||||
const requestIndex = dataTransfer.getData("requestIndex")
|
|
||||||
const moveRequestResult = await moveRESTTeamRequest(
|
|
||||||
requestIndex,
|
|
||||||
this.collection.id
|
|
||||||
)()
|
|
||||||
if (E.isLeft(moveRequestResult))
|
|
||||||
this.$toast.error(`${this.$t("error.something_went_wrong")}`)
|
|
||||||
},
|
|
||||||
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
||||||
this.$emit("remove-request", {
|
this.$emit("remove-request", {
|
||||||
collectionIndex,
|
collectionIndex,
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
<div class="flex flex-col">
|
||||||
<div
|
<div
|
||||||
class="flex items-stretch group"
|
class="flex items-stretch group"
|
||||||
@dragover.prevent
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
@drop.prevent="dropEvent"
|
|
||||||
@dragover="dragging = true"
|
|
||||||
@drop="dragging = false"
|
|
||||||
@dragleave="dragging = false"
|
|
||||||
@dragend="dragging = false"
|
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-4 items-center justify-center"
|
class="cursor-pointer flex px-4 items-center justify-center"
|
||||||
@@ -16,7 +10,7 @@
|
|||||||
>
|
>
|
||||||
<SmartIcon
|
<SmartIcon
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
:name="getCollectionIcon"
|
:name="getCollectionIcon"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -24,7 +18,7 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
@click="toggleShowChildren()"
|
@click="toggleShowChildren()"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate">
|
||||||
{{ folder.name ? folder.name : folder.title }}
|
{{ folder.name ? folder.name : folder.title }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -45,7 +39,6 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -54,57 +47,45 @@
|
|||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
class="flex flex-col focus:outline-none"
|
svg="folder-plus"
|
||||||
tabindex="0"
|
:label="$t('folder.new')"
|
||||||
@keyup.n="folderAction.$el.click()"
|
@click.native="
|
||||||
@keyup.e="edit.$el.click()"
|
() => {
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
$emit('add-folder', { folder, path: folderPath })
|
||||||
@keyup.escape="options.tippy().hide()"
|
$refs.options.tippy().hide()
|
||||||
>
|
}
|
||||||
<SmartItem
|
"
|
||||||
ref="folderAction"
|
/>
|
||||||
svg="folder-plus"
|
<SmartItem
|
||||||
:label="$t('folder.new')"
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
:shortcut="['N']"
|
svg="edit"
|
||||||
@click.native="
|
:label="$t('action.edit')"
|
||||||
() => {
|
@click.native="
|
||||||
$emit('add-folder', { folder, path: folderPath })
|
() => {
|
||||||
options.tippy().hide()
|
$emit('edit-folder', {
|
||||||
}
|
folder,
|
||||||
"
|
folderIndex,
|
||||||
/>
|
collectionIndex,
|
||||||
<SmartItem
|
folderPath: '',
|
||||||
ref="edit"
|
})
|
||||||
svg="edit"
|
$refs.options.tippy().hide()
|
||||||
:label="$t('action.edit')"
|
}
|
||||||
:shortcut="['E']"
|
"
|
||||||
@click.native="
|
/>
|
||||||
() => {
|
<SmartItem
|
||||||
$emit('edit-folder', {
|
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
|
||||||
folder,
|
svg="trash-2"
|
||||||
folderIndex,
|
color="red"
|
||||||
collectionIndex,
|
:label="$t('action.delete')"
|
||||||
folderPath: '',
|
@click.native="
|
||||||
})
|
() => {
|
||||||
options.tippy().hide()
|
confirmRemove = true
|
||||||
}
|
$refs.options.tippy().hide()
|
||||||
"
|
}
|
||||||
/>
|
"
|
||||||
<SmartItem
|
/>
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="$t('action.delete')"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,7 +114,6 @@
|
|||||||
@select="$emit('select', $event)"
|
@select="$emit('select', $event)"
|
||||||
@expand-collection="expandCollection"
|
@expand-collection="expandCollection"
|
||||||
@remove-request="removeRequest"
|
@remove-request="removeRequest"
|
||||||
@duplicate-request="$emit('duplicate-request', $event)"
|
|
||||||
/>
|
/>
|
||||||
<CollectionsTeamsRequest
|
<CollectionsTeamsRequest
|
||||||
v-for="(request, index) in folder.requests"
|
v-for="(request, index) in folder.requests"
|
||||||
@@ -147,11 +127,9 @@
|
|||||||
:save-request="saveRequest"
|
:save-request="saveRequest"
|
||||||
:collections-type="collectionsType"
|
:collections-type="collectionsType"
|
||||||
:picked="picked"
|
:picked="picked"
|
||||||
:collection-i-d="folder.id"
|
|
||||||
@edit-request="$emit('edit-request', $event)"
|
@edit-request="$emit('edit-request', $event)"
|
||||||
@select="$emit('select', $event)"
|
@select="$emit('select', $event)"
|
||||||
@remove-request="removeRequest"
|
@remove-request="removeRequest"
|
||||||
@duplicate-request="$emit('duplicate-request', $event)"
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
@@ -164,7 +142,7 @@
|
|||||||
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
:src="`/images/states/${$colorMode.value}/pack.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.folder')}`"
|
:alt="$t('empty.folder')"
|
||||||
/>
|
/>
|
||||||
<span class="text-center">
|
<span class="text-center">
|
||||||
{{ $t("empty.folder") }}
|
{{ $t("empty.folder") }}
|
||||||
@@ -181,10 +159,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import * as E from "fp-ts/Either"
|
|
||||||
import { moveRESTTeamRequest } from "~/helpers/backend/mutations/TeamRequest"
|
|
||||||
import * as teamUtils from "~/helpers/teams/utils"
|
import * as teamUtils from "~/helpers/teams/utils"
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -200,26 +176,16 @@ export default defineComponent({
|
|||||||
collectionsType: { type: Object, default: () => {} },
|
collectionsType: { type: Object, default: () => {} },
|
||||||
picked: { type: Object, default: () => {} },
|
picked: { type: Object, default: () => {} },
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
return {
|
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
folderAction: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showChildren: false,
|
showChildren: false,
|
||||||
dragging: false,
|
|
||||||
confirmRemove: false,
|
confirmRemove: false,
|
||||||
prevCursor: "",
|
prevCursor: "",
|
||||||
cursor: "",
|
cursor: "",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isSelected(): boolean {
|
isSelected() {
|
||||||
return (
|
return (
|
||||||
this.picked &&
|
this.picked &&
|
||||||
this.picked.pickedType === "teams-folder" &&
|
this.picked.pickedType === "teams-folder" &&
|
||||||
@@ -260,11 +226,11 @@ export default defineComponent({
|
|||||||
teamUtils
|
teamUtils
|
||||||
.deleteCollection(this.$apollo, this.folder.id)
|
.deleteCollection(this.$apollo, this.folder.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$toast.success(`${this.$t("state.deleted")}`)
|
this.$toast.success(this.$t("state.deleted"))
|
||||||
this.$emit("update-team-collections")
|
this.$emit("update-team-collections")
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
this.$toast.error(`${this.$t("error.something_went_wrong")}`)
|
this.$toast.error(this.$t("error.something_went_wrong"))
|
||||||
console.error(e)
|
console.error(e)
|
||||||
})
|
})
|
||||||
this.$emit("update-team-collections")
|
this.$emit("update-team-collections")
|
||||||
@@ -273,16 +239,6 @@ export default defineComponent({
|
|||||||
expandCollection(collectionID) {
|
expandCollection(collectionID) {
|
||||||
this.$emit("expand-collection", collectionID)
|
this.$emit("expand-collection", collectionID)
|
||||||
},
|
},
|
||||||
async dropEvent({ dataTransfer }) {
|
|
||||||
this.dragging = !this.dragging
|
|
||||||
const requestIndex = dataTransfer.getData("requestIndex")
|
|
||||||
const moveRequestResult = await moveRESTTeamRequest(
|
|
||||||
requestIndex,
|
|
||||||
this.folder.id
|
|
||||||
)()
|
|
||||||
if (E.isLeft(moveRequestResult))
|
|
||||||
this.$toast.error(`${this.$t("error.something_went_wrong")}`)
|
|
||||||
},
|
|
||||||
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
removeRequest({ collectionIndex, folderName, requestIndex }) {
|
||||||
this.$emit("remove-request", {
|
this.$emit("remove-request", {
|
||||||
collectionIndex,
|
collectionIndex,
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]">
|
<div class="flex flex-col">
|
||||||
<div
|
<div
|
||||||
class="flex items-stretch group"
|
class="flex items-stretch group"
|
||||||
draggable="true"
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
@dragstart="dragStart"
|
|
||||||
@dragover.stop
|
|
||||||
@dragleave="dragging = false"
|
|
||||||
@dragend="dragging = false"
|
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="cursor-pointer flex px-2 w-16 items-center justify-center truncate"
|
class="cursor-pointer flex px-2 w-16 items-center justify-center truncate"
|
||||||
@@ -17,10 +12,10 @@
|
|||||||
<SmartIcon
|
<SmartIcon
|
||||||
v-if="isSelected"
|
v-if="isSelected"
|
||||||
class="svg-icons"
|
class="svg-icons"
|
||||||
:class="{ 'text-accent': isSelected }"
|
:class="{ 'text-green-500': isSelected }"
|
||||||
name="check-circle"
|
name="check-circle"
|
||||||
/>
|
/>
|
||||||
<span v-else class="truncate">
|
<span v-else>
|
||||||
{{ request.method }}
|
{{ request.method }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -28,27 +23,15 @@
|
|||||||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition items-center group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition items-center group-hover:text-secondaryDark"
|
||||||
@click="!doc ? selectRequest() : {}"
|
@click="!doc ? selectRequest() : {}"
|
||||||
>
|
>
|
||||||
<span class="truncate" :class="{ 'text-accent': isSelected }">
|
<span class="truncate"> {{ request.name }} </span>
|
||||||
{{ request.name }}
|
|
||||||
</span>
|
|
||||||
<span
|
<span
|
||||||
v-if="
|
v-if="
|
||||||
active &&
|
active &&
|
||||||
active.originLocation === 'team-collection' &&
|
active.originLocation === 'team-collection' &&
|
||||||
active.requestID === requestIndex
|
active.requestID === requestIndex
|
||||||
"
|
"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
class="rounded-full bg-green-500 flex-shrink-0 h-1.5 mx-3 w-1.5"
|
||||||
class="relative h-1.5 w-1.5 flex flex-shrink-0 mx-3"
|
></span>
|
||||||
:title="`${$t('collection.request_in_use')}`"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="absolute animate-ping inline-flex flex-shrink-0 h-full w-full rounded-full bg-green-500 opacity-75"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="relative inline-flex flex-shrink-0 rounded-full h-1.5 w-1.5 bg-green-500"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -67,7 +50,6 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -76,62 +58,29 @@
|
|||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
svg="edit"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="$t('action.edit')"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.e="edit.$el.click()"
|
$emit('edit-request', {
|
||||||
@keyup.d="duplicate.$el.click()"
|
collectionIndex,
|
||||||
@keyup.delete="deleteAction.$el.click()"
|
folderIndex,
|
||||||
@keyup.escape="options.tippy().hide()"
|
folderName,
|
||||||
>
|
request,
|
||||||
<SmartItem
|
requestIndex,
|
||||||
ref="edit"
|
})
|
||||||
svg="edit"
|
$refs.options.tippy().hide()
|
||||||
:label="$t('action.edit')"
|
"
|
||||||
:shortcut="['E']"
|
/>
|
||||||
@click.native="
|
<SmartItem
|
||||||
() => {
|
svg="trash-2"
|
||||||
$emit('edit-request', {
|
color="red"
|
||||||
collectionIndex,
|
:label="$t('action.delete')"
|
||||||
folderIndex,
|
@click.native="
|
||||||
folderName,
|
confirmRemove = true
|
||||||
request,
|
$refs.options.tippy().hide()
|
||||||
requestIndex,
|
"
|
||||||
})
|
/>
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="duplicate"
|
|
||||||
svg="copy"
|
|
||||||
:label="$t('action.duplicate')"
|
|
||||||
:shortcut="['D']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
$emit('duplicate-request', {
|
|
||||||
request,
|
|
||||||
requestIndex,
|
|
||||||
collectionID,
|
|
||||||
})
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="$t('action.delete')"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -146,14 +95,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import {
|
import { translateToNewRequest } from "@hoppscotch/data"
|
||||||
safelyExtractRESTRequest,
|
|
||||||
translateToNewRequest,
|
|
||||||
} from "@hoppscotch/data"
|
|
||||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||||
import {
|
import {
|
||||||
getDefaultRESTRequest,
|
|
||||||
restSaveContext$,
|
restSaveContext$,
|
||||||
setRESTRequest,
|
setRESTRequest,
|
||||||
setRESTSaveContext,
|
setRESTSaveContext,
|
||||||
@@ -171,22 +116,15 @@ export default defineComponent({
|
|||||||
saveRequest: Boolean,
|
saveRequest: Boolean,
|
||||||
collectionsType: { type: Object, default: () => {} },
|
collectionsType: { type: Object, default: () => {} },
|
||||||
picked: { type: Object, default: () => {} },
|
picked: { type: Object, default: () => {} },
|
||||||
collectionID: { type: String, default: null },
|
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const active = useReadonlyStream(restSaveContext$, null)
|
const active = useReadonlyStream(restSaveContext$, null)
|
||||||
return {
|
return {
|
||||||
active,
|
active,
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
duplicate: ref<any | null>(null),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dragging: false,
|
|
||||||
requestMethodLabels: {
|
requestMethodLabels: {
|
||||||
get: "text-green-500",
|
get: "text-green-500",
|
||||||
post: "text-yellow-500",
|
post: "text-yellow-500",
|
||||||
@@ -224,20 +162,10 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
setRESTRequest(
|
setRESTRequest(translateToNewRequest(this.request), {
|
||||||
safelyExtractRESTRequest(
|
originLocation: "team-collection",
|
||||||
translateToNewRequest(this.request),
|
requestID: this.requestIndex as string,
|
||||||
getDefaultRESTRequest()
|
})
|
||||||
),
|
|
||||||
{
|
|
||||||
originLocation: "team-collection",
|
|
||||||
requestID: this.requestIndex as string,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
dragStart({ dataTransfer }) {
|
|
||||||
this.dragging = !this.dragging
|
|
||||||
dataTransfer.setData("requestIndex", this.requestIndex)
|
|
||||||
},
|
},
|
||||||
removeRequest() {
|
removeRequest() {
|
||||||
this.$emit("remove-request", {
|
this.$emit("remove-request", {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex flex-col px-2">
|
<div class="flex flex-col px-2">
|
||||||
<div class="relative flex">
|
<div class="flex relative">
|
||||||
<input
|
<input
|
||||||
id="selectLabelEnvEdit"
|
id="selectLabelEnvEdit"
|
||||||
v-model="name"
|
v-model="name"
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
{{ $t("action.label") }}
|
{{ $t("action.label") }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between flex-1">
|
<div class="flex flex-1 items-center justify-between">
|
||||||
<label for="variableList" class="p-4">
|
<label for="variableList" class="p-4">
|
||||||
{{ $t("environment.variable_list") }}
|
{{ $t("environment.variable_list") }}
|
||||||
</label>
|
</label>
|
||||||
@@ -41,21 +41,21 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border divide-y rounded divide-dividerLight border-divider">
|
<div class="divide-dividerLight divide-y border border-divider rounded">
|
||||||
<div
|
<div
|
||||||
v-for="(variable, index) in vars"
|
v-for="(variable, index) in vars"
|
||||||
:key="`variable-${index}`"
|
:key="`variable-${index}`"
|
||||||
class="flex divide-x divide-dividerLight"
|
class="divide-dividerLight divide-x flex"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-model="variable.key"
|
v-model="variable.key"
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
:placeholder="`${$t('count.variable', { count: index + 1 })}`"
|
:placeholder="`${$t('count.variable', { count: index + 1 })}`"
|
||||||
:name="'param' + index"
|
:name="'param' + index"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
v-model="variable.value"
|
v-model="variable.value"
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
:placeholder="`${$t('count.value', { count: index + 1 })}`"
|
:placeholder="`${$t('count.value', { count: index + 1 })}`"
|
||||||
:name="'value' + index"
|
:name="'value' + index"
|
||||||
/>
|
/>
|
||||||
@@ -72,15 +72,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="vars.length === 0"
|
v-if="vars.length === 0"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/blockchain.svg`"
|
:src="`/images/states/${$colorMode.value}/blockchain.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.environments')}`"
|
:alt="$t('empty.environments')"
|
||||||
/>
|
/>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center pb-4">
|
||||||
{{ $t("empty.environments") }}
|
{{ $t("empty.environments") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex items-stretch group"
|
class="flex items-stretch group"
|
||||||
@contextmenu.prevent="options.tippy().show()"
|
@contextmenu.prevent="$refs.options.tippy().show()"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="flex items-center justify-center px-4 cursor-pointer"
|
class="cursor-pointer flex px-4 items-center justify-center"
|
||||||
@click="$emit('edit-environment')"
|
@click="$emit('edit-environment')"
|
||||||
>
|
>
|
||||||
<SmartIcon class="svg-icons" name="layers" />
|
<SmartIcon class="svg-icons" name="layers" />
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="flex flex-1 min-w-0 py-2 pr-2 transition cursor-pointer group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
@click="$emit('edit-environment')"
|
@click="$emit('edit-environment')"
|
||||||
>
|
>
|
||||||
<span class="truncate">
|
<span class="truncate">
|
||||||
@@ -18,14 +18,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<tippy
|
<tippy ref="options" interactive trigger="click" theme="popover" arrow>
|
||||||
ref="options"
|
|
||||||
interactive
|
|
||||||
trigger="click"
|
|
||||||
theme="popover"
|
|
||||||
arrow
|
|
||||||
:on-shown="() => tippyActions.focus()"
|
|
||||||
>
|
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -33,55 +26,38 @@
|
|||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="tippyActions"
|
svg="edit"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="`${$t('action.edit')}`"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.e="edit.$el.click()"
|
() => {
|
||||||
@keyup.d="duplicate.$el.click()"
|
$emit('edit-environment')
|
||||||
@keyup.delete="
|
$refs.options.tippy().hide()
|
||||||
!(environmentIndex === 'Global') ? deleteAction.$el.click() : null
|
}
|
||||||
"
|
"
|
||||||
@keyup.escape="options.tippy().hide()"
|
/>
|
||||||
>
|
<SmartItem
|
||||||
<SmartItem
|
svg="copy"
|
||||||
ref="edit"
|
:label="`${$t('action.duplicate')}`"
|
||||||
svg="edit"
|
@click.native="
|
||||||
:label="`${$t('action.edit')}`"
|
() => {
|
||||||
:shortcut="['E']"
|
duplicateEnvironment()
|
||||||
@click.native="
|
$refs.options.tippy().hide()
|
||||||
() => {
|
}
|
||||||
$emit('edit-environment')
|
"
|
||||||
options.tippy().hide()
|
/>
|
||||||
}
|
<SmartItem
|
||||||
"
|
v-if="!(environmentIndex === 'Global')"
|
||||||
/>
|
svg="trash-2"
|
||||||
<SmartItem
|
color="red"
|
||||||
ref="duplicate"
|
:label="`${$t('action.delete')}`"
|
||||||
svg="copy"
|
@click.native="
|
||||||
:label="`${$t('action.duplicate')}`"
|
() => {
|
||||||
:shortcut="['D']"
|
confirmRemove = true
|
||||||
@click.native="
|
$refs.options.tippy().hide()
|
||||||
() => {
|
}
|
||||||
duplicateEnvironment()
|
"
|
||||||
options.tippy().hide()
|
/>
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
v-if="!(environmentIndex === 'Global')"
|
|
||||||
ref="deleteAction"
|
|
||||||
svg="trash-2"
|
|
||||||
:label="`${$t('action.delete')}`"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
confirmRemove = true
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
<SmartConfirmModal
|
<SmartConfirmModal
|
||||||
@@ -94,7 +70,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType, ref } from "@nuxtjs/composition-api"
|
import { defineComponent, PropType } from "@nuxtjs/composition-api"
|
||||||
import {
|
import {
|
||||||
deleteEnvironment,
|
deleteEnvironment,
|
||||||
duplicateEnvironment,
|
duplicateEnvironment,
|
||||||
@@ -112,15 +88,6 @@ export default defineComponent({
|
|||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup() {
|
|
||||||
return {
|
|
||||||
tippyActions: ref<any | null>(null),
|
|
||||||
options: ref<any | null>(null),
|
|
||||||
edit: ref<any | null>(null),
|
|
||||||
duplicate: ref<any | null>(null),
|
|
||||||
deleteAction: ref<any | null>(null),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
confirmRemove: false,
|
confirmRemove: false,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<SmartModal
|
<SmartModal
|
||||||
v-if="show"
|
v-if="show"
|
||||||
:title="`${t('environment.title')}`"
|
:title="`${$t('modal.import_export')} ${$t('environment.title')}`"
|
||||||
max-width="sm:max-w-md"
|
max-width="sm:max-w-md"
|
||||||
@close="hideModal"
|
@close="hideModal"
|
||||||
>
|
>
|
||||||
@@ -11,28 +11,26 @@
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.more')"
|
:title="$t('action.more')"
|
||||||
svg="more-vertical"
|
svg="more-vertical"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
icon="assignment_returned"
|
icon="assignment_returned"
|
||||||
:label="t('import.from_gist')"
|
:label="$t('import.from_gist')"
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
readEnvironmentGist
|
||||||
readEnvironmentGist()
|
$refs.options.tippy().hide()
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="
|
:title="
|
||||||
!currentUser
|
!currentUser
|
||||||
? `${t('export.require_github')}`
|
? $t('export.require_github')
|
||||||
: currentUser.provider !== 'github.com'
|
: currentUser.provider !== 'github.com'
|
||||||
? `${t('export.require_github')}`
|
? $t('export.require_github')
|
||||||
: undefined
|
: null
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
@@ -44,12 +42,10 @@
|
|||||||
: false
|
: false
|
||||||
"
|
"
|
||||||
icon="assignment_turned_in"
|
icon="assignment_turned_in"
|
||||||
:label="t('export.create_secret_gist')"
|
:label="$t('export.create_secret_gist')"
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
createEnvironmentGist
|
||||||
createEnvironmentGist()
|
$refs.options.tippy().hide()
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -57,10 +53,26 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex flex-col px-2 space-y-2">
|
<div class="flex flex-col space-y-2">
|
||||||
<SmartItem
|
<SmartItem
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.replace_current')"
|
||||||
|
svg="file"
|
||||||
|
:label="$t('action.replace_json')"
|
||||||
|
@click.native="openDialogChooseFileToReplaceWith"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
ref="inputChooseFileToReplaceWith"
|
||||||
|
class="input"
|
||||||
|
type="file"
|
||||||
|
accept="application/json"
|
||||||
|
@change="replaceWithJSON"
|
||||||
|
/>
|
||||||
|
<SmartItem
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.preserve_current')"
|
||||||
svg="folder-plus"
|
svg="folder-plus"
|
||||||
:label="t('import.from_json')"
|
:label="$t('import.json')"
|
||||||
@click.native="openDialogChooseFileToImportFrom"
|
@click.native="openDialogChooseFileToImportFrom"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
@@ -70,12 +82,11 @@
|
|||||||
accept="application/json"
|
accept="application/json"
|
||||||
@change="importFromJSON"
|
@change="importFromJSON"
|
||||||
/>
|
/>
|
||||||
<hr />
|
|
||||||
<SmartItem
|
<SmartItem
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('action.download_file')"
|
:title="$t('action.download_file')"
|
||||||
svg="download"
|
svg="download"
|
||||||
:label="t('export.as_json')"
|
:label="$t('export.as_json')"
|
||||||
@click.native="exportJSON"
|
@click.native="exportJSON"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,197 +94,146 @@
|
|||||||
</SmartModal>
|
</SmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script>
|
||||||
import { computed, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { currentUser$ } from "~/helpers/fb/auth"
|
import { currentUser$ } from "~/helpers/fb/auth"
|
||||||
import {
|
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||||
useAxios,
|
|
||||||
useI18n,
|
|
||||||
useReadonlyStream,
|
|
||||||
useToast,
|
|
||||||
} from "~/helpers/utils/composables"
|
|
||||||
import {
|
import {
|
||||||
environments$,
|
environments$,
|
||||||
replaceEnvironments,
|
replaceEnvironments,
|
||||||
appendEnvironments,
|
appendEnvironments,
|
||||||
Environment,
|
|
||||||
} from "~/newstore/environments"
|
} from "~/newstore/environments"
|
||||||
|
|
||||||
defineProps<{
|
export default defineComponent({
|
||||||
show: boolean
|
props: {
|
||||||
}>()
|
show: Boolean,
|
||||||
|
},
|
||||||
const emit = defineEmits<{
|
setup() {
|
||||||
(e: "hide-modal"): void
|
return {
|
||||||
}>()
|
environments: useReadonlyStream(environments$, []),
|
||||||
|
currentUser: useReadonlyStream(currentUser$, null),
|
||||||
const axios = useAxios()
|
}
|
||||||
const toast = useToast()
|
},
|
||||||
const t = useI18n()
|
computed: {
|
||||||
const environments = useReadonlyStream(environments$, [])
|
environmentJson() {
|
||||||
const currentUser = useReadonlyStream(currentUser$, null)
|
return JSON.stringify(this.environments, null, 2)
|
||||||
|
},
|
||||||
// Template refs
|
},
|
||||||
const options = ref<any>()
|
methods: {
|
||||||
const inputChooseFileToImportFrom = ref<HTMLInputElement>()
|
async createEnvironmentGist() {
|
||||||
|
await this.$axios
|
||||||
const environmentJson = computed(() => {
|
.$post(
|
||||||
return JSON.stringify(environments.value, null, 2)
|
"https://api.github.com/gists",
|
||||||
})
|
{
|
||||||
|
files: {
|
||||||
const createEnvironmentGist = async () => {
|
"hoppscotch-environments.json": {
|
||||||
if (!currentUser.value) {
|
content: this.environmentJson,
|
||||||
toast.error(t("profile.no_permission").toString())
|
},
|
||||||
|
},
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await axios.$post(
|
|
||||||
"https://api.github.com/gists",
|
|
||||||
{
|
|
||||||
files: {
|
|
||||||
"hoppscotch-environments.json": {
|
|
||||||
content: environmentJson.value,
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
},
|
headers: {
|
||||||
{
|
Authorization: `token ${this.currentUser.accessToken}`,
|
||||||
headers: {
|
Accept: "application/vnd.github.v3+json",
|
||||||
Authorization: `token ${currentUser.value.accessToken}`,
|
},
|
||||||
Accept: "application/vnd.github.v3+json",
|
}
|
||||||
},
|
)
|
||||||
|
.then((res) => {
|
||||||
|
this.$toast.success(this.$t("export.gist_created"))
|
||||||
|
window.open(res.html_url)
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
this.$toast.error(this.$t("error.something_went_wrong"))
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async readEnvironmentGist() {
|
||||||
|
const gist = prompt(this.$t("import.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 }) => {
|
||||||
|
const environments = JSON.parse(Object.values(files)[0].content)
|
||||||
|
replaceEnvironments(environments)
|
||||||
|
this.fileImported()
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
this.failedImport()
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
hideModal() {
|
||||||
|
this.$emit("hide-modal")
|
||||||
|
},
|
||||||
|
openDialogChooseFileToReplaceWith() {
|
||||||
|
this.$refs.inputChooseFileToReplaceWith.click()
|
||||||
|
},
|
||||||
|
openDialogChooseFileToImportFrom() {
|
||||||
|
this.$refs.inputChooseFileToImportFrom.click()
|
||||||
|
},
|
||||||
|
replaceWithJSON() {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = ({ target }) => {
|
||||||
|
const content = target.result
|
||||||
|
const environments = JSON.parse(content)
|
||||||
|
replaceEnvironments(environments)
|
||||||
}
|
}
|
||||||
)
|
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0])
|
||||||
|
this.fileImported()
|
||||||
toast.success(t("export.gist_created").toString())
|
this.$refs.inputChooseFileToReplaceWith.value = ""
|
||||||
window.open(res.html_url)
|
},
|
||||||
} catch (e) {
|
importFromJSON() {
|
||||||
toast.error(t("error.something_went_wrong").toString())
|
const reader = new FileReader()
|
||||||
console.error(e)
|
reader.onload = ({ target }) => {
|
||||||
}
|
const content = target.result
|
||||||
}
|
const importFileObj = JSON.parse(content)
|
||||||
|
if (
|
||||||
const fileImported = () => {
|
importFileObj._postman_variable_scope === "environment" ||
|
||||||
toast.success(t("state.file_imported").toString())
|
importFileObj._postman_variable_scope === "globals"
|
||||||
}
|
) {
|
||||||
|
this.importFromPostman(importFileObj)
|
||||||
const failedImport = () => {
|
} else {
|
||||||
toast.error(t("import.failed").toString())
|
this.importFromHoppscotch(importFileObj)
|
||||||
}
|
|
||||||
|
|
||||||
const readEnvironmentGist = async () => {
|
|
||||||
const gist = prompt(t("import.gist_url").toString())
|
|
||||||
if (!gist) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { files } = (await axios.$get(
|
|
||||||
`https://api.github.com/gists/${gist.split("/").pop()}`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Accept: "application/vnd.github.v3+json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)) as {
|
|
||||||
files: {
|
|
||||||
[fileName: string]: {
|
|
||||||
content: any
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0])
|
||||||
const environments = JSON.parse(Object.values(files)[0].content)
|
this.$refs.inputChooseFileToImportFrom.value = ""
|
||||||
replaceEnvironments(environments)
|
},
|
||||||
fileImported()
|
importFromHoppscotch(environments) {
|
||||||
} catch (e) {
|
appendEnvironments(environments)
|
||||||
failedImport()
|
this.fileImported()
|
||||||
console.error(e)
|
},
|
||||||
}
|
importFromPostman({ name, values }) {
|
||||||
}
|
const environment = { name, variables: [] }
|
||||||
|
values.forEach(({ key, value }) =>
|
||||||
const hideModal = () => {
|
environment.variables.push({ key, value })
|
||||||
emit("hide-modal")
|
)
|
||||||
}
|
const environments = [environment]
|
||||||
|
this.importFromHoppscotch(environments)
|
||||||
const openDialogChooseFileToImportFrom = () => {
|
},
|
||||||
if (inputChooseFileToImportFrom.value)
|
exportJSON() {
|
||||||
inputChooseFileToImportFrom.value.click()
|
const dataToWrite = this.environmentJson
|
||||||
}
|
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
const importFromJSON = () => {
|
const url = URL.createObjectURL(file)
|
||||||
if (!inputChooseFileToImportFrom.value) return
|
a.href = url
|
||||||
|
// TODO get uri from meta
|
||||||
if (
|
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}.json`
|
||||||
!inputChooseFileToImportFrom.value.files ||
|
document.body.appendChild(a)
|
||||||
inputChooseFileToImportFrom.value.files.length === 0
|
a.click()
|
||||||
) {
|
this.$toast.success(this.$t("state.download_started"))
|
||||||
toast.show(t("action.choose_file").toString())
|
setTimeout(() => {
|
||||||
return
|
document.body.removeChild(a)
|
||||||
}
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
const reader = new FileReader()
|
},
|
||||||
|
fileImported() {
|
||||||
reader.onload = ({ target }) => {
|
this.$toast.success(this.$t("state.file_imported"))
|
||||||
const content = target!.result as string | null
|
},
|
||||||
|
},
|
||||||
if (!content) {
|
})
|
||||||
toast.show(t("action.choose_file").toString())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const environments = JSON.parse(content)
|
|
||||||
if (
|
|
||||||
environments._postman_variable_scope === "environment" ||
|
|
||||||
environments._postman_variable_scope === "globals"
|
|
||||||
) {
|
|
||||||
importFromPostman(environments)
|
|
||||||
} else if (environments[0]) {
|
|
||||||
const [name, variables] = Object.keys(environments[0])
|
|
||||||
if (name === "name" && variables === "variables") {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
importFromHoppscotch(environments)
|
|
||||||
} else {
|
|
||||||
failedImport()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.readAsText(inputChooseFileToImportFrom.value.files[0])
|
|
||||||
inputChooseFileToImportFrom.value.value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
const importFromHoppscotch = (environments: Environment[]) => {
|
|
||||||
appendEnvironments(environments)
|
|
||||||
fileImported()
|
|
||||||
}
|
|
||||||
|
|
||||||
const importFromPostman = ({
|
|
||||||
name,
|
|
||||||
values,
|
|
||||||
}: {
|
|
||||||
name: string
|
|
||||||
values: { key: string; value: string }[]
|
|
||||||
}) => {
|
|
||||||
const environment: Environment = { name, variables: [] }
|
|
||||||
values.forEach(({ key, value }) => environment.variables.push({ key, value }))
|
|
||||||
const environments = [environment]
|
|
||||||
importFromHoppscotch(environments)
|
|
||||||
}
|
|
||||||
|
|
||||||
const exportJSON = () => {
|
|
||||||
const dataToWrite = environmentJson.value
|
|
||||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
a.href = url
|
|
||||||
|
|
||||||
// TODO: get uri from meta
|
|
||||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
|
||||||
document.body.appendChild(a)
|
|
||||||
a.click()
|
|
||||||
toast.success(t("state.download_started").toString())
|
|
||||||
setTimeout(() => {
|
|
||||||
document.body.removeChild(a)
|
|
||||||
URL.revokeObjectURL(url)
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection :label="`${$t('environment.title')}`">
|
||||||
<div class="sticky top-0 z-10 flex flex-col rounded-t bg-primary">
|
<div class="bg-primary rounded-t flex flex-col top-0 z-10 sticky">
|
||||||
<tippy ref="options" interactive trigger="click" theme="popover" arrow>
|
<tippy ref="options" interactive trigger="click" theme="popover" arrow>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<span
|
<span
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="`${$t('environment.select')}`"
|
:title="`${$t('environment.select')}`"
|
||||||
class="flex-1 bg-transparent border-b border-dividerLight select-wrapper"
|
class="bg-transparent border-b border-dividerLight flex-1 select-wrapper"
|
||||||
>
|
>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="selectedEnvironmentIndex !== -1"
|
v-if="selectedEnvironmentIndex !== -1"
|
||||||
:label="environments[selectedEnvironmentIndex].name"
|
:label="environments[selectedEnvironmentIndex].name"
|
||||||
class="flex-1 pr-8 rounded-none"
|
class="rounded-none flex-1 pr-8"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-else
|
v-else
|
||||||
:label="`${$t('environment.no_environment')}`"
|
:label="`${$t('environment.no_environment')}`"
|
||||||
class="flex-1 pr-8 rounded-none"
|
class="rounded-none flex-1 pr-8"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</tippy>
|
</tippy>
|
||||||
<div class="flex justify-between flex-1 border-b border-dividerLight">
|
<div class="border-b border-dividerLight flex flex-1 justify-between">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
svg="plus"
|
svg="plus"
|
||||||
:label="`${$t('action.new')}`"
|
:label="`${$t('action.new')}`"
|
||||||
@@ -69,6 +69,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<EnvironmentsAdd
|
||||||
|
:show="showModalAdd"
|
||||||
|
@hide-modal="displayModalAdd(false)"
|
||||||
|
/>
|
||||||
|
<EnvironmentsEdit
|
||||||
|
:show="showModalEdit"
|
||||||
|
:editing-environment-index="editingEnvironmentIndex"
|
||||||
|
@hide-modal="displayModalEdit(false)"
|
||||||
|
/>
|
||||||
|
<EnvironmentsImportExport
|
||||||
|
:show="showModalImportExport"
|
||||||
|
@hide-modal="displayModalImportExport(false)"
|
||||||
|
/>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<EnvironmentsEnvironment
|
<EnvironmentsEnvironment
|
||||||
environment-index="Global"
|
environment-index="Global"
|
||||||
@@ -86,15 +99,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="environments.length === 0"
|
v-if="environments.length === 0"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/blockchain.svg`"
|
:src="`/images/states/${$colorMode.value}/blockchain.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.environments')}`"
|
:alt="$t('empty.environments')"
|
||||||
/>
|
/>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center pb-4">
|
||||||
{{ $t("empty.environments") }}
|
{{ $t("empty.environments") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -104,20 +117,7 @@
|
|||||||
@click.native="displayModalAdd(true)"
|
@click.native="displayModalAdd(true)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<EnvironmentsAdd
|
</AppSection>
|
||||||
:show="showModalAdd"
|
|
||||||
@hide-modal="displayModalAdd(false)"
|
|
||||||
/>
|
|
||||||
<EnvironmentsEdit
|
|
||||||
:show="showModalEdit"
|
|
||||||
:editing-environment-index="editingEnvironmentIndex"
|
|
||||||
@hide-modal="displayModalEdit(false)"
|
|
||||||
/>
|
|
||||||
<EnvironmentsImportExport
|
|
||||||
:show="showModalImportExport"
|
|
||||||
@hide-modal="displayModalImportExport(false)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
@close="hideModal"
|
@close="hideModal"
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div v-if="mode === 'sign-in'" class="flex flex-col px-2 space-y-2">
|
<div v-if="mode === 'sign-in'" class="flex flex-col space-y-2 px-2">
|
||||||
<SmartItem
|
<SmartItem
|
||||||
:loading="signingInWithGitHub"
|
:loading="signingInWithGitHub"
|
||||||
svg="auth/github"
|
svg="auth/github"
|
||||||
@@ -56,8 +56,8 @@
|
|||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
<div v-if="mode === 'email-sent'" class="flex flex-col px-4">
|
<div v-if="mode === 'email-sent'" class="flex flex-col px-4">
|
||||||
<div class="flex flex-col items-center justify-center max-w-md">
|
<div class="flex flex-col max-w-md items-center justify-center">
|
||||||
<SmartIcon class="w-6 h-6 text-accent" name="inbox" />
|
<SmartIcon class="h-6 text-accent w-6" name="inbox" />
|
||||||
<h3 class="my-2 text-lg text-center">
|
<h3 class="my-2 text-lg text-center">
|
||||||
{{ $t("auth.we_sent_magic_link") }}
|
{{ $t("auth.we_sent_magic_link") }}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
v-if="mode === 'email-sent'"
|
v-if="mode === 'email-sent'"
|
||||||
class="flex justify-between flex-1 text-secondaryLight"
|
class="flex flex-1 text-secondaryLight justify-between"
|
||||||
>
|
>
|
||||||
<SmartAnchor
|
<SmartAnchor
|
||||||
class="link"
|
class="link"
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex" @click="$refs.logout.$el.click()">
|
<div class="flex">
|
||||||
<SmartItem
|
<SmartItem
|
||||||
ref="logout"
|
|
||||||
svg="log-out"
|
svg="log-out"
|
||||||
:label="`${$t('auth.logout')}`"
|
:label="`${$t('auth.logout')}`"
|
||||||
:outline="outline"
|
:outline="outline"
|
||||||
:shortcut="shortcut"
|
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
() => {
|
||||||
$emit('confirm-logout')
|
$emit('confirm-logout')
|
||||||
@@ -32,10 +30,6 @@ export default defineComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
shortcut: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -21,19 +21,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="gqlField.description"
|
v-if="gqlField.description"
|
||||||
class="py-2 text-secondaryLight field-desc"
|
class="text-secondaryLight py-2 field-desc"
|
||||||
>
|
>
|
||||||
{{ gqlField.description }}
|
{{ gqlField.description }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="gqlField.isDeprecated"
|
v-if="gqlField.isDeprecated"
|
||||||
class="inline-block px-2 py-1 my-1 text-black bg-yellow-200 rounded field-deprecated"
|
class="rounded bg-yellow-200 my-1 text-black py-1 px-2 inline-block field-deprecated"
|
||||||
>
|
>
|
||||||
{{ $t("state.deprecated") }}
|
{{ $t("state.deprecated") }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="fieldArgs.length > 0">
|
<div v-if="fieldArgs.length > 0">
|
||||||
<h5 class="my-2">Arguments:</h5>
|
<h5 class="my-2">Arguments:</h5>
|
||||||
<div class="pl-4 border-l-2 border-divider">
|
<div class="border-divider border-l-2 pl-4">
|
||||||
<div v-for="(field, index) in fieldArgs" :key="`field-${index}`">
|
<div v-for="(field, index) in fieldArgs" :key="`field-${index}`">
|
||||||
<span>
|
<span>
|
||||||
{{ field.name }}:
|
{{ field.name }}:
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
v-if="field.description"
|
v-if="field.description"
|
||||||
class="py-2 text-secondaryLight field-desc"
|
class="text-secondaryLight py-2 field-desc"
|
||||||
>
|
>
|
||||||
{{ field.description }}
|
{{ field.description }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sticky top-0 z-10 flex p-4 bg-primary">
|
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||||
<div class="inline-flex flex-1 space-x-2">
|
<div class="space-x-2 flex-1 inline-flex">
|
||||||
<input
|
<input
|
||||||
id="url"
|
id="url"
|
||||||
v-model="url"
|
v-model="url"
|
||||||
type="url"
|
type="url"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
class="w-full px-4 py-2 border rounded bg-primaryLight border-divider text-secondaryDark hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
class="bg-primaryLight border border-divider rounded text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
:placeholder="`${t('request.url')}`"
|
:placeholder="`${t('request.url')}`"
|
||||||
:disabled="connected"
|
:disabled="connected"
|
||||||
@keyup.enter="onConnectClick"
|
@keyup.enter="onConnectClick"
|
||||||
|
|||||||
@@ -1,168 +1,133 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10">
|
<SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10">
|
||||||
<SmartTab
|
<SmartTab :id="'query'" :label="`${t('tab.query')}`" :selected="true">
|
||||||
:id="'query'"
|
<AppSection label="query">
|
||||||
:label="`${t('tab.query')}`"
|
|
||||||
:selected="true"
|
|
||||||
:indicator="gqlQueryString && gqlQueryString.length > 0 ? true : false"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold gqlRunQuery"
|
|
||||||
>
|
|
||||||
<label class="font-semibold text-secondaryLight">
|
|
||||||
{{ t("request.query") }}
|
|
||||||
</label>
|
|
||||||
<div class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip', delay: [500, 20] }"
|
|
||||||
:title="`${t(
|
|
||||||
'request.run'
|
|
||||||
)} <kbd>${getSpecialKey()}</kbd><kbd>G</kbd>`"
|
|
||||||
:label="`${t('request.run')}`"
|
|
||||||
svg="play"
|
|
||||||
class="rounded-none !text-accent !hover:text-accentDark"
|
|
||||||
@click.native="runQuery()"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
ref="saveRequest"
|
|
||||||
v-tippy="{ theme: 'tooltip', delay: [500, 20] }"
|
|
||||||
:title="`${t(
|
|
||||||
'request.save'
|
|
||||||
)} <kbd>${getSpecialKey()}</kbd><kbd>S</kbd>`"
|
|
||||||
:label="`${t('request.save')}`"
|
|
||||||
svg="save"
|
|
||||||
class="rounded-none"
|
|
||||||
@click.native="saveRequest"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
to="https://docs.hoppscotch.io/graphql"
|
|
||||||
blank
|
|
||||||
:title="t('app.wiki')"
|
|
||||||
svg="help-circle"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.clear_all')"
|
|
||||||
svg="trash-2"
|
|
||||||
@click.native="clearGQLQuery()"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.prettify')"
|
|
||||||
:svg="`${prettifyQueryIcon}`"
|
|
||||||
@click.native="prettifyQuery"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.copy')"
|
|
||||||
:svg="`${copyQueryIcon}`"
|
|
||||||
@click.native="copyQuery"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div ref="queryEditor"></div>
|
|
||||||
</SmartTab>
|
|
||||||
|
|
||||||
<SmartTab
|
|
||||||
:id="'variables'"
|
|
||||||
:label="`${t('tab.variables')}`"
|
|
||||||
:indicator="variableString && variableString.length > 0 ? true : false"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
|
||||||
>
|
|
||||||
<label class="font-semibold text-secondaryLight">
|
|
||||||
{{ t("request.variables") }}
|
|
||||||
</label>
|
|
||||||
<div class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
to="https://docs.hoppscotch.io/graphql"
|
|
||||||
blank
|
|
||||||
:title="t('app.wiki')"
|
|
||||||
svg="help-circle"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.clear_all')"
|
|
||||||
svg="trash-2"
|
|
||||||
@click.native="clearGQLVariables()"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
ref="prettifyRequest"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.prettify')"
|
|
||||||
:svg="prettifyVariablesIcon"
|
|
||||||
@click.native="prettifyVariableString"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.copy')"
|
|
||||||
:svg="`${copyVariablesIcon}`"
|
|
||||||
@click.native="copyVariables"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div ref="variableEditor"></div>
|
|
||||||
</SmartTab>
|
|
||||||
|
|
||||||
<SmartTab
|
|
||||||
:id="'headers'"
|
|
||||||
:label="`${t('tab.headers')}`"
|
|
||||||
:info="activeGQLHeadersCount === 0 ? null : `${activeGQLHeadersCount}`"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
|
||||||
>
|
|
||||||
<label class="font-semibold text-secondaryLight">
|
|
||||||
{{ t("tab.headers") }}
|
|
||||||
</label>
|
|
||||||
<div class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
to="https://docs.hoppscotch.io/graphql"
|
|
||||||
blank
|
|
||||||
:title="t('app.wiki')"
|
|
||||||
svg="help-circle"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.clear_all')"
|
|
||||||
svg="trash-2"
|
|
||||||
@click.native="clearContent()"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('state.bulk_mode')"
|
|
||||||
svg="edit"
|
|
||||||
:class="{ '!text-accent': bulkMode }"
|
|
||||||
@click.native="bulkMode = !bulkMode"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('add.new')"
|
|
||||||
svg="plus"
|
|
||||||
:disabled="bulkMode"
|
|
||||||
@click.native="addHeader"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="bulkMode" ref="bulkEditor"></div>
|
|
||||||
<div v-else>
|
|
||||||
<div
|
<div
|
||||||
v-for="(header, index) in workingHeaders"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between gqlRunQuery"
|
||||||
:key="`header-${String(index)}`"
|
|
||||||
class="flex border-b divide-x divide-dividerLight border-dividerLight"
|
|
||||||
>
|
>
|
||||||
<SmartAutoComplete
|
<label class="font-semibold text-secondaryLight">
|
||||||
:placeholder="`${t('count.header', { count: index + 1 })}`"
|
{{ t("request.query") }}
|
||||||
:source="commonHeaders"
|
</label>
|
||||||
:spellcheck="false"
|
<div class="flex">
|
||||||
:value="header.key"
|
<ButtonSecondary
|
||||||
autofocus
|
:label="`${t('request.run')}`"
|
||||||
styles="
|
svg="play"
|
||||||
|
class="rounded-none !text-accent !hover:text-accentDark"
|
||||||
|
@click.native="runQuery()"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
ref="saveRequest"
|
||||||
|
:label="`${t('request.save')}`"
|
||||||
|
svg="save"
|
||||||
|
class="rounded-none"
|
||||||
|
@click.native="saveRequest"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
to="https://docs.hoppscotch.io/graphql"
|
||||||
|
blank
|
||||||
|
:title="t('app.wiki')"
|
||||||
|
svg="help-circle"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.prettify')"
|
||||||
|
:svg="`${prettifyQueryIcon}`"
|
||||||
|
@click.native="prettifyQuery"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.copy')"
|
||||||
|
:svg="`${copyQueryIcon}`"
|
||||||
|
@click.native="copyQuery"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="queryEditor"></div>
|
||||||
|
</AppSection>
|
||||||
|
</SmartTab>
|
||||||
|
|
||||||
|
<SmartTab :id="'variables'" :label="`${t('tab.variables')}`">
|
||||||
|
<AppSection label="variables">
|
||||||
|
<div
|
||||||
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
|
>
|
||||||
|
<label class="font-semibold text-secondaryLight">
|
||||||
|
{{ t("request.variables") }}
|
||||||
|
</label>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
to="https://docs.hoppscotch.io/graphql"
|
||||||
|
blank
|
||||||
|
:title="t('app.wiki')"
|
||||||
|
svg="help-circle"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.copy')"
|
||||||
|
:svg="`${copyVariablesIcon}`"
|
||||||
|
@click.native="copyVariables"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="variableEditor"></div>
|
||||||
|
</AppSection>
|
||||||
|
</SmartTab>
|
||||||
|
|
||||||
|
<SmartTab :id="'headers'" :label="`${t('tab.headers')}`">
|
||||||
|
<AppSection label="headers">
|
||||||
|
<div
|
||||||
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
|
>
|
||||||
|
<label class="font-semibold text-secondaryLight">
|
||||||
|
{{ t("tab.headers") }}
|
||||||
|
</label>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
to="https://docs.hoppscotch.io/graphql"
|
||||||
|
blank
|
||||||
|
:title="t('app.wiki')"
|
||||||
|
svg="help-circle"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.clear_all')"
|
||||||
|
svg="trash-2"
|
||||||
|
@click.native="clearContent()"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('state.bulk_mode')"
|
||||||
|
svg="edit"
|
||||||
|
:class="{ '!text-accent': bulkMode }"
|
||||||
|
@click.native="bulkMode = !bulkMode"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('add.new')"
|
||||||
|
svg="plus"
|
||||||
|
:disabled="bulkMode"
|
||||||
|
@click.native="addRequestHeader"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="bulkMode" ref="bulkEditor"></div>
|
||||||
|
<div v-else>
|
||||||
|
<div
|
||||||
|
v-for="(header, index) in headers"
|
||||||
|
:key="`header-${String(index)}`"
|
||||||
|
class="divide-dividerLight divide-x border-b border-dividerLight flex"
|
||||||
|
>
|
||||||
|
<SmartAutoComplete
|
||||||
|
:placeholder="`${t('count.header', { count: index + 1 })}`"
|
||||||
|
:source="commonHeaders"
|
||||||
|
:spellcheck="false"
|
||||||
|
:value="header.key"
|
||||||
|
autofocus
|
||||||
|
styles="
|
||||||
bg-transparent
|
bg-transparent
|
||||||
flex
|
flex
|
||||||
flex-1
|
flex-1
|
||||||
@@ -170,90 +135,92 @@
|
|||||||
px-4
|
px-4
|
||||||
truncate
|
truncate
|
||||||
"
|
"
|
||||||
class="flex-1 !flex"
|
class="flex-1 !flex"
|
||||||
@input="
|
@input="
|
||||||
updateHeader(index, {
|
updateRequestHeader(index, {
|
||||||
key: $event,
|
key: $event,
|
||||||
value: header.value,
|
|
||||||
active: header.active,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
|
||||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
|
||||||
:name="`value ${String(index)}`"
|
|
||||||
:value="header.value"
|
|
||||||
autofocus
|
|
||||||
@change="
|
|
||||||
updateHeader(index, {
|
|
||||||
key: header.key,
|
|
||||||
value: $event.target.value,
|
|
||||||
active: header.active,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="
|
|
||||||
header.hasOwnProperty('active')
|
|
||||||
? header.active
|
|
||||||
? t('action.turn_off')
|
|
||||||
: t('action.turn_on')
|
|
||||||
: t('action.turn_off')
|
|
||||||
"
|
|
||||||
:svg="
|
|
||||||
header.hasOwnProperty('active')
|
|
||||||
? header.active
|
|
||||||
? 'check-circle'
|
|
||||||
: 'circle'
|
|
||||||
: 'check-circle'
|
|
||||||
"
|
|
||||||
color="green"
|
|
||||||
@click.native="
|
|
||||||
updateHeader(index, {
|
|
||||||
key: header.key,
|
|
||||||
value: header.value,
|
value: header.value,
|
||||||
active: !header.active,
|
active: header.active,
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</span>
|
<input
|
||||||
<span>
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
<ButtonSecondary
|
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
:name="`value ${String(index)}`"
|
||||||
:title="t('action.remove')"
|
:value="header.value"
|
||||||
svg="trash"
|
autofocus
|
||||||
color="red"
|
@change="
|
||||||
@click.native="deleteHeader(index)"
|
updateRequestHeader(index, {
|
||||||
|
key: header.key,
|
||||||
|
value: $event.target.value,
|
||||||
|
active: header.active,
|
||||||
|
})
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</span>
|
<span>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="
|
||||||
|
header.hasOwnProperty('active')
|
||||||
|
? header.active
|
||||||
|
? t('action.turn_off')
|
||||||
|
: t('action.turn_on')
|
||||||
|
: t('action.turn_off')
|
||||||
|
"
|
||||||
|
:svg="
|
||||||
|
header.hasOwnProperty('active')
|
||||||
|
? header.active
|
||||||
|
? 'check-circle'
|
||||||
|
: 'circle'
|
||||||
|
: 'check-circle'
|
||||||
|
"
|
||||||
|
color="green"
|
||||||
|
@click.native="
|
||||||
|
updateRequestHeader(index, {
|
||||||
|
key: header.key,
|
||||||
|
value: header.value,
|
||||||
|
active: !header.active,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.remove')"
|
||||||
|
svg="trash"
|
||||||
|
color="red"
|
||||||
|
@click.native="removeRequestHeader(index)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="headers.length === 0"
|
||||||
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="`/images/states/${$colorMode.value}/add_category.svg`"
|
||||||
|
loading="lazy"
|
||||||
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
|
:alt="`${t('empty.headers')}`"
|
||||||
|
/>
|
||||||
|
<span class="text-center pb-4">
|
||||||
|
{{ t("empty.headers") }}
|
||||||
|
</span>
|
||||||
|
<ButtonSecondary
|
||||||
|
:label="`${t('add.new')}`"
|
||||||
|
filled
|
||||||
|
svg="plus"
|
||||||
|
class="mb-4"
|
||||||
|
@click.native="addRequestHeader"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
</AppSection>
|
||||||
v-if="workingHeaders.length === 0"
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${$colorMode.value}/add_category.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
|
||||||
:alt="`${t('empty.headers')}`"
|
|
||||||
/>
|
|
||||||
<span class="pb-4 text-center">
|
|
||||||
{{ t("empty.headers") }}
|
|
||||||
</span>
|
|
||||||
<ButtonSecondary
|
|
||||||
:label="`${t('add.new')}`"
|
|
||||||
filled
|
|
||||||
svg="plus"
|
|
||||||
class="mb-4"
|
|
||||||
@click.native="addHeader"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SmartTab>
|
</SmartTab>
|
||||||
</SmartTabs>
|
</SmartTabs>
|
||||||
|
|
||||||
<CollectionsSaveRequest
|
<CollectionsSaveRequest
|
||||||
mode="graphql"
|
mode="graphql"
|
||||||
:show="showSaveRequestModal"
|
:show="showSaveRequestModal"
|
||||||
@@ -263,11 +230,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Ref, computed, reactive, ref, watch } from "@nuxtjs/composition-api"
|
import { onMounted, ref, watch } from "@nuxtjs/composition-api"
|
||||||
import clone from "lodash/clone"
|
import clone from "lodash/clone"
|
||||||
import * as gql from "graphql"
|
import * as gql from "graphql"
|
||||||
import { GQLHeader, makeGQLRequest } from "@hoppscotch/data"
|
import { GQLHeader, makeGQLRequest } from "@hoppscotch/data"
|
||||||
import isEqual from "lodash/isEqual"
|
|
||||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import {
|
import {
|
||||||
useNuxt,
|
useNuxt,
|
||||||
@@ -277,15 +243,18 @@ import {
|
|||||||
useToast,
|
useToast,
|
||||||
} from "~/helpers/utils/composables"
|
} from "~/helpers/utils/composables"
|
||||||
import {
|
import {
|
||||||
|
addGQLHeader,
|
||||||
gqlHeaders$,
|
gqlHeaders$,
|
||||||
gqlQuery$,
|
gqlQuery$,
|
||||||
gqlResponse$,
|
gqlResponse$,
|
||||||
gqlURL$,
|
gqlURL$,
|
||||||
gqlVariables$,
|
gqlVariables$,
|
||||||
|
removeGQLHeader,
|
||||||
setGQLHeaders,
|
setGQLHeaders,
|
||||||
setGQLQuery,
|
setGQLQuery,
|
||||||
setGQLResponse,
|
setGQLResponse,
|
||||||
setGQLVariables,
|
setGQLVariables,
|
||||||
|
updateGQLHeader,
|
||||||
} from "~/newstore/GQLSession"
|
} from "~/newstore/GQLSession"
|
||||||
import { commonHeaders } from "~/helpers/headers"
|
import { commonHeaders } from "~/helpers/headers"
|
||||||
import { GQLConnection } from "~/helpers/GQLConnection"
|
import { GQLConnection } from "~/helpers/GQLConnection"
|
||||||
@@ -296,8 +265,6 @@ import { useCodemirror } from "~/helpers/editor/codemirror"
|
|||||||
import jsonLinter from "~/helpers/editor/linting/json"
|
import jsonLinter from "~/helpers/editor/linting/json"
|
||||||
import { createGQLQueryLinter } from "~/helpers/editor/linting/gqlQuery"
|
import { createGQLQueryLinter } from "~/helpers/editor/linting/gqlQuery"
|
||||||
import queryCompleter from "~/helpers/editor/completion/gqlQuery"
|
import queryCompleter from "~/helpers/editor/completion/gqlQuery"
|
||||||
import { defineActionHandler } from "~/helpers/actions"
|
|
||||||
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -306,18 +273,33 @@ const props = defineProps<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
|
|
||||||
|
const bulkMode = ref(false)
|
||||||
|
const bulkHeaders = ref("")
|
||||||
|
|
||||||
|
watch(bulkHeaders, () => {
|
||||||
|
try {
|
||||||
|
const transformation = bulkHeaders.value.split("\n").map((item) => ({
|
||||||
|
key: item.substring(0, item.indexOf(":")).trim().replace(/^\/\//, ""),
|
||||||
|
value: item.substring(item.indexOf(":") + 1).trim(),
|
||||||
|
active: !item.trim().startsWith("//"),
|
||||||
|
}))
|
||||||
|
setGQLHeaders(transformation as GQLHeader[])
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(`${t("error.something_went_wrong")}`)
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const url = useReadonlyStream(gqlURL$, "")
|
const url = useReadonlyStream(gqlURL$, "")
|
||||||
const gqlQueryString = useStream(gqlQuery$, "", setGQLQuery)
|
const gqlQueryString = useStream(gqlQuery$, "", setGQLQuery)
|
||||||
const variableString = useStream(gqlVariables$, "", setGQLVariables)
|
const variableString = useStream(gqlVariables$, "", setGQLVariables)
|
||||||
|
const headers = useStream(gqlHeaders$, [], setGQLHeaders)
|
||||||
|
|
||||||
const bulkMode = ref(false)
|
|
||||||
const bulkHeaders = ref("")
|
|
||||||
const bulkEditor = ref<any | null>(null)
|
const bulkEditor = ref<any | null>(null)
|
||||||
|
|
||||||
const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
|
|
||||||
|
|
||||||
useCodemirror(bulkEditor, bulkHeaders, {
|
useCodemirror(bulkEditor, bulkHeaders, {
|
||||||
extendedEditorConfig: {
|
extendedEditorConfig: {
|
||||||
mode: "text/x-yaml",
|
mode: "text/x-yaml",
|
||||||
@@ -325,192 +307,18 @@ useCodemirror(bulkEditor, bulkHeaders, {
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// The functional headers list (the headers actually in the system)
|
|
||||||
const headers = useStream(gqlHeaders$, [], setGQLHeaders) as Ref<GQLHeader[]>
|
|
||||||
|
|
||||||
// The UI representation of the headers list (has the empty end header)
|
|
||||||
const workingHeaders = ref<GQLHeader[]>([
|
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// Rule: Working Headers always have one empty header or the last element is always an empty header
|
|
||||||
watch(workingHeaders, (headersList) => {
|
|
||||||
if (
|
|
||||||
headersList.length > 0 &&
|
|
||||||
headersList[headersList.length - 1].key !== ""
|
|
||||||
) {
|
|
||||||
workingHeaders.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sync logic between headers and working headers
|
|
||||||
watch(
|
|
||||||
headers,
|
|
||||||
(newHeadersList) => {
|
|
||||||
// Sync should overwrite working headers
|
|
||||||
const filteredWorkingHeaders = workingHeaders.value.filter(
|
|
||||||
(e) => e.key !== ""
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!isEqual(newHeadersList, filteredWorkingHeaders)) {
|
|
||||||
workingHeaders.value = newHeadersList
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(workingHeaders, (newWorkingHeaders) => {
|
|
||||||
const fixedHeaders = newWorkingHeaders.filter((e) => e.key !== "")
|
|
||||||
if (!isEqual(headers.value, fixedHeaders)) {
|
|
||||||
headers.value = fixedHeaders
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Bulk Editor Syncing with Working Headers
|
|
||||||
watch(bulkHeaders, () => {
|
|
||||||
try {
|
|
||||||
const transformation = bulkHeaders.value
|
|
||||||
.split("\n")
|
|
||||||
.filter((x) => x.trim().length > 0 && x.includes(":"))
|
|
||||||
.map((item) => ({
|
|
||||||
key: item.substring(0, item.indexOf(":")).trimLeft().replace(/^#/, ""),
|
|
||||||
value: item.substring(item.indexOf(":") + 1).trimLeft(),
|
|
||||||
active: !item.trim().startsWith("#"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const filteredHeaders = workingHeaders.value.filter((x) => x.key !== "")
|
|
||||||
|
|
||||||
if (!isEqual(filteredHeaders, transformation)) {
|
|
||||||
workingHeaders.value = transformation
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`${t("error.something_went_wrong")}`)
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(workingHeaders, (newHeadersList) => {
|
|
||||||
// If we are in bulk mode, don't apply direct changes
|
|
||||||
if (bulkMode.value) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentBulkHeaders = bulkHeaders.value.split("\n").map((item) => ({
|
|
||||||
key: item.substring(0, item.indexOf(":")).trimLeft().replace(/^#/, ""),
|
|
||||||
value: item.substring(item.indexOf(":") + 1).trimLeft(),
|
|
||||||
active: !item.trim().startsWith("#"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const filteredHeaders = newHeadersList.filter((x) => x.key !== "")
|
|
||||||
|
|
||||||
if (!isEqual(currentBulkHeaders, filteredHeaders)) {
|
|
||||||
bulkHeaders.value = filteredHeaders
|
|
||||||
.map((header) => {
|
|
||||||
return `${header.active ? "" : "#"}${header.key}: ${header.value}`
|
|
||||||
})
|
|
||||||
.join("\n")
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`${t("error.something_went_wrong")}`)
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const addHeader = () => {
|
|
||||||
workingHeaders.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateHeader = (index: number, header: GQLHeader) => {
|
|
||||||
workingHeaders.value = workingHeaders.value.map((h, i) =>
|
|
||||||
i === index ? header : h
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteHeader = (index: number) => {
|
|
||||||
const headersBeforeDeletion = clone(workingHeaders.value)
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
headersBeforeDeletion.length > 0 &&
|
|
||||||
index === headersBeforeDeletion.length - 1
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (deletionToast.value) {
|
|
||||||
deletionToast.value.goAway(0)
|
|
||||||
deletionToast.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
deletionToast.value = toast.success(`${t("state.deleted")}`, {
|
|
||||||
action: [
|
|
||||||
{
|
|
||||||
text: `${t("action.undo")}`,
|
|
||||||
onClick: (_, toastObject) => {
|
|
||||||
workingHeaders.value = headersBeforeDeletion
|
|
||||||
toastObject.goAway(0)
|
|
||||||
deletionToast.value = null
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
onComplete: () => {
|
|
||||||
deletionToast.value = null
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
workingHeaders.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearContent = () => {
|
|
||||||
// set headers list to the initial state
|
|
||||||
workingHeaders.value = [
|
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
bulkHeaders.value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeGQLHeadersCount = computed(
|
|
||||||
() =>
|
|
||||||
headers.value.filter((x) => x.active && (x.key !== "" || x.value !== ""))
|
|
||||||
.length
|
|
||||||
)
|
|
||||||
|
|
||||||
const variableEditor = ref<any | null>(null)
|
const variableEditor = ref<any | null>(null)
|
||||||
|
|
||||||
useCodemirror(
|
useCodemirror(variableEditor, variableString, {
|
||||||
variableEditor,
|
extendedEditorConfig: {
|
||||||
variableString,
|
mode: "application/ld+json",
|
||||||
reactive({
|
placeholder: `${t("request.variables")}`,
|
||||||
extendedEditorConfig: {
|
},
|
||||||
mode: "application/ld+json",
|
linter: jsonLinter,
|
||||||
placeholder: `${t("request.variables")}`,
|
completer: null,
|
||||||
},
|
})
|
||||||
linter: computed(() =>
|
|
||||||
variableString.value.length > 0 ? jsonLinter : null
|
|
||||||
),
|
|
||||||
completer: null,
|
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const queryEditor = ref<any | null>(null)
|
const queryEditor = ref<any | null>(null)
|
||||||
const schemaString = useReadonlyStream(props.conn.schema$, null)
|
const schemaString = useReadonlyStream(props.conn.schema$, null)
|
||||||
@@ -522,16 +330,50 @@ useCodemirror(queryEditor, gqlQueryString, {
|
|||||||
},
|
},
|
||||||
linter: createGQLQueryLinter(schemaString),
|
linter: createGQLQueryLinter(schemaString),
|
||||||
completer: queryCompleter(schemaString),
|
completer: queryCompleter(schemaString),
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const copyQueryIcon = ref("copy")
|
const copyQueryIcon = ref("copy")
|
||||||
const copyVariablesIcon = ref("copy")
|
|
||||||
const prettifyQueryIcon = ref("wand")
|
const prettifyQueryIcon = ref("wand")
|
||||||
const prettifyVariablesIcon = ref("wand")
|
const copyVariablesIcon = ref("copy")
|
||||||
|
|
||||||
const showSaveRequestModal = ref(false)
|
const showSaveRequestModal = ref(false)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
headers,
|
||||||
|
() => {
|
||||||
|
if (!bulkMode.value)
|
||||||
|
if (
|
||||||
|
(headers.value[headers.value.length - 1]?.key !== "" ||
|
||||||
|
headers.value[headers.value.length - 1]?.value !== "") &&
|
||||||
|
headers.value.length
|
||||||
|
)
|
||||||
|
addRequestHeader()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const editBulkHeadersLine = (index: number, item?: GQLHeader | null) => {
|
||||||
|
bulkHeaders.value = headers.value
|
||||||
|
.reduce((all, header, pIndex) => {
|
||||||
|
const current =
|
||||||
|
index === pIndex && item != null
|
||||||
|
? `${item.active ? "" : "//"}${item.key}: ${item.value}`
|
||||||
|
: `${header.active ? "" : "//"}${header.key}: ${header.value}`
|
||||||
|
return [...all, current]
|
||||||
|
}, [])
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearBulkEditor = () => {
|
||||||
|
bulkHeaders.value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!headers.value?.length) {
|
||||||
|
addRequestHeader()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const copyQuery = () => {
|
const copyQuery = () => {
|
||||||
copyToClipboard(gqlQueryString.value)
|
copyToClipboard(gqlQueryString.value)
|
||||||
copyQueryIcon.value = "check"
|
copyQueryIcon.value = "check"
|
||||||
@@ -623,28 +465,47 @@ const copyVariables = () => {
|
|||||||
setTimeout(() => (copyVariablesIcon.value = "copy"), 1000)
|
setTimeout(() => (copyVariablesIcon.value = "copy"), 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
const prettifyVariableString = () => {
|
const addRequestHeader = () => {
|
||||||
try {
|
const empty = { key: "", value: "", active: true }
|
||||||
const jsonObj = JSON.parse(variableString.value)
|
const index = headers.value.length
|
||||||
variableString.value = JSON.stringify(jsonObj, null, 2)
|
|
||||||
prettifyVariablesIcon.value = "check"
|
addGQLHeader(empty)
|
||||||
} catch (e) {
|
editBulkHeadersLine(index, empty)
|
||||||
console.error(e)
|
}
|
||||||
prettifyVariablesIcon.value = "info"
|
|
||||||
toast.error(`${t("error.json_prettify_invalid_body")}`)
|
const updateRequestHeader = (
|
||||||
|
index: number,
|
||||||
|
item: { key: string; value: string; active: boolean }
|
||||||
|
) => {
|
||||||
|
updateGQLHeader(index, item)
|
||||||
|
editBulkHeadersLine(index, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeRequestHeader = (index: number) => {
|
||||||
|
const headersBeforeDeletion = headers.value
|
||||||
|
|
||||||
|
removeGQLHeader(index)
|
||||||
|
editBulkHeadersLine(index, null)
|
||||||
|
|
||||||
|
const deletedItem = headersBeforeDeletion[index]
|
||||||
|
if (deletedItem.key || deletedItem.value) {
|
||||||
|
toast.success(`${t("state.deleted")}`, {
|
||||||
|
action: [
|
||||||
|
{
|
||||||
|
text: `${t("action.undo")}`,
|
||||||
|
onClick: (_, toastObject) => {
|
||||||
|
setGQLHeaders(headersBeforeDeletion as GQLHeader[])
|
||||||
|
editBulkHeadersLine(index, deletedItem)
|
||||||
|
toastObject.goAway(0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
setTimeout(() => (prettifyVariablesIcon.value = "wand"), 1000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearGQLQuery = () => {
|
const clearContent = () => {
|
||||||
gqlQueryString.value = ""
|
headers.value = []
|
||||||
|
clearBulkEditor()
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearGQLVariables = () => {
|
|
||||||
variableString.value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
defineActionHandler("request.send-cancel", runQuery)
|
|
||||||
defineActionHandler("request.save", saveRequest)
|
|
||||||
defineActionHandler("request.reset", clearGQLQuery)
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection ref="response" label="response">
|
||||||
<div
|
<div
|
||||||
v-if="responseString === 'loading'"
|
v-if="responseString === 'loading'"
|
||||||
class="flex flex-col items-center justify-center p-4"
|
class="flex flex-col p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<SmartSpinner class="my-4" />
|
<SmartSpinner class="my-4" />
|
||||||
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
|
<span class="text-secondaryLight">{{ t("state.loading") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="responseString">
|
<div v-else-if="responseString">
|
||||||
<div
|
<div
|
||||||
class="sticky top-0 z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight"
|
class="bg-primary border-b border-dividerLight flex flex-1 pl-4 top-0 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("response.title") }}
|
{{ t("response.title") }}
|
||||||
@@ -42,14 +42,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="flex flex-col items-center justify-center flex-1 p-4 text-secondaryLight"
|
class="flex flex-col flex-1 text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<div class="flex pb-4 my-4 space-x-2">
|
<div class="flex space-x-2 my-4 pb-4">
|
||||||
<div class="flex flex-col items-end space-y-4 text-right">
|
<div class="flex flex-col space-y-4 text-right items-end">
|
||||||
<span class="flex items-center flex-1">
|
<span class="flex flex-1 items-center">
|
||||||
{{ t("shortcut.general.command_menu") }}
|
{{ t("shortcut.general.command_menu") }}
|
||||||
</span>
|
</span>
|
||||||
<span class="flex items-center flex-1">
|
<span class="flex flex-1 items-center">
|
||||||
{{ t("shortcut.general.help_menu") }}
|
{{ t("shortcut.general.help_menu") }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
reverse
|
reverse
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -105,7 +105,6 @@ useCodemirror(
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -137,3 +136,14 @@ const downloadResponse = () => {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.shortcut-key {
|
||||||
|
@apply bg-dividerLight;
|
||||||
|
@apply rounded;
|
||||||
|
@apply ml-2;
|
||||||
|
@apply py-1;
|
||||||
|
@apply px-2;
|
||||||
|
@apply inline-flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -26,34 +26,125 @@
|
|||||||
icon="book-open"
|
icon="book-open"
|
||||||
:label="`${t('tab.documentation')}`"
|
:label="`${t('tab.documentation')}`"
|
||||||
>
|
>
|
||||||
<div
|
<AppSection label="docs">
|
||||||
v-if="
|
<div
|
||||||
queryFields.length === 0 &&
|
v-if="
|
||||||
mutationFields.length === 0 &&
|
queryFields.length === 0 &&
|
||||||
subscriptionFields.length === 0 &&
|
mutationFields.length === 0 &&
|
||||||
graphqlTypes.length === 0
|
subscriptionFields.length === 0 &&
|
||||||
"
|
graphqlTypes.length === 0
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
"
|
||||||
>
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
<img
|
>
|
||||||
:src="`/images/states/${$colorMode.value}/add_comment.svg`"
|
<img
|
||||||
loading="lazy"
|
:src="`/images/states/${$colorMode.value}/add_comment.svg`"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
loading="lazy"
|
||||||
:alt="`${t('empty.documentation')}`"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
/>
|
:alt="`${t('empty.documentation')}`"
|
||||||
<span class="mb-4 text-center">
|
|
||||||
{{ t("empty.documentation") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<div class="sticky top-0 z-10 flex bg-primary">
|
|
||||||
<input
|
|
||||||
v-model="graphqlFieldsFilterText"
|
|
||||||
type="search"
|
|
||||||
autocomplete="off"
|
|
||||||
:placeholder="`${t('action.search')}`"
|
|
||||||
class="flex w-full p-4 py-2 bg-transparent"
|
|
||||||
/>
|
/>
|
||||||
|
<span class="text-center mb-4">
|
||||||
|
{{ t("empty.documentation") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="bg-primary flex top-0 z-10 sticky">
|
||||||
|
<input
|
||||||
|
v-model="graphqlFieldsFilterText"
|
||||||
|
type="search"
|
||||||
|
autocomplete="off"
|
||||||
|
:placeholder="`${t('action.search')}`"
|
||||||
|
class="bg-transparent flex w-full p-4 py-2"
|
||||||
|
/>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
to="https://docs.hoppscotch.io/quickstart/graphql"
|
||||||
|
blank
|
||||||
|
:title="t('app.wiki')"
|
||||||
|
svg="help-circle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SmartTabs
|
||||||
|
ref="gqlTabs"
|
||||||
|
styles="border-t border-b border-dividerLight bg-primary sticky z-10 top-sidebarPrimaryStickyFold"
|
||||||
|
>
|
||||||
|
<div class="gqlTabs">
|
||||||
|
<SmartTab
|
||||||
|
v-if="queryFields.length > 0"
|
||||||
|
:id="'queries'"
|
||||||
|
:label="`${t('tab.queries')}`"
|
||||||
|
:selected="true"
|
||||||
|
class="divide-dividerLight divide-y"
|
||||||
|
>
|
||||||
|
<GraphqlField
|
||||||
|
v-for="(field, index) in filteredQueryFields"
|
||||||
|
:key="`field-${index}`"
|
||||||
|
:gql-field="field"
|
||||||
|
:jump-type-callback="handleJumpToType"
|
||||||
|
class="p-4"
|
||||||
|
/>
|
||||||
|
</SmartTab>
|
||||||
|
<SmartTab
|
||||||
|
v-if="mutationFields.length > 0"
|
||||||
|
:id="'mutations'"
|
||||||
|
:label="`${t('graphql.mutations')}`"
|
||||||
|
class="divide-dividerLight divide-y"
|
||||||
|
>
|
||||||
|
<GraphqlField
|
||||||
|
v-for="(field, index) in filteredMutationFields"
|
||||||
|
:key="`field-${index}`"
|
||||||
|
:gql-field="field"
|
||||||
|
:jump-type-callback="handleJumpToType"
|
||||||
|
class="p-4"
|
||||||
|
/>
|
||||||
|
</SmartTab>
|
||||||
|
<SmartTab
|
||||||
|
v-if="subscriptionFields.length > 0"
|
||||||
|
:id="'subscriptions'"
|
||||||
|
:label="`${t('graphql.subscriptions')}`"
|
||||||
|
class="divide-dividerLight divide-y"
|
||||||
|
>
|
||||||
|
<GraphqlField
|
||||||
|
v-for="(field, index) in filteredSubscriptionFields"
|
||||||
|
:key="`field-${index}`"
|
||||||
|
:gql-field="field"
|
||||||
|
:jump-type-callback="handleJumpToType"
|
||||||
|
class="p-4"
|
||||||
|
/>
|
||||||
|
</SmartTab>
|
||||||
|
<SmartTab
|
||||||
|
v-if="graphqlTypes.length > 0"
|
||||||
|
:id="'types'"
|
||||||
|
ref="typesTab"
|
||||||
|
:label="`${t('tab.types')}`"
|
||||||
|
class="divide-dividerLight divide-y"
|
||||||
|
>
|
||||||
|
<GraphqlType
|
||||||
|
v-for="(type, index) in filteredGraphqlTypes"
|
||||||
|
:key="`type-${index}`"
|
||||||
|
:gql-type="type"
|
||||||
|
:gql-types="graphqlTypes"
|
||||||
|
:is-highlighted="isGqlTypeHighlighted(type)"
|
||||||
|
:highlighted-fields="getGqlTypeHighlightedFields(type)"
|
||||||
|
:jump-type-callback="handleJumpToType"
|
||||||
|
/>
|
||||||
|
</SmartTab>
|
||||||
|
</div>
|
||||||
|
</SmartTabs>
|
||||||
|
</div>
|
||||||
|
</AppSection>
|
||||||
|
</SmartTab>
|
||||||
|
|
||||||
|
<SmartTab :id="'schema'" icon="box" :label="`${t('tab.schema')}`">
|
||||||
|
<AppSection ref="schema" label="schema">
|
||||||
|
<div
|
||||||
|
v-if="schemaString"
|
||||||
|
class="bg-primary border-b border-dividerLight flex flex-1 pl-4 top-0 z-10 sticky items-center justify-between"
|
||||||
|
>
|
||||||
|
<label class="font-semibold text-secondaryLight">
|
||||||
|
{{ t("graphql.schema") }}
|
||||||
|
</label>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -62,132 +153,45 @@
|
|||||||
:title="t('app.wiki')"
|
:title="t('app.wiki')"
|
||||||
svg="help-circle"
|
svg="help-circle"
|
||||||
/>
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('state.linewrap')"
|
||||||
|
:class="{ '!text-accent': linewrapEnabled }"
|
||||||
|
svg="wrap-text"
|
||||||
|
@click.native.prevent="linewrapEnabled = !linewrapEnabled"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
ref="downloadSchema"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.download_file')"
|
||||||
|
:svg="downloadSchemaIcon"
|
||||||
|
@click.native="downloadSchema"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
ref="copySchemaCode"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="t('action.copy')"
|
||||||
|
:svg="copySchemaIcon"
|
||||||
|
@click.native="copySchema"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SmartTabs
|
<div v-if="schemaString" ref="schemaEditor"></div>
|
||||||
ref="gqlTabs"
|
<div
|
||||||
styles="border-t border-b border-dividerLight bg-primary sticky z-10 top-sidebarPrimaryStickyFold"
|
v-else
|
||||||
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<div class="gqlTabs">
|
<img
|
||||||
<SmartTab
|
:src="`/images/states/${$colorMode.value}/blockchain.svg`"
|
||||||
v-if="queryFields.length > 0"
|
loading="lazy"
|
||||||
:id="'queries'"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:label="`${t('tab.queries')}`"
|
:alt="`${t('empty.schema')}`"
|
||||||
:selected="true"
|
|
||||||
class="divide-y divide-dividerLight"
|
|
||||||
>
|
|
||||||
<GraphqlField
|
|
||||||
v-for="(field, index) in filteredQueryFields"
|
|
||||||
:key="`field-${index}`"
|
|
||||||
:gql-field="field"
|
|
||||||
:jump-type-callback="handleJumpToType"
|
|
||||||
class="p-4"
|
|
||||||
/>
|
|
||||||
</SmartTab>
|
|
||||||
<SmartTab
|
|
||||||
v-if="mutationFields.length > 0"
|
|
||||||
:id="'mutations'"
|
|
||||||
:label="`${t('graphql.mutations')}`"
|
|
||||||
class="divide-y divide-dividerLight"
|
|
||||||
>
|
|
||||||
<GraphqlField
|
|
||||||
v-for="(field, index) in filteredMutationFields"
|
|
||||||
:key="`field-${index}`"
|
|
||||||
:gql-field="field"
|
|
||||||
:jump-type-callback="handleJumpToType"
|
|
||||||
class="p-4"
|
|
||||||
/>
|
|
||||||
</SmartTab>
|
|
||||||
<SmartTab
|
|
||||||
v-if="subscriptionFields.length > 0"
|
|
||||||
:id="'subscriptions'"
|
|
||||||
:label="`${t('graphql.subscriptions')}`"
|
|
||||||
class="divide-y divide-dividerLight"
|
|
||||||
>
|
|
||||||
<GraphqlField
|
|
||||||
v-for="(field, index) in filteredSubscriptionFields"
|
|
||||||
:key="`field-${index}`"
|
|
||||||
:gql-field="field"
|
|
||||||
:jump-type-callback="handleJumpToType"
|
|
||||||
class="p-4"
|
|
||||||
/>
|
|
||||||
</SmartTab>
|
|
||||||
<SmartTab
|
|
||||||
v-if="graphqlTypes.length > 0"
|
|
||||||
:id="'types'"
|
|
||||||
ref="typesTab"
|
|
||||||
:label="`${t('tab.types')}`"
|
|
||||||
class="divide-y divide-dividerLight"
|
|
||||||
>
|
|
||||||
<GraphqlType
|
|
||||||
v-for="(type, index) in filteredGraphqlTypes"
|
|
||||||
:key="`type-${index}`"
|
|
||||||
:gql-type="type"
|
|
||||||
:gql-types="graphqlTypes"
|
|
||||||
:is-highlighted="isGqlTypeHighlighted(type)"
|
|
||||||
:highlighted-fields="getGqlTypeHighlightedFields(type)"
|
|
||||||
:jump-type-callback="handleJumpToType"
|
|
||||||
/>
|
|
||||||
</SmartTab>
|
|
||||||
</div>
|
|
||||||
</SmartTabs>
|
|
||||||
</div>
|
|
||||||
</SmartTab>
|
|
||||||
|
|
||||||
<SmartTab :id="'schema'" icon="box" :label="`${t('tab.schema')}`">
|
|
||||||
<div
|
|
||||||
v-if="schemaString"
|
|
||||||
class="sticky top-0 z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight"
|
|
||||||
>
|
|
||||||
<label class="font-semibold text-secondaryLight">
|
|
||||||
{{ t("graphql.schema") }}
|
|
||||||
</label>
|
|
||||||
<div class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
to="https://docs.hoppscotch.io/quickstart/graphql"
|
|
||||||
blank
|
|
||||||
:title="t('app.wiki')"
|
|
||||||
svg="help-circle"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('state.linewrap')"
|
|
||||||
:class="{ '!text-accent': linewrapEnabled }"
|
|
||||||
svg="wrap-text"
|
|
||||||
@click.native.prevent="linewrapEnabled = !linewrapEnabled"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
ref="downloadSchema"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.download_file')"
|
|
||||||
:svg="downloadSchemaIcon"
|
|
||||||
@click.native="downloadSchema"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
ref="copySchemaCode"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.copy')"
|
|
||||||
:svg="copySchemaIcon"
|
|
||||||
@click.native="copySchema"
|
|
||||||
/>
|
/>
|
||||||
|
<span class="text-center mb-4">
|
||||||
|
{{ t("empty.schema") }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
<div v-if="schemaString" ref="schemaEditor"></div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${$colorMode.value}/blockchain.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
|
||||||
:alt="`${t('empty.schema')}`"
|
|
||||||
/>
|
|
||||||
<span class="mb-4 text-center">
|
|
||||||
{{ t("empty.schema") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</SmartTab>
|
</SmartTab>
|
||||||
</SmartTabs>
|
</SmartTabs>
|
||||||
</template>
|
</template>
|
||||||
@@ -405,7 +409,6 @@ useCodemirror(
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<span v-else-if="isEnum" class="text-accent">enum </span>
|
<span v-else-if="isEnum" class="text-accent">enum </span>
|
||||||
{{ gqlType.name }}
|
{{ gqlType.name }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="gqlType.description" class="py-2 text-secondaryLight type-desc">
|
<div v-if="gqlType.description" class="text-secondaryLight py-2 type-desc">
|
||||||
{{ gqlType.description }}
|
{{ gqlType.description }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="interfaces.length > 0">
|
<div v-if="interfaces.length > 0">
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<GraphqlTypeLink
|
<GraphqlTypeLink
|
||||||
:gql-type="gqlInterface"
|
:gql-type="gqlInterface"
|
||||||
:jump-type-callback="jumpTypeCallback"
|
:jump-type-callback="jumpTypeCallback"
|
||||||
class="pl-4 border-l-2 border-divider"
|
class="border-divider border-l-2 pl-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
:key="`child-${index}`"
|
:key="`child-${index}`"
|
||||||
:gql-type="child"
|
:gql-type="child"
|
||||||
:jump-type-callback="jumpTypeCallback"
|
:jump-type-callback="jumpTypeCallback"
|
||||||
class="pl-4 border-l-2 border-divider"
|
class="border-divider border-l-2 pl-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="gqlType.getFields">
|
<div v-if="gqlType.getFields">
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
<GraphqlField
|
<GraphqlField
|
||||||
v-for="(field, index) in gqlType.getFields()"
|
v-for="(field, index) in gqlType.getFields()"
|
||||||
:key="`field-${index}`"
|
:key="`field-${index}`"
|
||||||
class="pl-4 border-l-2 border-divider"
|
class="border-divider border-l-2 pl-4"
|
||||||
:gql-field="field"
|
:gql-field="field"
|
||||||
:is-highlighted="isFieldHighlighted({ field })"
|
:is-highlighted="isFieldHighlighted({ field })"
|
||||||
:jump-type-callback="jumpTypeCallback"
|
:jump-type-callback="jumpTypeCallback"
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(value, index) in gqlType.getValues()"
|
v-for="(value, index) in gqlType.getValues()"
|
||||||
:key="`value-${index}`"
|
:key="`value-${index}`"
|
||||||
class="pl-4 border-l-2 border-divider"
|
class="border-divider border-l-2 pl-4"
|
||||||
v-text="value.name"
|
v-text="value.name"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,19 +2,13 @@
|
|||||||
<div class="flex flex-col group">
|
<div class="flex flex-col group">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span
|
<span
|
||||||
class="flex flex-1 min-w-0 py-2 pl-4 pr-2 transition cursor-pointer group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 pl-4 transition group-hover:text-secondaryDark"
|
||||||
data-testid="restore_history_entry"
|
data-testid="restore_history_entry"
|
||||||
@click="useEntry"
|
@click="useEntry"
|
||||||
>
|
>
|
||||||
<span class="truncate">
|
<span class="truncate">
|
||||||
{{ entry.request.url }}
|
{{ entry.request.url }}
|
||||||
</span>
|
</span>
|
||||||
<tippy
|
|
||||||
v-if="entry.updatedOn"
|
|
||||||
theme="tooltip"
|
|
||||||
:delay="[500, 20]"
|
|
||||||
:content="`${new Date(entry.updatedOn).toLocaleString()}`"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -42,11 +36,11 @@
|
|||||||
@click.native="$emit('toggle-star')"
|
@click.native="$emit('toggle-star')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col text-tiny">
|
<div class="flex flex-col">
|
||||||
<span
|
<span
|
||||||
v-for="(line, index) in query"
|
v-for="(line, index) in query"
|
||||||
:key="`line-${index}`"
|
:key="`line-${index}`"
|
||||||
class="px-4 font-mono truncate whitespace-pre cursor-pointer text-secondaryLight"
|
class="cursor-pointer font-mono text-secondaryLight px-4 truncate whitespace-pre"
|
||||||
data-testid="restore_history_entry"
|
data-testid="restore_history_entry"
|
||||||
@click="useEntry"
|
@click="useEntry"
|
||||||
>{{ line }}</span
|
>{{ line }}</span
|
||||||
@@ -55,39 +49,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { computed, ref } from "@nuxtjs/composition-api"
|
import {
|
||||||
|
computed,
|
||||||
|
defineComponent,
|
||||||
|
PropType,
|
||||||
|
ref,
|
||||||
|
} from "@nuxtjs/composition-api"
|
||||||
import { makeGQLRequest } from "@hoppscotch/data"
|
import { makeGQLRequest } from "@hoppscotch/data"
|
||||||
import { setGQLSession } from "~/newstore/GQLSession"
|
import { setGQLSession } from "~/newstore/GQLSession"
|
||||||
import { GQLHistoryEntry } from "~/newstore/history"
|
import { GQLHistoryEntry } from "~/newstore/history"
|
||||||
|
|
||||||
const props = defineProps<{
|
export default defineComponent({
|
||||||
entry: GQLHistoryEntry
|
props: {
|
||||||
showMore: Boolean
|
entry: { type: Object as PropType<GQLHistoryEntry>, default: () => {} },
|
||||||
}>()
|
showMore: Boolean,
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const expand = ref(false)
|
||||||
|
|
||||||
const expand = ref(false)
|
const query = computed(() =>
|
||||||
|
expand.value
|
||||||
|
? (props.entry.request.query.split("\n") as string[])
|
||||||
|
: (props.entry.request.query
|
||||||
|
.split("\n")
|
||||||
|
.slice(0, 2)
|
||||||
|
.concat(["..."]) as string[])
|
||||||
|
)
|
||||||
|
|
||||||
const query = computed(() =>
|
const useEntry = () => {
|
||||||
expand.value
|
setGQLSession({
|
||||||
? (props.entry.request.query.split("\n") as string[])
|
request: makeGQLRequest({
|
||||||
: (props.entry.request.query
|
name: props.entry.request.name,
|
||||||
.split("\n")
|
url: props.entry.request.url,
|
||||||
.slice(0, 2)
|
headers: props.entry.request.headers,
|
||||||
.concat(["..."]) as string[])
|
query: props.entry.request.query,
|
||||||
)
|
variables: props.entry.request.variables,
|
||||||
|
}),
|
||||||
|
schema: "",
|
||||||
|
response: props.entry.response,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const useEntry = () => {
|
return {
|
||||||
setGQLSession({
|
expand,
|
||||||
request: makeGQLRequest({
|
query,
|
||||||
name: props.entry.request.name,
|
useEntry,
|
||||||
url: props.entry.request.url,
|
}
|
||||||
headers: props.entry.request.headers,
|
},
|
||||||
query: props.entry.request.query,
|
})
|
||||||
variables: props.entry.request.variables,
|
|
||||||
}),
|
|
||||||
schema: "",
|
|
||||||
response: props.entry.response,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection label="history">
|
||||||
<div class="sticky top-0 z-10 flex border-b bg-primary border-dividerLight">
|
<div class="bg-primary border-b border-dividerLight flex top-0 z-10 sticky">
|
||||||
<input
|
<input
|
||||||
v-model="filterText"
|
v-model="filterText"
|
||||||
type="search"
|
type="search"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="flex w-full p-4 py-2 bg-transparent"
|
class="bg-transparent flex w-full p-4 py-2"
|
||||||
:placeholder="`${t('action.search')}`"
|
:placeholder="`${$t('action.search')}`"
|
||||||
/>
|
/>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
to="https://docs.hoppscotch.io/features/history"
|
to="https://docs.hoppscotch.io/features/history"
|
||||||
blank
|
blank
|
||||||
:title="t('app.wiki')"
|
:title="$t('app.wiki')"
|
||||||
svg="help-circle"
|
svg="help-circle"
|
||||||
/>
|
/>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -21,94 +21,67 @@
|
|||||||
data-testid="clear_history"
|
data-testid="clear_history"
|
||||||
:disabled="history.length === 0"
|
:disabled="history.length === 0"
|
||||||
svg="trash-2"
|
svg="trash-2"
|
||||||
:title="t('action.clear_all')"
|
:title="$t('action.clear_all')"
|
||||||
@click.native="confirmRemove = true"
|
@click.native="confirmRemove = true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<details
|
<div v-for="(entry, index) in filteredHistory" :key="`entry-${index}`">
|
||||||
v-for="(
|
<HistoryRestCard
|
||||||
filteredHistoryGroup, filteredHistoryGroupIndex
|
v-if="page == 'rest'"
|
||||||
) in filteredHistoryGroups"
|
:id="index"
|
||||||
:key="`filteredHistoryGroup-${filteredHistoryGroupIndex}`"
|
:entry="entry"
|
||||||
class="flex flex-col"
|
:show-more="showMore"
|
||||||
open
|
@toggle-star="toggleStar(entry)"
|
||||||
>
|
@delete-entry="deleteHistory(entry)"
|
||||||
<summary
|
@use-entry="useHistory(entry)"
|
||||||
class="flex items-center justify-between flex-1 min-w-0 transition cursor-pointer focus:outline-none text-secondaryLight text-tiny group"
|
/>
|
||||||
>
|
<HistoryGraphqlCard
|
||||||
<span
|
v-if="page == 'graphql'"
|
||||||
class="px-4 py-2 truncate transition group-hover:text-secondary capitalize-first"
|
:entry="entry"
|
||||||
>
|
:show-more="showMore"
|
||||||
{{ filteredHistoryGroupIndex }}
|
@toggle-star="toggleStar(entry)"
|
||||||
</span>
|
@delete-entry="deleteHistory(entry)"
|
||||||
<ButtonSecondary
|
@use-entry="useHistory(entry)"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
/>
|
||||||
svg="trash"
|
</div>
|
||||||
color="red"
|
|
||||||
:title="$t('action.remove')"
|
|
||||||
class="hidden group-hover:inline-flex"
|
|
||||||
@click.native="deleteBattleHistoryEntry(filteredHistoryGroup)"
|
|
||||||
/>
|
|
||||||
</summary>
|
|
||||||
<div
|
|
||||||
v-for="(entry, index) in filteredHistoryGroup"
|
|
||||||
:key="`entry-${index}`"
|
|
||||||
>
|
|
||||||
<component
|
|
||||||
:is="page == 'rest' ? 'HistoryRestCard' : 'HistoryGraphqlCard'"
|
|
||||||
:id="index"
|
|
||||||
:entry="entry"
|
|
||||||
:show-more="showMore"
|
|
||||||
@toggle-star="toggleStar(entry)"
|
|
||||||
@delete-entry="deleteHistory(entry)"
|
|
||||||
@use-entry="useHistory(entry)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!(filteredHistory.length !== 0 || history.length === 0)"
|
v-if="!(filteredHistory.length !== 0 || history.length === 0)"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<i class="pb-2 opacity-75 material-icons">manage_search</i>
|
<i class="opacity-75 pb-2 material-icons">manage_search</i>
|
||||||
<span class="my-2 text-center">
|
<span class="text-center">
|
||||||
{{ t("state.nothing_found") }} "{{ filterText }}"
|
{{ $t("state.nothing_found") }} "{{ filterText }}"
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="history.length === 0"
|
v-if="history.length === 0"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/history.svg`"
|
:src="`/images/states/${$colorMode.value}/history.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${t('empty.history')}`"
|
:alt="$t('empty.history')"
|
||||||
/>
|
/>
|
||||||
<span class="mb-4 text-center">
|
<span class="text-center mb-4">
|
||||||
{{ t("empty.history") }}
|
{{ $t("empty.history") }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<SmartConfirmModal
|
<SmartConfirmModal
|
||||||
:show="confirmRemove"
|
:show="confirmRemove"
|
||||||
:title="`${t('confirm.remove_history')}`"
|
:title="`${$t('confirm.remove_history')}`"
|
||||||
@hide-modal="confirmRemove = false"
|
@hide-modal="confirmRemove = false"
|
||||||
@resolve="clearHistory"
|
@resolve="clearHistory"
|
||||||
/>
|
/>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { computed, ref } from "@nuxtjs/composition-api"
|
import { defineComponent, PropType } from "@nuxtjs/composition-api"
|
||||||
import * as timeago from "timeago.js"
|
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||||
import { safelyExtractRESTRequest } from "@hoppscotch/data"
|
|
||||||
import {
|
|
||||||
useI18n,
|
|
||||||
useReadonlyStream,
|
|
||||||
useToast,
|
|
||||||
} from "~/helpers/utils/composables"
|
|
||||||
import {
|
import {
|
||||||
restHistory$,
|
restHistory$,
|
||||||
graphqlHistory$,
|
graphqlHistory$,
|
||||||
@@ -121,87 +94,66 @@ import {
|
|||||||
RESTHistoryEntry,
|
RESTHistoryEntry,
|
||||||
GQLHistoryEntry,
|
GQLHistoryEntry,
|
||||||
} from "~/newstore/history"
|
} from "~/newstore/history"
|
||||||
import { getDefaultRESTRequest, setRESTRequest } from "~/newstore/RESTSession"
|
import { setRESTRequest } from "~/newstore/RESTSession"
|
||||||
|
|
||||||
const props = defineProps<{
|
export default defineComponent({
|
||||||
page: "rest" | "graphql"
|
props: {
|
||||||
}>()
|
page: { type: String as PropType<"rest" | "graphql">, default: null },
|
||||||
|
},
|
||||||
const filterText = ref("")
|
setup(props) {
|
||||||
const showMore = ref(false)
|
return {
|
||||||
const confirmRemove = ref(false)
|
history: useReadonlyStream<RESTHistoryEntry[] | GQLHistoryEntry[]>(
|
||||||
const toast = useToast()
|
props.page === "rest" ? restHistory$ : graphqlHistory$,
|
||||||
const t = useI18n()
|
[]
|
||||||
|
),
|
||||||
const groupByDate = (array: any[], key: string) => {
|
|
||||||
return array.reduce((rv: any, x: any) => {
|
|
||||||
;(rv[timeago.format(x[key])] = rv[timeago.format(x[key])] || []).push(x)
|
|
||||||
return rv
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
const history = useReadonlyStream<RESTHistoryEntry[] | GQLHistoryEntry[]>(
|
|
||||||
props.page === "rest" ? restHistory$ : graphqlHistory$,
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const filteredHistory = computed(() => {
|
|
||||||
const regExp = new RegExp(filterText.value, "gi")
|
|
||||||
const check = (obj: any) => {
|
|
||||||
if (obj !== null && typeof obj === "object") {
|
|
||||||
return Object.values(obj).some(check)
|
|
||||||
}
|
}
|
||||||
if (Array.isArray(obj)) {
|
},
|
||||||
return obj.some(check)
|
data() {
|
||||||
|
return {
|
||||||
|
filterText: "",
|
||||||
|
showMore: false,
|
||||||
|
confirmRemove: false,
|
||||||
}
|
}
|
||||||
return (
|
},
|
||||||
(typeof obj === "string" || typeof obj === "number") &&
|
computed: {
|
||||||
regExp.test(obj as string)
|
filteredHistory(): any[] {
|
||||||
)
|
const filteringHistory = this.history as Array<
|
||||||
}
|
RESTHistoryEntry | GQLHistoryEntry
|
||||||
return (history.value as Array<RESTHistoryEntry | GQLHistoryEntry>).filter(
|
>
|
||||||
check
|
|
||||||
)
|
return filteringHistory.filter(
|
||||||
|
(entry: RESTHistoryEntry | GQLHistoryEntry) => {
|
||||||
|
const filterText = this.filterText.toLowerCase()
|
||||||
|
return Object.keys(entry).some((key) => {
|
||||||
|
let value = entry[key as keyof typeof entry]
|
||||||
|
if (value) {
|
||||||
|
value = `${value}`
|
||||||
|
return value.toLowerCase().includes(filterText)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clearHistory() {
|
||||||
|
if (this.page === "rest") clearRESTHistory()
|
||||||
|
else clearGraphqlHistory()
|
||||||
|
this.$toast.success(`${this.$t("state.history_deleted")}`)
|
||||||
|
},
|
||||||
|
useHistory(entry: any) {
|
||||||
|
if (this.page === "rest") setRESTRequest(entry.request)
|
||||||
|
},
|
||||||
|
deleteHistory(entry: any) {
|
||||||
|
if (this.page === "rest") deleteRESTHistoryEntry(entry)
|
||||||
|
else deleteGraphqlHistoryEntry(entry)
|
||||||
|
this.$toast.success(`${this.$t("state.deleted")}`)
|
||||||
|
},
|
||||||
|
toggleStar(entry: any) {
|
||||||
|
if (this.page === "rest") toggleRESTHistoryEntryStar(entry)
|
||||||
|
else toggleGraphqlHistoryEntryStar(entry)
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const filteredHistoryGroups = computed(() =>
|
|
||||||
groupByDate(filteredHistory.value, "updatedOn")
|
|
||||||
)
|
|
||||||
|
|
||||||
const clearHistory = () => {
|
|
||||||
if (props.page === "rest") clearRESTHistory()
|
|
||||||
else clearGraphqlHistory()
|
|
||||||
toast.success(`${t("state.history_deleted")}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const useHistory = (entry: any) => {
|
|
||||||
if (props.page === "rest")
|
|
||||||
setRESTRequest(
|
|
||||||
safelyExtractRESTRequest(entry.request, getDefaultRESTRequest())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteBattleHistoryEntry = (entries: number[]) => {
|
|
||||||
if (props.page === "rest") {
|
|
||||||
entries.forEach((entry: any) => {
|
|
||||||
deleteRESTHistoryEntry(entry)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
entries.forEach((entry: any) => {
|
|
||||||
deleteGraphqlHistoryEntry(entry)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
toast.success(`${t("state.deleted")}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteHistory = (entry: any) => {
|
|
||||||
if (props.page === "rest") deleteRESTHistoryEntry(entry)
|
|
||||||
else deleteGraphqlHistoryEntry(entry)
|
|
||||||
toast.success(`${t("state.deleted")}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleStar = (entry: any) => {
|
|
||||||
if (props.page === "rest") toggleRESTHistoryEntryStar(entry)
|
|
||||||
else toggleGraphqlHistoryEntryStar(entry)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-stretch group">
|
<div class="flex items-stretch group">
|
||||||
<span
|
<span
|
||||||
v-tippy="{ theme: 'tooltip', delay: [500, 20] }"
|
class="cursor-pointer flex px-2 w-16 items-center justify-center truncate"
|
||||||
class="flex items-center justify-center w-16 px-2 truncate cursor-pointer"
|
|
||||||
:class="entryStatus.className"
|
:class="entryStatus.className"
|
||||||
data-testid="restore_history_entry"
|
data-testid="restore_history_entry"
|
||||||
:title="`${duration}`"
|
:title="`${duration}`"
|
||||||
@@ -11,19 +10,14 @@
|
|||||||
{{ entry.request.method }}
|
{{ entry.request.method }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="flex flex-1 min-w-0 py-2 pr-2 transition cursor-pointer group-hover:text-secondaryDark"
|
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hover:text-secondaryDark"
|
||||||
data-testid="restore_history_entry"
|
data-testid="restore_history_entry"
|
||||||
|
:title="`${duration}`"
|
||||||
@click="$emit('use-entry')"
|
@click="$emit('use-entry')"
|
||||||
>
|
>
|
||||||
<span class="truncate">
|
<span class="truncate">
|
||||||
{{ entry.request.endpoint }}
|
{{ entry.request.endpoint }}
|
||||||
</span>
|
</span>
|
||||||
<tippy
|
|
||||||
v-if="entry.updatedOn"
|
|
||||||
theme="tooltip"
|
|
||||||
:delay="[500, 20]"
|
|
||||||
:content="`${new Date(entry.updatedOn).toLocaleString()}`"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
@@ -46,36 +40,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { computed } from "@nuxtjs/composition-api"
|
import { computed, defineComponent, PropType } from "@nuxtjs/composition-api"
|
||||||
import findStatusGroup from "~/helpers/findStatusGroup"
|
import findStatusGroup from "~/helpers/findStatusGroup"
|
||||||
import { useI18n } from "~/helpers/utils/composables"
|
import { useI18n } from "~/helpers/utils/composables"
|
||||||
import { RESTHistoryEntry } from "~/newstore/history"
|
import { RESTHistoryEntry } from "~/newstore/history"
|
||||||
|
|
||||||
const props = defineProps<{
|
export default defineComponent({
|
||||||
entry: RESTHistoryEntry
|
props: {
|
||||||
showMore: Boolean
|
entry: { type: Object as PropType<RESTHistoryEntry>, default: () => {} },
|
||||||
}>()
|
showMore: Boolean,
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const t = useI18n()
|
||||||
|
|
||||||
const t = useI18n()
|
const duration = computed(() => {
|
||||||
|
if (props.entry.responseMeta.duration) {
|
||||||
|
const responseDuration = props.entry.responseMeta.duration
|
||||||
|
if (!responseDuration) return ""
|
||||||
|
|
||||||
const duration = computed(() => {
|
return responseDuration > 0
|
||||||
if (props.entry.responseMeta.duration) {
|
? `${t("request.duration")}: ${responseDuration}ms`
|
||||||
const responseDuration = props.entry.responseMeta.duration
|
: t("error.no_duration")
|
||||||
if (!responseDuration) return ""
|
} else return t("error.no_duration")
|
||||||
|
})
|
||||||
|
|
||||||
return responseDuration > 0
|
const entryStatus = computed(() => {
|
||||||
? `${t("request.duration")}: ${responseDuration}ms`
|
const foundStatusGroup = findStatusGroup(
|
||||||
: t("error.no_duration")
|
props.entry.responseMeta.statusCode
|
||||||
} else return t("error.no_duration")
|
)
|
||||||
})
|
return (
|
||||||
|
foundStatusGroup || {
|
||||||
|
className: "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const entryStatus = computed(() => {
|
return {
|
||||||
const foundStatusGroup = findStatusGroup(props.entry.responseMeta.statusCode)
|
duration,
|
||||||
return (
|
entryStatus,
|
||||||
foundStatusGroup || {
|
|
||||||
className: "",
|
|
||||||
}
|
}
|
||||||
)
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<span class="flex items-center">
|
<span class="flex items-center">
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<span class="select-wrapper">
|
<span class="select-wrapper">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
class="pr-8 ml-2 rounded-none"
|
class="rounded-none ml-2 pr-8"
|
||||||
:label="authName"
|
:label="authName"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -29,12 +29,9 @@
|
|||||||
? 'radio_button_checked'
|
? 'radio_button_checked'
|
||||||
: 'radio_button_unchecked'
|
: 'radio_button_unchecked'
|
||||||
"
|
"
|
||||||
:active="authName === 'None'"
|
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
authType = 'none'
|
||||||
authType = 'none'
|
$refs.authTypeOptions.tippy().hide()
|
||||||
authTypeOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
@@ -44,12 +41,9 @@
|
|||||||
? 'radio_button_checked'
|
? 'radio_button_checked'
|
||||||
: 'radio_button_unchecked'
|
: 'radio_button_unchecked'
|
||||||
"
|
"
|
||||||
:active="authName === 'Basic Auth'"
|
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
authType = 'basic'
|
||||||
authType = 'basic'
|
$refs.authTypeOptions.tippy().hide()
|
||||||
authTypeOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
@@ -59,12 +53,9 @@
|
|||||||
? 'radio_button_checked'
|
? 'radio_button_checked'
|
||||||
: 'radio_button_unchecked'
|
: 'radio_button_unchecked'
|
||||||
"
|
"
|
||||||
:active="authName === 'Bearer'"
|
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
authType = 'bearer'
|
||||||
authType = 'bearer'
|
$refs.authTypeOptions.tippy().hide()
|
||||||
authTypeOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<SmartItem
|
<SmartItem
|
||||||
@@ -74,27 +65,9 @@
|
|||||||
? 'radio_button_checked'
|
? 'radio_button_checked'
|
||||||
: 'radio_button_unchecked'
|
: 'radio_button_unchecked'
|
||||||
"
|
"
|
||||||
:active="authName === 'OAuth 2.0'"
|
|
||||||
@click.native="
|
@click.native="
|
||||||
() => {
|
authType = 'oauth-2'
|
||||||
authType = 'oauth-2'
|
$refs.authTypeOptions.tippy().hide()
|
||||||
authTypeOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
label="API key"
|
|
||||||
:icon="
|
|
||||||
authName === 'API key'
|
|
||||||
? 'radio_button_checked'
|
|
||||||
: 'radio_button_unchecked'
|
|
||||||
"
|
|
||||||
:active="authName === 'API key'"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
authType = 'api-key'
|
|
||||||
authTypeOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</tippy>
|
</tippy>
|
||||||
@@ -130,15 +103,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="authType === 'none'"
|
v-if="authType === 'none'"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/login.svg`"
|
:src="`/images/states/${$colorMode.value}/login.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.authorization')}`"
|
:alt="$t('empty.authorization')"
|
||||||
/>
|
/>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center pb-4">
|
||||||
{{ $t("empty.authorization") }}
|
{{ $t("empty.authorization") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -151,135 +124,104 @@
|
|||||||
class="mb-4"
|
class="mb-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex border-b border-dividerLight">
|
<div v-if="authType === 'basic'" class="border-b border-dividerLight flex">
|
||||||
<div class="w-2/3 border-r border-dividerLight">
|
<div class="border-r border-dividerLight w-2/3">
|
||||||
<div v-if="authType === 'basic'">
|
<div class="border-b border-dividerLight flex">
|
||||||
<div class="flex border-b border-dividerLight">
|
<SmartEnvInput
|
||||||
<SmartEnvInput
|
v-model="basicUsername"
|
||||||
v-model="basicUsername"
|
:placeholder="$t('authorization.username')"
|
||||||
:placeholder="$t('authorization.username')"
|
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex border-b border-dividerLight">
|
|
||||||
<SmartEnvInput
|
|
||||||
v-model="basicPassword"
|
|
||||||
:placeholder="$t('authorization.password')"
|
|
||||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="authType === 'bearer'">
|
<div class="border-b border-dividerLight flex">
|
||||||
<div class="flex border-b border-dividerLight">
|
<SmartEnvInput
|
||||||
<SmartEnvInput
|
v-model="basicPassword"
|
||||||
v-model="bearerToken"
|
:placeholder="$t('authorization.password')"
|
||||||
placeholder="Token"
|
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="authType === 'oauth-2'">
|
|
||||||
<div class="flex border-b border-dividerLight">
|
|
||||||
<SmartEnvInput
|
|
||||||
v-model="oauth2Token"
|
|
||||||
placeholder="Token"
|
|
||||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<HttpOAuth2Authorization />
|
|
||||||
</div>
|
|
||||||
<div v-if="authType === 'api-key'">
|
|
||||||
<div class="flex border-b border-dividerLight">
|
|
||||||
<SmartEnvInput
|
|
||||||
v-model="apiKey"
|
|
||||||
placeholder="Key"
|
|
||||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex border-b border-dividerLight">
|
|
||||||
<SmartEnvInput
|
|
||||||
v-model="apiValue"
|
|
||||||
placeholder="Value"
|
|
||||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center border-b border-dividerLight">
|
|
||||||
<label class="ml-4 text-secondaryLight">
|
|
||||||
{{ $t("authorization.pass_key_by") }}
|
|
||||||
</label>
|
|
||||||
<tippy
|
|
||||||
ref="addToOptions"
|
|
||||||
interactive
|
|
||||||
trigger="click"
|
|
||||||
theme="popover"
|
|
||||||
arrow
|
|
||||||
>
|
|
||||||
<template #trigger>
|
|
||||||
<span class="select-wrapper">
|
|
||||||
<ButtonSecondary
|
|
||||||
:label="addTo || $t('state.none')"
|
|
||||||
class="pr-8 ml-2 rounded-none"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<SmartItem
|
|
||||||
:icon="
|
|
||||||
addTo === 'Headers'
|
|
||||||
? 'radio_button_checked'
|
|
||||||
: 'radio_button_unchecked'
|
|
||||||
"
|
|
||||||
:active="addTo === 'Headers'"
|
|
||||||
:label="'Headers'"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
addTo = 'Headers'
|
|
||||||
addToOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
:icon="
|
|
||||||
addTo === 'Query params'
|
|
||||||
? 'radio_button_checked'
|
|
||||||
: 'radio_button_unchecked'
|
|
||||||
"
|
|
||||||
:active="addTo === 'Query params'"
|
|
||||||
:label="'Query params'"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
addTo = 'Query params'
|
|
||||||
addToOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</tippy>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="sticky h-full p-4 overflow-auto bg-primary top-upperTertiaryStickyFold min-w-46 max-w-1/3 z-9"
|
class="bg-primary h-full top-upperTertiaryStickyFold min-w-46 max-w-1/3 p-4 z-9 sticky overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="pb-2 text-secondaryLight">
|
<div class="p-2">
|
||||||
{{ $t("helpers.authorization") }}
|
<div class="text-secondaryLight pb-2">
|
||||||
|
{{ $t("helpers.authorization") }}
|
||||||
|
</div>
|
||||||
|
<SmartAnchor
|
||||||
|
class="link"
|
||||||
|
:label="`${$t('authorization.learn')} \xA0 →`"
|
||||||
|
to="https://docs.hoppscotch.io/features/authorization"
|
||||||
|
blank
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="authType === 'bearer'" class="border-b border-dividerLight flex">
|
||||||
|
<div class="border-r border-dividerLight w-2/3">
|
||||||
|
<div class="border-b border-dividerLight flex">
|
||||||
|
<SmartEnvInput
|
||||||
|
v-model="bearerToken"
|
||||||
|
placeholder="Token"
|
||||||
|
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="bg-primary h-full top-upperTertiaryStickyFold min-w-46 max-w-1/3 p-4 z-9 sticky overflow-auto"
|
||||||
|
>
|
||||||
|
<div class="p-2">
|
||||||
|
<div class="text-secondaryLight pb-2">
|
||||||
|
{{ $t("helpers.authorization") }}
|
||||||
|
</div>
|
||||||
|
<SmartAnchor
|
||||||
|
class="link"
|
||||||
|
:label="`${$t('authorization.learn')} \xA0 →`"
|
||||||
|
to="https://docs.hoppscotch.io/features/authorization"
|
||||||
|
blank
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="authType === 'oauth-2'"
|
||||||
|
class="border-b border-dividerLight flex"
|
||||||
|
>
|
||||||
|
<div class="border-r border-dividerLight w-2/3">
|
||||||
|
<div class="border-b border-dividerLight flex">
|
||||||
|
<SmartEnvInput
|
||||||
|
v-model="oauth2Token"
|
||||||
|
placeholder="Token"
|
||||||
|
styles="bg-transparent flex flex-1 py-1 px-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<HttpOAuth2Authorization />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="bg-primary h-full top-upperTertiaryStickyFold min-w-46 max-w-1/3 p-4 z-9 sticky overflow-auto"
|
||||||
|
>
|
||||||
|
<div class="p-2">
|
||||||
|
<div class="text-secondaryLight pb-2">
|
||||||
|
{{ $t("helpers.authorization") }}
|
||||||
|
</div>
|
||||||
|
<SmartAnchor
|
||||||
|
class="link"
|
||||||
|
:label="`${$t('authorization.learn')} \xA0 →`"
|
||||||
|
to="https://docs.hoppscotch.io/features/authorization"
|
||||||
|
blank
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SmartAnchor
|
|
||||||
class="link"
|
|
||||||
:label="`${$t('authorization.learn')} \xA0 →`"
|
|
||||||
to="https://docs.hoppscotch.io/features/authorization"
|
|
||||||
blank
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, ref, Ref } from "@nuxtjs/composition-api"
|
import { computed, defineComponent, Ref } from "@nuxtjs/composition-api"
|
||||||
import {
|
import {
|
||||||
HoppRESTAuthBasic,
|
HoppRESTAuthBasic,
|
||||||
HoppRESTAuthBearer,
|
HoppRESTAuthBearer,
|
||||||
HoppRESTAuthOAuth2,
|
HoppRESTAuthOAuth2,
|
||||||
HoppRESTAuthAPIKey,
|
|
||||||
} from "@hoppscotch/data"
|
} from "@hoppscotch/data"
|
||||||
import { pluckRef, useStream } from "~/helpers/utils/composables"
|
import { pluckRef, useStream } from "~/helpers/utils/composables"
|
||||||
import { restAuth$, setRESTAuth } from "~/newstore/RESTSession"
|
import { restAuth$, setRESTAuth } from "~/newstore/RESTSession"
|
||||||
@@ -297,7 +239,6 @@ export default defineComponent({
|
|||||||
if (authType.value === "basic") return "Basic Auth"
|
if (authType.value === "basic") return "Basic Auth"
|
||||||
else if (authType.value === "bearer") return "Bearer"
|
else if (authType.value === "bearer") return "Bearer"
|
||||||
else if (authType.value === "oauth-2") return "OAuth 2.0"
|
else if (authType.value === "oauth-2") return "OAuth 2.0"
|
||||||
else if (authType.value === "api-key") return "API key"
|
|
||||||
else return "None"
|
else return "None"
|
||||||
})
|
})
|
||||||
const authActive = pluckRef(auth, "authActive")
|
const authActive = pluckRef(auth, "authActive")
|
||||||
@@ -305,15 +246,6 @@ export default defineComponent({
|
|||||||
const basicPassword = pluckRef(auth as Ref<HoppRESTAuthBasic>, "password")
|
const basicPassword = pluckRef(auth as Ref<HoppRESTAuthBasic>, "password")
|
||||||
const bearerToken = pluckRef(auth as Ref<HoppRESTAuthBearer>, "token")
|
const bearerToken = pluckRef(auth as Ref<HoppRESTAuthBearer>, "token")
|
||||||
const oauth2Token = pluckRef(auth as Ref<HoppRESTAuthOAuth2>, "token")
|
const oauth2Token = pluckRef(auth as Ref<HoppRESTAuthOAuth2>, "token")
|
||||||
const apiKey = pluckRef(auth as Ref<HoppRESTAuthAPIKey>, "key")
|
|
||||||
const apiValue = pluckRef(auth as Ref<HoppRESTAuthAPIKey>, "value")
|
|
||||||
const addTo = pluckRef(auth as Ref<HoppRESTAuthAPIKey>, "addTo")
|
|
||||||
if (typeof addTo.value === "undefined") {
|
|
||||||
addTo.value = "Headers"
|
|
||||||
apiKey.value = ""
|
|
||||||
apiValue.value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
const URLExcludes = useSetting("URL_EXCLUDES")
|
const URLExcludes = useSetting("URL_EXCLUDES")
|
||||||
const clearContent = () => {
|
const clearContent = () => {
|
||||||
auth.value = {
|
auth.value = {
|
||||||
@@ -332,11 +264,6 @@ export default defineComponent({
|
|||||||
oauth2Token,
|
oauth2Token,
|
||||||
URLExcludes,
|
URLExcludes,
|
||||||
clearContent,
|
clearContent,
|
||||||
apiKey,
|
|
||||||
apiValue,
|
|
||||||
addTo,
|
|
||||||
authTypeOptions: ref<any | null>(null),
|
|
||||||
addToOptions: ref<any | null>(null),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<span class="flex items-center">
|
<span class="flex items-center">
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<span class="select-wrapper">
|
<span class="select-wrapper">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
:label="contentType || $t('state.none').toLowerCase()"
|
:label="contentType || $t('state.none').toLowerCase()"
|
||||||
class="pr-8 ml-2 rounded-none"
|
class="rounded-none ml-2 pr-8"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -50,21 +50,18 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<HttpBodyParameters v-if="contentType === 'multipart/form-data'" />
|
<HttpBodyParameters v-if="contentType === 'multipart/form-data'" />
|
||||||
<HttpURLEncodedParams
|
|
||||||
v-else-if="contentType === 'application/x-www-form-urlencoded'"
|
|
||||||
/>
|
|
||||||
<HttpRawBody v-else-if="contentType !== null" :content-type="contentType" />
|
<HttpRawBody v-else-if="contentType !== null" :content-type="contentType" />
|
||||||
<div
|
<div
|
||||||
v-if="contentType == null"
|
v-if="contentType == null"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/upload_single_file.svg`"
|
:src="`/images/states/${$colorMode.value}/upload_single_file.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.body')}`"
|
:alt="$t('empty.body')"
|
||||||
/>
|
/>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center pb-4">
|
||||||
{{ $t("empty.body") }}
|
{{ $t("empty.body") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection label="bodyParameters">
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperTertiaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperTertiaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ $t("request.body") }}
|
{{ $t("request.body") }}
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="(param, index) in workingParams"
|
v-for="(param, index) in bodyParams"
|
||||||
:key="`param-${index}`"
|
:key="`param-${index}`"
|
||||||
class="flex border-b divide-x divide-dividerLight border-dividerLight"
|
class="divide-dividerLight divide-x border-b border-dividerLight flex"
|
||||||
>
|
>
|
||||||
<SmartEnvInput
|
<SmartEnvInput
|
||||||
v-model="param.key"
|
v-model="param.key"
|
||||||
@@ -54,12 +54,13 @@
|
|||||||
/>
|
/>
|
||||||
<div v-if="param.isFile" class="file-chips-container hide-scrollbar">
|
<div v-if="param.isFile" class="file-chips-container hide-scrollbar">
|
||||||
<div class="space-x-2 file-chips-wrapper">
|
<div class="space-x-2 file-chips-wrapper">
|
||||||
<SmartFileChip
|
<SmartDeletableChip
|
||||||
v-for="(file, fileIndex) in param.value"
|
v-for="(file, fileIndex) in param.value"
|
||||||
:key="`param-${index}-file-${fileIndex}`"
|
:key="`param-${index}-file-${fileIndex}`"
|
||||||
|
@chip-delete="chipDelete(index, fileIndex)"
|
||||||
>
|
>
|
||||||
{{ file.name }}
|
{{ file.name }}
|
||||||
</SmartFileChip>
|
</SmartDeletableChip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span v-else class="flex flex-1">
|
<span v-else class="flex flex-1">
|
||||||
@@ -84,17 +85,21 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<label :for="`attachment${index}`" class="p-0">
|
<label for="attachment" class="p-0">
|
||||||
<input
|
<ButtonSecondary
|
||||||
:id="`attachment${index}`"
|
class="w-full"
|
||||||
:ref="`attachment${index}`"
|
svg="paperclip"
|
||||||
:name="`attachment${index}`"
|
@click.native="$refs.attachment[index].click()"
|
||||||
type="file"
|
|
||||||
multiple
|
|
||||||
class="p-1 transition cursor-pointer file:transition file:cursor-pointer text-secondaryLight hover:text-secondaryDark file:mr-2 file:py-1 file:px-4 file:rounded file:border-0 file:text-tiny text-tiny file:text-secondary hover:file:text-secondaryDark file:bg-primaryLight hover:file:bg-primaryDark"
|
|
||||||
@change="setRequestAttachment(index, param, $event)"
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
<input
|
||||||
|
ref="attachment"
|
||||||
|
class="input"
|
||||||
|
name="attachment"
|
||||||
|
type="file"
|
||||||
|
multiple
|
||||||
|
@change="setRequestAttachment(index, param, $event)"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -136,15 +141,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="bodyParams.length === 0"
|
v-if="bodyParams.length === 0"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/upload_single_file.svg`"
|
:src="`/images/states/${$colorMode.value}/upload_single_file.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${$t('empty.body')}`"
|
:alt="$t('empty.body')"
|
||||||
/>
|
/>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center pb-4">
|
||||||
{{ $t("empty.body") }}
|
{{ $t("empty.body") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -155,156 +160,103 @@
|
|||||||
@click.native="addBodyParam"
|
@click.native="addBodyParam"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { ref, Ref, watch } from "@nuxtjs/composition-api"
|
import { defineComponent, onMounted, Ref, watch } from "@nuxtjs/composition-api"
|
||||||
import { FormDataKeyValue } from "@hoppscotch/data"
|
import { FormDataKeyValue } from "@hoppscotch/data"
|
||||||
import isEqual from "lodash/isEqual"
|
import { pluckRef } from "~/helpers/utils/composables"
|
||||||
import { clone } from "lodash"
|
import {
|
||||||
import { pluckRef, useI18n, useToast } from "~/helpers/utils/composables"
|
addFormDataEntry,
|
||||||
import { useRESTRequestBody } from "~/newstore/RESTSession"
|
deleteAllFormDataEntries,
|
||||||
|
deleteFormDataEntry,
|
||||||
|
updateFormDataEntry,
|
||||||
|
useRESTRequestBody,
|
||||||
|
} from "~/newstore/RESTSession"
|
||||||
|
|
||||||
const t = useI18n()
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const bodyParams = pluckRef<any, any>(useRESTRequestBody(), "body") as Ref<
|
||||||
|
FormDataKeyValue[]
|
||||||
|
>
|
||||||
|
|
||||||
const toast = useToast()
|
const addBodyParam = () => {
|
||||||
|
addFormDataEntry({ key: "", value: "", active: true, isFile: false })
|
||||||
const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
|
|
||||||
|
|
||||||
const bodyParams = pluckRef<any, any>(useRESTRequestBody(), "body") as Ref<
|
|
||||||
FormDataKeyValue[]
|
|
||||||
>
|
|
||||||
|
|
||||||
// The UI representation of the parameters list (has the empty end param)
|
|
||||||
const workingParams = ref<FormDataKeyValue[]>([
|
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
isFile: false,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// Rule: Working Params always have last element is always an empty param
|
|
||||||
watch(workingParams, (paramsList) => {
|
|
||||||
if (paramsList.length > 0 && paramsList[paramsList.length - 1].key !== "") {
|
|
||||||
workingParams.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
isFile: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sync logic between params and working params
|
|
||||||
watch(
|
|
||||||
bodyParams,
|
|
||||||
(newParamsList) => {
|
|
||||||
// Sync should overwrite working params
|
|
||||||
const filteredWorkingParams = workingParams.value.filter(
|
|
||||||
(e) => e.key !== ""
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!isEqual(newParamsList, filteredWorkingParams)) {
|
|
||||||
workingParams.value = newParamsList
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(workingParams, (newWorkingParams) => {
|
|
||||||
const fixedParams = newWorkingParams.filter((e) => e.key !== "")
|
|
||||||
if (!isEqual(bodyParams.value, fixedParams)) {
|
|
||||||
bodyParams.value = fixedParams
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const addBodyParam = () => {
|
|
||||||
workingParams.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
isFile: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateBodyParam = (index: number, param: FormDataKeyValue) => {
|
|
||||||
workingParams.value = workingParams.value.map((h, i) =>
|
|
||||||
i === index ? param : h
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteBodyParam = (index: number) => {
|
|
||||||
const paramsBeforeDeletion = clone(workingParams.value)
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
paramsBeforeDeletion.length > 0 &&
|
|
||||||
index === paramsBeforeDeletion.length - 1
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (deletionToast.value) {
|
|
||||||
deletionToast.value.goAway(0)
|
|
||||||
deletionToast.value = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deletionToast.value = toast.success(`${t("state.deleted")}`, {
|
const updateBodyParam = (index: number, entry: FormDataKeyValue) => {
|
||||||
action: [
|
updateFormDataEntry(index, entry)
|
||||||
{
|
}
|
||||||
text: `${t("action.undo")}`,
|
|
||||||
onClick: (_, toastObject) => {
|
|
||||||
workingParams.value = paramsBeforeDeletion
|
|
||||||
toastObject.goAway(0)
|
|
||||||
deletionToast.value = null
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
onComplete: () => {
|
const deleteBodyParam = (index: number) => {
|
||||||
deletionToast.value = null
|
deleteFormDataEntry(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearContent = () => {
|
||||||
|
deleteAllFormDataEntries()
|
||||||
|
}
|
||||||
|
|
||||||
|
const chipDelete = (paramIndex: number, fileIndex: number) => {
|
||||||
|
const entry = bodyParams.value[paramIndex]
|
||||||
|
if (entry.isFile) {
|
||||||
|
entry.value.splice(fileIndex, 1)
|
||||||
|
if (entry.value.length === 0) {
|
||||||
|
updateFormDataEntry(paramIndex, {
|
||||||
|
...entry,
|
||||||
|
isFile: false,
|
||||||
|
value: "",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFormDataEntry(paramIndex, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setRequestAttachment = (
|
||||||
|
index: number,
|
||||||
|
entry: FormDataKeyValue,
|
||||||
|
event: InputEvent
|
||||||
|
) => {
|
||||||
|
const fileEntry: FormDataKeyValue = {
|
||||||
|
...entry,
|
||||||
|
isFile: true,
|
||||||
|
value: Array.from((event.target as HTMLInputElement).files!),
|
||||||
|
}
|
||||||
|
updateFormDataEntry(index, fileEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
bodyParams,
|
||||||
|
() => {
|
||||||
|
if (
|
||||||
|
bodyParams.value.length > 0 &&
|
||||||
|
(bodyParams.value[bodyParams.value.length - 1].key !== "" ||
|
||||||
|
bodyParams.value[bodyParams.value.length - 1].value !== "")
|
||||||
|
)
|
||||||
|
addBodyParam()
|
||||||
},
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!bodyParams.value?.length) {
|
||||||
|
addBodyParam()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
workingParams.value.splice(index, 1)
|
return {
|
||||||
}
|
bodyParams,
|
||||||
|
addBodyParam,
|
||||||
const clearContent = () => {
|
updateBodyParam,
|
||||||
// set params list to the initial state
|
deleteBodyParam,
|
||||||
workingParams.value = [
|
clearContent,
|
||||||
{
|
setRequestAttachment,
|
||||||
key: "",
|
chipDelete,
|
||||||
value: "",
|
}
|
||||||
active: true,
|
},
|
||||||
isFile: false,
|
})
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const setRequestAttachment = (
|
|
||||||
index: number,
|
|
||||||
entry: FormDataKeyValue,
|
|
||||||
event: InputEvent
|
|
||||||
) => {
|
|
||||||
// check if file exists or not
|
|
||||||
if ((event.target as HTMLInputElement).files?.length === 0) {
|
|
||||||
updateBodyParam(index, {
|
|
||||||
...entry,
|
|
||||||
isFile: false,
|
|
||||||
value: "",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileEntry: FormDataKeyValue = {
|
|
||||||
...entry,
|
|
||||||
isFile: true,
|
|
||||||
value: Array.from((event.target as HTMLInputElement).files!),
|
|
||||||
}
|
|
||||||
updateBodyParam(index, fileEntry)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -316,7 +268,8 @@ const setRequestAttachment = (
|
|||||||
|
|
||||||
.file-chips-wrapper {
|
.file-chips-wrapper {
|
||||||
@apply flex;
|
@apply flex;
|
||||||
@apply p-1;
|
@apply px-4;
|
||||||
|
@apply py-1;
|
||||||
@apply w-0;
|
@apply w-0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,56 +13,35 @@
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<span class="select-wrapper">
|
<span class="select-wrapper">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
:label="
|
:label="codegens.find((x) => x.id === codegenType).name"
|
||||||
CodegenDefinitions.find((x) => x.name === codegenType).caption
|
|
||||||
"
|
|
||||||
outline
|
outline
|
||||||
class="flex-1 pr-8"
|
class="flex-1 pr-8"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex flex-col space-y-2">
|
<SmartItem
|
||||||
<div class="sticky top-0">
|
v-for="(gen, index) in codegens"
|
||||||
<input
|
:key="`gen-${index}`"
|
||||||
v-model="searchQuery"
|
:label="gen.name"
|
||||||
type="search"
|
:info-icon="gen.id === codegenType ? 'done' : ''"
|
||||||
autocomplete="off"
|
:active-info-icon="gen.id === codegenType"
|
||||||
class="flex w-full p-4 py-2 !bg-popover input"
|
@click.native="
|
||||||
:placeholder="`${t('action.search')}`"
|
() => {
|
||||||
/>
|
codegenType = gen.id
|
||||||
</div>
|
options.tippy().hide()
|
||||||
<div class="flex flex-col">
|
}
|
||||||
<SmartItem
|
"
|
||||||
v-for="codegen in filteredCodegenDefinitions"
|
/>
|
||||||
:key="codegen.name"
|
|
||||||
:label="codegen.caption"
|
|
||||||
:info-icon="codegen.name === codegenType ? 'done' : ''"
|
|
||||||
:active-info-icon="codegen.name === codegenType"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
codegenType = codegen.name
|
|
||||||
options.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
<div class="flex justify-between flex-1">
|
<div class="flex flex-1 justify-between">
|
||||||
<label for="generatedCode" class="p-4">
|
<label for="generatedCode" class="p-4">
|
||||||
{{ t("request.generated_code") }}
|
{{ t("request.generated_code") }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="errorState"
|
v-if="codegenType"
|
||||||
class="bg-primaryLight rounded font-mono w-full py-2 px-4 text-red-400 overflow-auto whitespace-normal"
|
|
||||||
>
|
|
||||||
{{ t("error.something_went_wrong") }}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else-if="codegenType"
|
|
||||||
ref="generatedCode"
|
ref="generatedCode"
|
||||||
class="border rounded border-dividerLight"
|
class="border border-dividerLight rounded"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -84,22 +63,13 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from "@nuxtjs/composition-api"
|
import { computed, ref, watch } from "@nuxtjs/composition-api"
|
||||||
import * as O from "fp-ts/Option"
|
import { codegens, generateCodegenContext } from "~/helpers/codegen/codegen"
|
||||||
import { makeRESTRequest } from "@hoppscotch/data"
|
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import {
|
import { getEffectiveRESTRequest } from "~/helpers/utils/EffectiveURL"
|
||||||
getEffectiveRESTRequest,
|
import { getCurrentEnvironment } from "~/newstore/environments"
|
||||||
resolvesEnvsInBody,
|
|
||||||
} from "~/helpers/utils/EffectiveURL"
|
|
||||||
import { Environment, getAggregateEnvs } from "~/newstore/environments"
|
|
||||||
import { getRESTRequest } from "~/newstore/RESTSession"
|
import { getRESTRequest } from "~/newstore/RESTSession"
|
||||||
import { useI18n, useToast } from "~/helpers/utils/composables"
|
import { useI18n, useToast } from "~/helpers/utils/composables"
|
||||||
import {
|
|
||||||
CodegenDefinitions,
|
|
||||||
CodegenName,
|
|
||||||
generateCode,
|
|
||||||
} from "~/helpers/new-codegen"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -116,44 +86,18 @@ const toast = useToast()
|
|||||||
const options = ref<any | null>(null)
|
const options = ref<any | null>(null)
|
||||||
|
|
||||||
const request = ref(getRESTRequest())
|
const request = ref(getRESTRequest())
|
||||||
const codegenType = ref<CodegenName>("shell-curl")
|
const codegenType = ref("curl")
|
||||||
const copyIcon = ref("copy")
|
const copyIcon = ref("copy")
|
||||||
const errorState = ref(false)
|
|
||||||
|
|
||||||
const requestCode = computed(() => {
|
const requestCode = computed(() => {
|
||||||
const aggregateEnvs = getAggregateEnvs()
|
const effectiveRequest = getEffectiveRESTRequest(
|
||||||
const env: Environment = {
|
request.value as any,
|
||||||
name: "Env",
|
getCurrentEnvironment()
|
||||||
variables: aggregateEnvs,
|
|
||||||
}
|
|
||||||
const effectiveRequest = getEffectiveRESTRequest(request.value, env)
|
|
||||||
|
|
||||||
if (!props.show) return ""
|
|
||||||
|
|
||||||
const result = generateCode(
|
|
||||||
codegenType.value,
|
|
||||||
makeRESTRequest({
|
|
||||||
...effectiveRequest,
|
|
||||||
body: resolvesEnvsInBody(effectiveRequest.body, env),
|
|
||||||
headers: effectiveRequest.effectiveFinalHeaders.map((header) => ({
|
|
||||||
...header,
|
|
||||||
active: true,
|
|
||||||
})),
|
|
||||||
params: effectiveRequest.effectiveFinalParams.map((param) => ({
|
|
||||||
...param,
|
|
||||||
active: true,
|
|
||||||
})),
|
|
||||||
endpoint: effectiveRequest.effectiveFinalURL,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (O.isSome(result)) {
|
return codegens
|
||||||
errorState.value = false
|
.find((x) => x.id === codegenType.value)!
|
||||||
return result.value
|
.generator(generateCodegenContext(effectiveRequest))
|
||||||
} else {
|
|
||||||
errorState.value = true
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const generatedCode = ref<any | null>(null)
|
const generatedCode = ref<any | null>(null)
|
||||||
@@ -165,7 +109,6 @@ useCodemirror(generatedCode, requestCode, {
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -185,12 +128,4 @@ const copyRequestCode = () => {
|
|||||||
toast.success(`${t("state.copied_to_clipboard")}`)
|
toast.success(`${t("state.copied_to_clipboard")}`)
|
||||||
setTimeout(() => (copyIcon.value = "copy"), 1000)
|
setTimeout(() => (copyIcon.value = "copy"), 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchQuery = ref("")
|
|
||||||
|
|
||||||
const filteredCodegenDefinitions = computed(() => {
|
|
||||||
return CodegenDefinitions.filter((obj) =>
|
|
||||||
Object.values(obj).some((val) => val.includes(searchQuery.value))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection label="headers">
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("request.header_list") }}
|
{{ t("request.header_list") }}
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
<div v-if="bulkMode" ref="bulkEditor"></div>
|
<div v-if="bulkMode" ref="bulkEditor"></div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div
|
<div
|
||||||
v-for="(header, index) in workingHeaders"
|
v-for="(header, index) in headers$"
|
||||||
:key="`header-${index}`"
|
:key="`header-${index}`"
|
||||||
class="flex border-b divide-x divide-dividerLight border-dividerLight"
|
class="divide-dividerLight divide-x border-b border-dividerLight flex"
|
||||||
>
|
>
|
||||||
<SmartAutoComplete
|
<SmartAutoComplete
|
||||||
:placeholder="`${t('count.header', { count: index + 1 })}`"
|
:placeholder="`${t('count.header', { count: index + 1 })}`"
|
||||||
@@ -106,7 +106,9 @@
|
|||||||
updateHeader(index, {
|
updateHeader(index, {
|
||||||
key: header.key,
|
key: header.key,
|
||||||
value: header.value,
|
value: header.value,
|
||||||
active: !header.active,
|
active: header.hasOwnProperty('active')
|
||||||
|
? !header.active
|
||||||
|
: false,
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
@@ -122,16 +124,16 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workingHeaders.length === 0"
|
v-if="headers$.length === 0"
|
||||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/add_category.svg`"
|
:src="`/images/states/${$colorMode.value}/add_category.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${t('empty.headers')}`"
|
:alt="`${t('empty.headers')}`"
|
||||||
/>
|
/>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center pb-4">
|
||||||
{{ t("empty.headers") }}
|
{{ t("empty.headers") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -143,28 +145,36 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Ref, ref, watch } from "@nuxtjs/composition-api"
|
import { onBeforeUpdate, ref, watch } from "@nuxtjs/composition-api"
|
||||||
import isEqual from "lodash/isEqual"
|
|
||||||
import clone from "lodash/clone"
|
|
||||||
import { HoppRESTHeader } from "@hoppscotch/data"
|
import { HoppRESTHeader } from "@hoppscotch/data"
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
import { restHeaders$, setRESTHeaders } from "~/newstore/RESTSession"
|
import {
|
||||||
|
addRESTHeader,
|
||||||
|
deleteAllRESTHeaders,
|
||||||
|
deleteRESTHeader,
|
||||||
|
restHeaders$,
|
||||||
|
setRESTHeaders,
|
||||||
|
updateRESTHeader,
|
||||||
|
} from "~/newstore/RESTSession"
|
||||||
import { commonHeaders } from "~/helpers/headers"
|
import { commonHeaders } from "~/helpers/headers"
|
||||||
import { useI18n, useStream, useToast } from "~/helpers/utils/composables"
|
import {
|
||||||
|
useReadonlyStream,
|
||||||
|
useI18n,
|
||||||
|
useToast,
|
||||||
|
} from "~/helpers/utils/composables"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
const bulkMode = ref(false)
|
const bulkMode = ref(false)
|
||||||
const bulkHeaders = ref("")
|
const bulkHeaders = ref("")
|
||||||
const bulkEditor = ref<any | null>(null)
|
const bulkEditor = ref<any | null>(null)
|
||||||
|
|
||||||
const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
|
|
||||||
|
|
||||||
useCodemirror(bulkEditor, bulkHeaders, {
|
useCodemirror(bulkEditor, bulkHeaders, {
|
||||||
extendedEditorConfig: {
|
extendedEditorConfig: {
|
||||||
mode: "text/x-yaml",
|
mode: "text/x-yaml",
|
||||||
@@ -172,168 +182,96 @@ useCodemirror(bulkEditor, bulkHeaders, {
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// The functional headers list (the headers actually in the system)
|
|
||||||
const headers = useStream(restHeaders$, [], setRESTHeaders) as Ref<
|
|
||||||
HoppRESTHeader[]
|
|
||||||
>
|
|
||||||
|
|
||||||
// The UI representation of the headers list (has the empty end header)
|
|
||||||
const workingHeaders = ref<HoppRESTHeader[]>([
|
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// Rule: Working Headers always have one empty header or the last element is always an empty header
|
|
||||||
watch(workingHeaders, (headersList) => {
|
|
||||||
if (
|
|
||||||
headersList.length > 0 &&
|
|
||||||
headersList[headersList.length - 1].key !== ""
|
|
||||||
) {
|
|
||||||
workingHeaders.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sync logic between headers and working headers
|
|
||||||
watch(
|
|
||||||
headers,
|
|
||||||
(newHeadersList) => {
|
|
||||||
// Sync should overwrite working headers
|
|
||||||
const filteredWorkingHeaders = workingHeaders.value.filter(
|
|
||||||
(e) => e.key !== ""
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!isEqual(newHeadersList, filteredWorkingHeaders)) {
|
|
||||||
workingHeaders.value = newHeadersList
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(workingHeaders, (newWorkingHeaders) => {
|
|
||||||
const fixedHeaders = newWorkingHeaders.filter((e) => e.key !== "")
|
|
||||||
if (!isEqual(headers.value, fixedHeaders)) {
|
|
||||||
headers.value = fixedHeaders
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Bulk Editor Syncing with Working Headers
|
|
||||||
watch(bulkHeaders, () => {
|
watch(bulkHeaders, () => {
|
||||||
try {
|
try {
|
||||||
const transformation = bulkHeaders.value
|
const transformation = bulkHeaders.value.split("\n").map((item) => ({
|
||||||
.split("\n")
|
key: item.substring(0, item.indexOf(":")).trim().replace(/^\/\//, ""),
|
||||||
.filter((x) => x.trim().length > 0 && x.includes(":"))
|
value: item.substring(item.indexOf(":") + 1).trim(),
|
||||||
.map((item) => ({
|
active: !item.trim().startsWith("//"),
|
||||||
key: item.substring(0, item.indexOf(":")).trimLeft().replace(/^#/, ""),
|
|
||||||
value: item.substring(item.indexOf(":") + 1).trimLeft(),
|
|
||||||
active: !item.trim().startsWith("#"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const filteredHeaders = workingHeaders.value.filter((x) => x.key !== "")
|
|
||||||
|
|
||||||
if (!isEqual(filteredHeaders, transformation)) {
|
|
||||||
workingHeaders.value = transformation
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`${t("error.something_went_wrong")}`)
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(workingHeaders, (newHeadersList) => {
|
|
||||||
// If we are in bulk mode, don't apply direct changes
|
|
||||||
if (bulkMode.value) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentBulkHeaders = bulkHeaders.value.split("\n").map((item) => ({
|
|
||||||
key: item.substring(0, item.indexOf(":")).trimLeft().replace(/^#/, ""),
|
|
||||||
value: item.substring(item.indexOf(":") + 1).trimLeft(),
|
|
||||||
active: !item.trim().startsWith("#"),
|
|
||||||
}))
|
}))
|
||||||
|
setRESTHeaders(transformation as HoppRESTHeader[])
|
||||||
const filteredHeaders = newHeadersList.filter((x) => x.key !== "")
|
|
||||||
|
|
||||||
if (!isEqual(currentBulkHeaders, filteredHeaders)) {
|
|
||||||
bulkHeaders.value = filteredHeaders
|
|
||||||
.map((header) => {
|
|
||||||
return `${header.active ? "" : "#"}${header.key}: ${header.value}`
|
|
||||||
})
|
|
||||||
.join("\n")
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error(`${t("error.something_went_wrong")}`)
|
toast.error(`${t("error.something_went_wrong")}`)
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const addHeader = () => {
|
const headers$ = useReadonlyStream(restHeaders$, [])
|
||||||
workingHeaders.value.push({
|
|
||||||
key: "",
|
watch(
|
||||||
value: "",
|
headers$,
|
||||||
active: true,
|
(newValue) => {
|
||||||
})
|
if (!bulkMode.value)
|
||||||
|
if (
|
||||||
|
(newValue[newValue.length - 1]?.key !== "" ||
|
||||||
|
newValue[newValue.length - 1]?.value !== "") &&
|
||||||
|
newValue.length
|
||||||
|
)
|
||||||
|
addHeader()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onBeforeUpdate(() => editBulkHeadersLine(-1, null))
|
||||||
|
|
||||||
|
const editBulkHeadersLine = (index: number, item?: HoppRESTHeader | null) => {
|
||||||
|
const headers = headers$.value
|
||||||
|
|
||||||
|
bulkHeaders.value = headers
|
||||||
|
.reduce((all, header, pIndex) => {
|
||||||
|
const current =
|
||||||
|
index === pIndex && item != null
|
||||||
|
? `${item.active ? "" : "//"}${item.key}: ${item.value}`
|
||||||
|
: `${header.active ? "" : "//"}${header.key}: ${header.value}`
|
||||||
|
return [...all, current]
|
||||||
|
}, [])
|
||||||
|
.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateHeader = (index: number, header: HoppRESTHeader) => {
|
const clearBulkEditor = () => {
|
||||||
workingHeaders.value = workingHeaders.value.map((h, i) =>
|
bulkHeaders.value = ""
|
||||||
i === index ? header : h
|
}
|
||||||
)
|
|
||||||
|
const addHeader = () => {
|
||||||
|
const empty = { key: "", value: "", active: true }
|
||||||
|
const index = headers$.value.length
|
||||||
|
|
||||||
|
addRESTHeader(empty)
|
||||||
|
editBulkHeadersLine(index, empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateHeader = (index: number, item: HoppRESTHeader) => {
|
||||||
|
updateRESTHeader(index, item)
|
||||||
|
editBulkHeadersLine(index, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteHeader = (index: number) => {
|
const deleteHeader = (index: number) => {
|
||||||
const headersBeforeDeletion = clone(workingHeaders.value)
|
const headersBeforeDeletion = headers$.value
|
||||||
|
|
||||||
if (
|
deleteRESTHeader(index)
|
||||||
!(
|
editBulkHeadersLine(index, null)
|
||||||
headersBeforeDeletion.length > 0 &&
|
|
||||||
index === headersBeforeDeletion.length - 1
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (deletionToast.value) {
|
|
||||||
deletionToast.value.goAway(0)
|
|
||||||
deletionToast.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
deletionToast.value = toast.success(`${t("state.deleted")}`, {
|
const deletedItem = headersBeforeDeletion[index]
|
||||||
|
if (deletedItem.key || deletedItem.value) {
|
||||||
|
toast.success(`${t("state.deleted")}`, {
|
||||||
action: [
|
action: [
|
||||||
{
|
{
|
||||||
text: `${t("action.undo")}`,
|
text: `${t("action.undo")}`,
|
||||||
onClick: (_, toastObject) => {
|
onClick: (_, toastObject) => {
|
||||||
workingHeaders.value = headersBeforeDeletion
|
setRESTHeaders(headersBeforeDeletion as HoppRESTHeader[])
|
||||||
|
editBulkHeadersLine(index, deletedItem)
|
||||||
toastObject.goAway(0)
|
toastObject.goAway(0)
|
||||||
deletionToast.value = null
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
onComplete: () => {
|
|
||||||
deletionToast.value = null
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
workingHeaders.value.splice(index, 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearContent = () => {
|
const clearContent = () => {
|
||||||
// set headers list to the initial state
|
deleteAllRESTHeaders()
|
||||||
workingHeaders.value = [
|
clearBulkEditor()
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
bulkHeaders.value = ""
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<SmartModal v-if="show" :title="`${t('import.curl')}`" @close="hideModal">
|
<SmartModal v-if="show" :title="`${t('import.curl')}`" @close="hideModal">
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="px-2 h-46">
|
<div class="flex flex-col px-2">
|
||||||
<div
|
<div ref="curlEditor" class="border border-dividerLight rounded"></div>
|
||||||
ref="curlEditor"
|
|
||||||
class="h-full border rounded border-dividerLight"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="flex">
|
<span class="flex">
|
||||||
<ButtonPrimary
|
<ButtonPrimary
|
||||||
ref="importButton"
|
|
||||||
:label="`${t('import.title')}`"
|
:label="`${t('import.title')}`"
|
||||||
@click.native="handleImport"
|
@click.native="handleImport"
|
||||||
/>
|
/>
|
||||||
@@ -25,7 +21,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from "@nuxtjs/composition-api"
|
import { ref } from "@nuxtjs/composition-api"
|
||||||
import {
|
import {
|
||||||
HoppRESTHeader,
|
HoppRESTHeader,
|
||||||
HoppRESTParam,
|
HoppRESTParam,
|
||||||
@@ -44,8 +40,6 @@ const curl = ref("")
|
|||||||
|
|
||||||
const curlEditor = ref<any | null>(null)
|
const curlEditor = ref<any | null>(null)
|
||||||
|
|
||||||
const props = defineProps<{ show: boolean; text: string }>()
|
|
||||||
|
|
||||||
useCodemirror(curlEditor, curl, {
|
useCodemirror(curlEditor, curl, {
|
||||||
extendedEditorConfig: {
|
extendedEditorConfig: {
|
||||||
mode: "application/x-sh",
|
mode: "application/x-sh",
|
||||||
@@ -53,18 +47,9 @@ useCodemirror(curlEditor, curl, {
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
defineProps<{ show: boolean }>()
|
||||||
() => props.show,
|
|
||||||
() => {
|
|
||||||
if (props.show) {
|
|
||||||
curl.value = props.text.toString()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: false }
|
|
||||||
)
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "hide-modal"): void
|
(e: "hide-modal"): void
|
||||||
@@ -84,7 +69,6 @@ const handleImport = () => {
|
|||||||
const endpoint = origin + pathname
|
const endpoint = origin + pathname
|
||||||
const headers: HoppRESTHeader[] = []
|
const headers: HoppRESTHeader[] = []
|
||||||
const params: HoppRESTParam[] = []
|
const params: HoppRESTParam[] = []
|
||||||
const body = parsedCurl.body
|
|
||||||
if (parsedCurl.query) {
|
if (parsedCurl.query) {
|
||||||
for (const key of Object.keys(parsedCurl.query)) {
|
for (const key of Object.keys(parsedCurl.query)) {
|
||||||
const val = parsedCurl.query[key]!
|
const val = parsedCurl.query[key]!
|
||||||
@@ -115,7 +99,6 @@ const handleImport = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const method = parsedCurl.method.toUpperCase()
|
const method = parsedCurl.method.toUpperCase()
|
||||||
|
|
||||||
setRESTRequest(
|
setRESTRequest(
|
||||||
@@ -133,7 +116,7 @@ const handleImport = () => {
|
|||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
body,
|
body: "",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex border-b border-dividerLight">
|
<div class="border-b border-dividerLight flex">
|
||||||
<input
|
<input
|
||||||
id="oidcDiscoveryURL"
|
id="oidcDiscoveryURL"
|
||||||
v-model="oidcDiscoveryURL"
|
v-model="oidcDiscoveryURL"
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
placeholder="OpenID Connect Discovery URL"
|
placeholder="OpenID Connect Discovery URL"
|
||||||
name="oidcDiscoveryURL"
|
name="oidcDiscoveryURL"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex border-b border-dividerLight">
|
<div class="border-b border-dividerLight flex">
|
||||||
<input
|
<input
|
||||||
id="authURL"
|
id="authURL"
|
||||||
v-model="authURL"
|
v-model="authURL"
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
placeholder="Authentication URL"
|
placeholder="Authentication URL"
|
||||||
name="authURL"
|
name="authURL"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex border-b border-dividerLight">
|
<div class="border-b border-dividerLight flex">
|
||||||
<input
|
<input
|
||||||
id="accessTokenURL"
|
id="accessTokenURL"
|
||||||
v-model="accessTokenURL"
|
v-model="accessTokenURL"
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
placeholder="Access Token URL"
|
placeholder="Access Token URL"
|
||||||
name="accessTokenURL"
|
name="accessTokenURL"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex border-b border-dividerLight">
|
<div class="border-b border-dividerLight flex">
|
||||||
<input
|
<input
|
||||||
id="clientID"
|
id="clientID"
|
||||||
v-model="clientID"
|
v-model="clientID"
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
placeholder="Client ID"
|
placeholder="Client ID"
|
||||||
name="clientID"
|
name="clientID"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex border-b border-dividerLight">
|
<div class="border-b border-dividerLight flex">
|
||||||
<input
|
<input
|
||||||
id="scope"
|
id="scope"
|
||||||
v-model="scope"
|
v-model="scope"
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
placeholder="Scope"
|
placeholder="Scope"
|
||||||
name="scope"
|
name="scope"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection label="parameters">
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("request.parameter_list") }}
|
{{ t("request.parameter_list") }}
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
<div v-if="bulkMode" ref="bulkEditor"></div>
|
<div v-if="bulkMode" ref="bulkEditor"></div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div
|
<div
|
||||||
v-for="(param, index) in workingParams"
|
v-for="(param, index) in params$"
|
||||||
:key="`param-${index}`"
|
:key="`param-${index}`"
|
||||||
class="flex border-b divide-x divide-dividerLight border-dividerLight"
|
class="divide-dividerLight divide-x border-b border-dividerLight flex"
|
||||||
>
|
>
|
||||||
<SmartEnvInput
|
<SmartEnvInput
|
||||||
v-model="param.key"
|
v-model="param.key"
|
||||||
@@ -117,16 +117,16 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workingParams.length === 0"
|
v-if="params$.length === 0"
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/add_files.svg`"
|
:src="`/images/states/${$colorMode.value}/add_files.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${t('empty.parameters')}`"
|
:alt="`${t('empty.parameters')}`"
|
||||||
/>
|
/>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center pb-4">
|
||||||
{{ t("empty.parameters") }}
|
{{ t("empty.parameters") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -138,17 +138,26 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from "@nuxtjs/composition-api"
|
import { ref, watch, onBeforeUpdate } from "@nuxtjs/composition-api"
|
||||||
import { HoppRESTParam } from "@hoppscotch/data"
|
import { HoppRESTParam } from "@hoppscotch/data"
|
||||||
import isEqual from "lodash/isEqual"
|
|
||||||
import clone from "lodash/clone"
|
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
import { useI18n, useToast, useStream } from "~/helpers/utils/composables"
|
import {
|
||||||
import { restParams$, setRESTParams } from "~/newstore/RESTSession"
|
useReadonlyStream,
|
||||||
|
useI18n,
|
||||||
|
useToast,
|
||||||
|
} from "~/helpers/utils/composables"
|
||||||
|
import {
|
||||||
|
restParams$,
|
||||||
|
addRESTParam,
|
||||||
|
updateRESTParam,
|
||||||
|
deleteRESTParam,
|
||||||
|
deleteAllRESTParams,
|
||||||
|
setRESTParams,
|
||||||
|
} from "~/newstore/RESTSession"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -157,7 +166,19 @@ const toast = useToast()
|
|||||||
const bulkMode = ref(false)
|
const bulkMode = ref(false)
|
||||||
const bulkParams = ref("")
|
const bulkParams = ref("")
|
||||||
|
|
||||||
const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
|
watch(bulkParams, () => {
|
||||||
|
try {
|
||||||
|
const transformation = bulkParams.value.split("\n").map((item) => ({
|
||||||
|
key: item.substring(0, item.indexOf(":")).trim().replace(/^\/\//, ""),
|
||||||
|
value: item.substring(item.indexOf(":") + 1).trim(),
|
||||||
|
active: !item.trim().startsWith("//"),
|
||||||
|
}))
|
||||||
|
setRESTParams(transformation as HoppRESTParam[])
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(`${t("error.something_went_wrong")}`)
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const bulkEditor = ref<any | null>(null)
|
const bulkEditor = ref<any | null>(null)
|
||||||
|
|
||||||
@@ -168,163 +189,82 @@ useCodemirror(bulkEditor, bulkParams, {
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// The functional parameters list (the parameters actually applied to the session)
|
const params$ = useReadonlyStream(restParams$, [])
|
||||||
const params = useStream(restParams$, [], setRESTParams)
|
|
||||||
|
|
||||||
// The UI representation of the parameters list (has the empty end param)
|
|
||||||
const workingParams = ref<HoppRESTParam[]>([
|
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// Rule: Working Params always have last element is always an empty param
|
|
||||||
watch(workingParams, (paramsList) => {
|
|
||||||
if (paramsList.length > 0 && paramsList[paramsList.length - 1].key !== "") {
|
|
||||||
workingParams.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sync logic between params and working params
|
|
||||||
watch(
|
watch(
|
||||||
params,
|
params$,
|
||||||
(newParamsList) => {
|
(newValue) => {
|
||||||
// Sync should overwrite working params
|
if (!bulkMode.value)
|
||||||
const filteredWorkingParams = workingParams.value.filter(
|
if (
|
||||||
(e) => e.key !== ""
|
(newValue[newValue.length - 1]?.key !== "" ||
|
||||||
)
|
newValue[newValue.length - 1]?.value !== "") &&
|
||||||
|
newValue.length
|
||||||
if (!isEqual(newParamsList, filteredWorkingParams)) {
|
)
|
||||||
workingParams.value = newParamsList
|
addParam()
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(workingParams, (newWorkingParams) => {
|
onBeforeUpdate(() => editBulkParamsLine(-1, null))
|
||||||
const fixedParams = newWorkingParams.filter((e) => e.key !== "")
|
|
||||||
if (!isEqual(params.value, fixedParams)) {
|
|
||||||
params.value = fixedParams
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Bulk Editor Syncing with Working Params
|
const editBulkParamsLine = (index: number, item?: HoppRESTParam | null) => {
|
||||||
watch(bulkParams, () => {
|
const params = params$.value
|
||||||
try {
|
|
||||||
const transformation = bulkParams.value
|
|
||||||
.split("\n")
|
|
||||||
.filter((x) => x.trim().length > 0 && x.includes(":"))
|
|
||||||
.map((item) => ({
|
|
||||||
key: item.substring(0, item.indexOf(":")).trimLeft().replace(/^#/, ""),
|
|
||||||
value: item.substring(item.indexOf(":") + 1).trimLeft(),
|
|
||||||
active: !item.trim().startsWith("#"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const filteredParams = workingParams.value.filter((x) => x.key !== "")
|
bulkParams.value = params
|
||||||
|
.reduce((all, param, pIndex) => {
|
||||||
if (!isEqual(filteredParams, transformation)) {
|
const current =
|
||||||
workingParams.value = transformation
|
index === pIndex && item != null
|
||||||
}
|
? `${item.active ? "" : "//"}${item.key}: ${item.value}`
|
||||||
} catch (e) {
|
: `${param.active ? "" : "//"}${param.key}: ${param.value}`
|
||||||
toast.error(`${t("error.something_went_wrong")}`)
|
return [...all, current]
|
||||||
console.error(e)
|
}, [])
|
||||||
}
|
.join("\n")
|
||||||
})
|
|
||||||
|
|
||||||
watch(workingParams, (newParamsList) => {
|
|
||||||
// If we are in bulk mode, don't apply direct changes
|
|
||||||
if (bulkMode.value) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentBulkParams = bulkParams.value.split("\n").map((item) => ({
|
|
||||||
key: item.substring(0, item.indexOf(":")).trimLeft().replace(/^#/, ""),
|
|
||||||
value: item.substring(item.indexOf(":") + 1).trimLeft(),
|
|
||||||
active: !item.trim().startsWith("#"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const filteredParams = newParamsList.filter((x) => x.key !== "")
|
|
||||||
|
|
||||||
if (!isEqual(currentBulkParams, filteredParams)) {
|
|
||||||
bulkParams.value = filteredParams
|
|
||||||
.map((param) => {
|
|
||||||
return `${param.active ? "" : "#"}${param.key}: ${param.value}`
|
|
||||||
})
|
|
||||||
.join("\n")
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`${t("error.something_went_wrong")}`)
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const addParam = () => {
|
|
||||||
workingParams.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateParam = (index: number, param: HoppRESTParam) => {
|
const clearBulkEditor = () => {
|
||||||
workingParams.value = workingParams.value.map((h, i) =>
|
bulkParams.value = ""
|
||||||
i === index ? param : h
|
}
|
||||||
)
|
|
||||||
|
const addParam = () => {
|
||||||
|
const empty = { key: "", value: "", active: true }
|
||||||
|
const index = params$.value.length
|
||||||
|
|
||||||
|
addRESTParam(empty)
|
||||||
|
editBulkParamsLine(index, empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateParam = (index: number, item: HoppRESTParam) => {
|
||||||
|
updateRESTParam(index, item)
|
||||||
|
editBulkParamsLine(index, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteParam = (index: number) => {
|
const deleteParam = (index: number) => {
|
||||||
const paramsBeforeDeletion = clone(workingParams.value)
|
const parametersBeforeDeletion = params$.value
|
||||||
|
|
||||||
if (
|
deleteRESTParam(index)
|
||||||
!(
|
editBulkParamsLine(index, null)
|
||||||
paramsBeforeDeletion.length > 0 &&
|
|
||||||
index === paramsBeforeDeletion.length - 1
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (deletionToast.value) {
|
|
||||||
deletionToast.value.goAway(0)
|
|
||||||
deletionToast.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
deletionToast.value = toast.success(`${t("state.deleted")}`, {
|
const deletedItem = parametersBeforeDeletion[index]
|
||||||
|
if (deletedItem.key || deletedItem.value) {
|
||||||
|
toast.success(`${t("state.deleted")}`, {
|
||||||
action: [
|
action: [
|
||||||
{
|
{
|
||||||
text: `${t("action.undo")}`,
|
text: `${t("action.undo")}`,
|
||||||
onClick: (_, toastObject) => {
|
onClick: (_, toastObject) => {
|
||||||
workingParams.value = paramsBeforeDeletion
|
setRESTParams(parametersBeforeDeletion as HoppRESTParam[])
|
||||||
|
editBulkParamsLine(index, deletedItem)
|
||||||
toastObject.goAway(0)
|
toastObject.goAway(0)
|
||||||
deletionToast.value = null
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
onComplete: () => {
|
|
||||||
deletionToast.value = null
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
workingParams.value.splice(index, 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearContent = () => {
|
const clearContent = () => {
|
||||||
// set params list to the initial state
|
deleteAllRESTParams()
|
||||||
workingParams.value = [
|
clearBulkEditor()
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
bulkParams.value = ""
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection id="script" :label="`${t('preRequest.script')}`">
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("preRequest.javascript_code") }}
|
{{ t("preRequest.javascript_code") }}
|
||||||
@@ -29,14 +29,14 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex border-b border-dividerLight">
|
<div class="border-b border-dividerLight flex">
|
||||||
<div class="w-2/3 border-r border-dividerLight">
|
<div class="border-r border-dividerLight w-2/3">
|
||||||
<div ref="preRrequestEditor" class="h-full"></div>
|
<div ref="preRrequestEditor"></div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="sticky h-full p-4 overflow-auto bg-primary top-upperTertiaryStickyFold min-w-46 max-w-1/3 z-9"
|
class="bg-primary h-full top-upperTertiaryStickyFold min-w-46 max-w-1/3 p-4 z-9 sticky overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="pb-2 text-secondaryLight">
|
<div class="text-secondaryLight pb-2">
|
||||||
{{ t("helpers.pre_request_script") }}
|
{{ t("helpers.pre_request_script") }}
|
||||||
</div>
|
</div>
|
||||||
<SmartAnchor
|
<SmartAnchor
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
to="https://docs.hoppscotch.io/features/pre-request-script"
|
to="https://docs.hoppscotch.io/features/pre-request-script"
|
||||||
blank
|
blank
|
||||||
/>
|
/>
|
||||||
<h4 class="pt-6 font-bold text-secondaryLight">
|
<h4 class="font-bold text-secondaryLight pt-6">
|
||||||
{{ t("preRequest.snippets") }}
|
{{ t("preRequest.snippets") }}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="flex flex-col pt-4">
|
<div class="flex flex-col pt-4">
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -88,7 +88,6 @@ useCodemirror(
|
|||||||
},
|
},
|
||||||
linter,
|
linter,
|
||||||
completer,
|
completer,
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperTertiaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperTertiaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("request.raw_body") }}
|
{{ t("request.raw_body") }}
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<label for="payload">
|
<label for="payload">
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="t('import.title')"
|
:title="t('import.json')"
|
||||||
svg="file-plus"
|
svg="file-plus"
|
||||||
@click.native="$refs.payload.click()"
|
@click.native="$refs.payload.click()"
|
||||||
/>
|
/>
|
||||||
@@ -57,57 +57,26 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import { computed, reactive, ref } from "@nuxtjs/composition-api"
|
||||||
computed,
|
|
||||||
reactive,
|
|
||||||
Ref,
|
|
||||||
ref,
|
|
||||||
watchEffect,
|
|
||||||
} from "@nuxtjs/composition-api"
|
|
||||||
import * as TO from "fp-ts/TaskOption"
|
|
||||||
import { pipe } from "fp-ts/function"
|
|
||||||
import { HoppRESTReqBody, ValidContentTypes } from "~/../hoppscotch-data/dist"
|
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
import { getEditorLangForMimeType } from "~/helpers/editorutils"
|
import { getEditorLangForMimeType } from "~/helpers/editorutils"
|
||||||
import { pluckRef, useI18n, useToast } from "~/helpers/utils/composables"
|
import { pluckRef, useI18n, useToast } from "~/helpers/utils/composables"
|
||||||
import { isJSONContentType } from "~/helpers/utils/contenttypes"
|
|
||||||
import { useRESTRequestBody } from "~/newstore/RESTSession"
|
import { useRESTRequestBody } from "~/newstore/RESTSession"
|
||||||
|
|
||||||
import jsonLinter from "~/helpers/editor/linting/json"
|
|
||||||
import { readFileAsText } from "~/helpers/functional/files"
|
|
||||||
|
|
||||||
type PossibleContentTypes = Exclude<
|
|
||||||
ValidContentTypes,
|
|
||||||
"multipart/form-data" | "application/x-www-form-urlencoded"
|
|
||||||
>
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
contentType: PossibleContentTypes
|
contentType: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
const rawParamsBody = pluckRef(
|
const rawParamsBody = pluckRef(useRESTRequestBody(), "body")
|
||||||
useRESTRequestBody() as Ref<
|
|
||||||
HoppRESTReqBody & {
|
|
||||||
contentType: PossibleContentTypes
|
|
||||||
}
|
|
||||||
>,
|
|
||||||
"body"
|
|
||||||
)
|
|
||||||
const prettifyIcon = ref("wand")
|
const prettifyIcon = ref("wand")
|
||||||
|
|
||||||
const rawInputEditorLang = computed(() =>
|
const rawInputEditorLang = computed(() =>
|
||||||
getEditorLangForMimeType(props.contentType)
|
getEditorLangForMimeType(props.contentType)
|
||||||
)
|
)
|
||||||
const langLinter = computed(() =>
|
|
||||||
isJSONContentType(props.contentType) ? jsonLinter : null
|
|
||||||
)
|
|
||||||
|
|
||||||
watchEffect(() => console.log(rawInputEditorLang.value))
|
|
||||||
|
|
||||||
const linewrapEnabled = ref(true)
|
const linewrapEnabled = ref(true)
|
||||||
const rawBodyParameters = ref<any | null>(null)
|
const rawBodyParameters = ref<any | null>(null)
|
||||||
|
|
||||||
@@ -118,11 +87,10 @@ useCodemirror(
|
|||||||
extendedEditorConfig: {
|
extendedEditorConfig: {
|
||||||
lineWrapping: linewrapEnabled,
|
lineWrapping: linewrapEnabled,
|
||||||
mode: rawInputEditorLang,
|
mode: rawInputEditorLang,
|
||||||
placeholder: t("request.raw_body").toString(),
|
placeholder: t("request.raw_body"),
|
||||||
},
|
},
|
||||||
linter: langLinter,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: true,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -130,32 +98,28 @@ const clearContent = () => {
|
|||||||
rawParamsBody.value = ""
|
rawParamsBody.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadPayload = async (e: InputEvent) => {
|
const uploadPayload = (e: InputEvent) => {
|
||||||
await pipe(
|
const file = e.target.files[0]
|
||||||
(e.target as HTMLInputElement).files?.[0],
|
if (file !== undefined && file !== null) {
|
||||||
TO.of,
|
const reader = new FileReader()
|
||||||
TO.chain(TO.fromPredicate((f): f is File => f !== undefined)),
|
reader.onload = ({ target }) => {
|
||||||
TO.chain(readFileAsText),
|
rawParamsBody.value = target?.result
|
||||||
|
}
|
||||||
TO.matchW(
|
reader.readAsText(file)
|
||||||
() => toast.error(`${t("action.choose_file")}`),
|
toast.success(`${t("state.file_imported")}`)
|
||||||
(result) => {
|
} else {
|
||||||
rawParamsBody.value = result
|
toast.error(`${t("action.choose_file")}`)
|
||||||
toast.success(`${t("state.file_imported")}`)
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
)()
|
|
||||||
}
|
}
|
||||||
const prettifyRequestBody = () => {
|
const prettifyRequestBody = () => {
|
||||||
try {
|
try {
|
||||||
const jsonObj = JSON.parse(rawParamsBody.value)
|
const jsonObj = JSON.parse(rawParamsBody.value)
|
||||||
rawParamsBody.value = JSON.stringify(jsonObj, null, 2)
|
rawParamsBody.value = JSON.stringify(jsonObj, null, 2)
|
||||||
prettifyIcon.value = "check"
|
prettifyIcon.value = "check"
|
||||||
|
setTimeout(() => (prettifyIcon.value = "wand"), 1000)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
prettifyIcon.value = "info"
|
|
||||||
toast.error(`${t("error.json_prettify_invalid_body")}`)
|
toast.error(`${t("error.json_prettify_invalid_body")}`)
|
||||||
}
|
}
|
||||||
setTimeout(() => (prettifyIcon.value = "wand"), 1000)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="sticky top-0 z-10 flex p-4 space-x-2 overflow-x-auto bg-primary hide-scrollbar"
|
class="bg-primary flex space-x-2 p-4 top-0 z-10 sticky overflow-x-auto hide-scrollbar"
|
||||||
>
|
>
|
||||||
<div class="flex flex-1">
|
<div class="flex flex-1">
|
||||||
<div class="relative flex">
|
<div class="flex relative">
|
||||||
<label for="method">
|
<label for="method">
|
||||||
<tippy
|
<tippy
|
||||||
ref="methodOptions"
|
ref="methodOptions"
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<span class="select-wrapper">
|
<span class="select-wrapper">
|
||||||
<input
|
<input
|
||||||
id="method"
|
id="method"
|
||||||
class="flex px-4 py-2 font-semibold border rounded-l cursor-pointer bg-primaryLight border-divider text-secondaryDark w-26 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
class="bg-primaryLight border border-divider rounded-l cursor-pointer flex font-semibold text-secondaryDark py-2 px-4 w-26 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
:value="newMethod"
|
:value="newMethod"
|
||||||
:readonly="!isCustomMethod"
|
:readonly="!isCustomMethod"
|
||||||
:placeholder="`${t('request.method')}`"
|
:placeholder="`${t('request.method')}`"
|
||||||
@@ -52,14 +52,13 @@
|
|||||||
focus-visible:bg-transparent
|
focus-visible:bg-transparent
|
||||||
"
|
"
|
||||||
@enter="newSendRequest()"
|
@enter="newSendRequest()"
|
||||||
@paste="onPasteUrl($event)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<ButtonPrimary
|
<ButtonPrimary
|
||||||
id="send"
|
id="send"
|
||||||
class="flex-1 rounded-r-none min-w-20"
|
class="rounded-r-none flex-1 min-w-20"
|
||||||
:label="`${!loading ? t('action.send') : t('action.cancel')}`"
|
:label="`${!loading ? t('action.send') : t('action.cancel')}`"
|
||||||
@click.native="!loading ? newSendRequest() : cancelRequest()"
|
@click.native="!loading ? newSendRequest() : cancelRequest()"
|
||||||
/>
|
/>
|
||||||
@@ -70,61 +69,45 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => sendTippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonPrimary class="rounded-l-none" filled svg="chevron-down" />
|
<ButtonPrimary class="rounded-l-none" filled svg="chevron-down" />
|
||||||
</template>
|
</template>
|
||||||
<div
|
<SmartItem
|
||||||
ref="sendTippyActions"
|
:label="`${t('import.curl')}`"
|
||||||
class="flex flex-col focus:outline-none"
|
svg="file-code"
|
||||||
tabindex="0"
|
@click.native="
|
||||||
@keyup.c="curl.$el.click()"
|
() => {
|
||||||
@keyup.s="show.$el.click()"
|
showCurlImportModal = !showCurlImportModal
|
||||||
@keyup.delete="clearAll.$el.click()"
|
sendOptions.tippy().hide()
|
||||||
@keyup.escape="sendOptions.tippy().hide()"
|
}
|
||||||
>
|
"
|
||||||
<SmartItem
|
/>
|
||||||
ref="curl"
|
<SmartItem
|
||||||
:label="`${t('import.curl')}`"
|
:label="`${t('show.code')}`"
|
||||||
svg="file-code"
|
svg="code-2"
|
||||||
:shortcut="['C']"
|
@click.native="
|
||||||
@click.native="
|
() => {
|
||||||
() => {
|
showCodegenModal = !showCodegenModal
|
||||||
showCurlImportModal = !showCurlImportModal
|
sendOptions.tippy().hide()
|
||||||
sendOptions.tippy().hide()
|
}
|
||||||
}
|
"
|
||||||
"
|
/>
|
||||||
/>
|
<SmartItem
|
||||||
<SmartItem
|
ref="clearAll"
|
||||||
ref="show"
|
:label="`${t('action.clear_all')}`"
|
||||||
:label="`${t('show.code')}`"
|
svg="rotate-ccw"
|
||||||
svg="code-2"
|
@click.native="
|
||||||
:shortcut="['S']"
|
() => {
|
||||||
@click.native="
|
clearContent()
|
||||||
() => {
|
sendOptions.tippy().hide()
|
||||||
showCodegenModal = !showCodegenModal
|
}
|
||||||
sendOptions.tippy().hide()
|
"
|
||||||
}
|
/>
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
ref="clearAll"
|
|
||||||
:label="`${t('action.clear_all')}`"
|
|
||||||
svg="rotate-ccw"
|
|
||||||
:shortcut="['⌫']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
clearContent()
|
|
||||||
sendOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
class="ml-2 rounded rounded-r-none"
|
class="rounded rounded-r-none ml-2"
|
||||||
:label="
|
:label="
|
||||||
windowInnerWidth.x.value >= 768 && COLUMN_LAYOUT
|
windowInnerWidth.x.value >= 768 && COLUMN_LAYOUT
|
||||||
? `${t('request.save')}`
|
? `${t('request.save')}`
|
||||||
@@ -141,7 +124,6 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
theme="popover"
|
theme="popover"
|
||||||
arrow
|
arrow
|
||||||
:on-shown="() => saveTippyActions.focus()"
|
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -160,44 +142,32 @@
|
|||||||
class="mb-2 input"
|
class="mb-2 input"
|
||||||
@keyup.enter="saveOptions.tippy().hide()"
|
@keyup.enter="saveOptions.tippy().hide()"
|
||||||
/>
|
/>
|
||||||
<div
|
<SmartItem
|
||||||
ref="saveTippyActions"
|
ref="copyRequest"
|
||||||
class="flex flex-col focus:outline-none"
|
:label="shareButtonText"
|
||||||
tabindex="0"
|
:svg="copyLinkIcon"
|
||||||
@keyup.c="copyRequestAction.$el.click()"
|
:loading="fetchingShareLink"
|
||||||
@keyup.s="saveRequestAction.$el.click()"
|
@click.native="
|
||||||
@keyup.escape="saveOptions.tippy().hide()"
|
() => {
|
||||||
>
|
copyRequest()
|
||||||
<SmartItem
|
}
|
||||||
ref="copyRequestAction"
|
"
|
||||||
:label="shareButtonText"
|
/>
|
||||||
:svg="copyLinkIcon"
|
<SmartItem
|
||||||
:loading="fetchingShareLink"
|
ref="saveRequest"
|
||||||
:shortcut="['C']"
|
:label="`${t('request.save_as')}`"
|
||||||
@click.native="
|
svg="folder-plus"
|
||||||
() => {
|
@click.native="
|
||||||
copyRequest()
|
() => {
|
||||||
}
|
showSaveRequestModal = true
|
||||||
"
|
saveOptions.tippy().hide()
|
||||||
/>
|
}
|
||||||
<SmartItem
|
"
|
||||||
ref="saveRequestAction"
|
/>
|
||||||
:label="`${t('request.save_as')}`"
|
|
||||||
svg="folder-plus"
|
|
||||||
:shortcut="['S']"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
showSaveRequestModal = true
|
|
||||||
saveOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<HttpImportCurl
|
<HttpImportCurl
|
||||||
:text="curlText"
|
|
||||||
:show="showCurlImportModal"
|
:show="showCurlImportModal"
|
||||||
@hide-modal="showCurlImportModal = false"
|
@hide-modal="showCurlImportModal = false"
|
||||||
/>
|
/>
|
||||||
@@ -269,7 +239,6 @@ const nuxt = useNuxt()
|
|||||||
const { subscribeToStream } = useStreamSubscriber()
|
const { subscribeToStream } = useStreamSubscriber()
|
||||||
|
|
||||||
const newEndpoint = useStream(restEndpoint$, "", setRESTEndpoint)
|
const newEndpoint = useStream(restEndpoint$, "", setRESTEndpoint)
|
||||||
const curlText = ref("")
|
|
||||||
const newMethod = useStream(restMethod$, "", updateRESTMethod)
|
const newMethod = useStream(restMethod$, "", updateRESTMethod)
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -284,13 +253,6 @@ const hasNavigatorShare = !!navigator.share
|
|||||||
const methodOptions = ref<any | null>(null)
|
const methodOptions = ref<any | null>(null)
|
||||||
const saveOptions = ref<any | null>(null)
|
const saveOptions = ref<any | null>(null)
|
||||||
const sendOptions = ref<any | null>(null)
|
const sendOptions = ref<any | null>(null)
|
||||||
const sendTippyActions = ref<any | null>(null)
|
|
||||||
const saveTippyActions = ref<any | null>(null)
|
|
||||||
const curl = ref<any | null>(null)
|
|
||||||
const show = ref<any | null>(null)
|
|
||||||
const clearAll = ref<any | null>(null)
|
|
||||||
const copyRequestAction = ref<any | null>(null)
|
|
||||||
const saveRequestAction = ref<any | null>(null)
|
|
||||||
|
|
||||||
// Update Nuxt Loading bar
|
// Update Nuxt Loading bar
|
||||||
watch(loading, () => {
|
watch(loading, () => {
|
||||||
@@ -345,27 +307,6 @@ const newSendRequest = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPasteUrl = (e: { event: ClipboardEvent; previousValue: string }) => {
|
|
||||||
if (!e) return
|
|
||||||
|
|
||||||
const clipboardData = e.event.clipboardData
|
|
||||||
|
|
||||||
const pastedData = clipboardData?.getData("Text")
|
|
||||||
|
|
||||||
if (!pastedData) return
|
|
||||||
|
|
||||||
if (isCURL(pastedData)) {
|
|
||||||
e.event.preventDefault()
|
|
||||||
showCurlImportModal.value = true
|
|
||||||
curlText.value = pastedData
|
|
||||||
newEndpoint.value = e.previousValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCURL(curl: string) {
|
|
||||||
return curl.includes("curl ")
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancelRequest = () => {
|
const cancelRequest = () => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
updateRESTResponse(null)
|
updateRESTResponse(null)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col flex-1">
|
<AppSection label="response">
|
||||||
<HttpResponseMeta :response="response" />
|
<HttpResponseMeta :response="response" />
|
||||||
<LensesResponseBodyRenderer
|
<LensesResponseBodyRenderer
|
||||||
v-if="!loading && hasResponse"
|
v-if="!loading && hasResponse"
|
||||||
:response="response"
|
:response="response"
|
||||||
/>
|
/>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="sticky top-0 z-10 flex items-center p-4 overflow-auto bg-primary hide-scrollbar whitespace-nowrap"
|
class="bg-primary flex p-4 top-0 z-10 sticky items-center overflow-auto hide-scrollbar whitespace-nowrap"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="response == null"
|
v-if="response == null"
|
||||||
class="flex flex-col items-center justify-center flex-1 text-secondaryLight"
|
class="flex flex-col flex-1 text-secondaryLight items-center justify-center"
|
||||||
>
|
>
|
||||||
<div class="flex pb-4 my-4 space-x-2">
|
<div class="flex space-x-2 my-4 pb-4">
|
||||||
<div class="flex flex-col items-end space-y-4 text-right">
|
<div class="flex flex-col space-y-4 text-right items-end">
|
||||||
<span class="flex items-center flex-1">
|
<span class="flex flex-1 items-center">
|
||||||
{{ t("shortcut.request.send_request") }}
|
{{ t("shortcut.request.send_request") }}
|
||||||
</span>
|
</span>
|
||||||
<span class="flex items-center flex-1">
|
<span class="flex flex-1 items-center">
|
||||||
{{ t("shortcut.general.show_all") }}
|
{{ t("shortcut.general.show_all") }}
|
||||||
</span>
|
</span>
|
||||||
<span class="flex items-center flex-1">
|
<span class="flex flex-1 items-center">
|
||||||
{{ t("shortcut.general.command_menu") }}
|
{{ t("shortcut.general.command_menu") }}
|
||||||
</span>
|
</span>
|
||||||
<span class="flex items-center flex-1">
|
<span class="flex flex-1 items-center">
|
||||||
{{ t("shortcut.general.help_menu") }}
|
{{ t("shortcut.general.help_menu") }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,71 +57,66 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="response.type === 'network_fail'"
|
v-if="response.type === 'network_fail'"
|
||||||
class="flex flex-col items-center justify-center flex-1 p-4"
|
class="flex flex-col flex-1 p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/youre_lost.svg`"
|
:src="`/images/states/${$colorMode.value}/youre_lost.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-32 h-32 my-4"
|
class="flex-col object-contain object-center h-32 my-4 w-32 inline-flex"
|
||||||
:alt="`${t('error.network_fail')}`"
|
:alt="`${t('error.network_fail')}`"
|
||||||
/>
|
/>
|
||||||
<span class="mb-2 font-semibold text-center">
|
<span class="font-semibold text-center mb-2">
|
||||||
{{ t("error.network_fail") }}
|
{{ t("error.network_fail") }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="max-w-sm mb-6 text-center whitespace-normal text-secondaryLight"
|
class="max-w-sm text-secondaryLight text-center mb-4 whitespace-normal"
|
||||||
>
|
>
|
||||||
{{ t("helpers.network_fail") }}
|
{{ t("helpers.network_fail") }}
|
||||||
</span>
|
</span>
|
||||||
<AppInterceptor class="border rounded border-dividerLight" />
|
<AppInterceptor />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="response.type === 'script_fail'"
|
v-if="response.type === 'script_fail'"
|
||||||
class="flex flex-col items-center justify-center flex-1 p-4"
|
class="flex flex-col flex-1 p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/youre_lost.svg`"
|
:src="`/images/states/${$colorMode.value}/youre_lost.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-32 h-32 my-4"
|
class="flex-col object-contain object-center h-32 my-4 w-32 inline-flex"
|
||||||
:alt="`${t('error.script_fail')}`"
|
:alt="`${t('error.script_fail')}`"
|
||||||
/>
|
/>
|
||||||
<span class="mb-2 font-semibold text-center">
|
<span class="font-semibold text-center mb-2">
|
||||||
{{ t("error.script_fail") }}
|
{{ t("error.script_fail") }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="max-w-sm mb-6 text-center whitespace-normal text-secondaryLight"
|
class="max-w-sm text-secondaryLight text-center mb-4 whitespace-normal"
|
||||||
>
|
>
|
||||||
{{ t("helpers.script_fail") }}
|
{{ t("helpers.script_fail") }}
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
class="w-full px-4 py-2 overflow-auto font-mono text-red-400 whitespace-normal rounded bg-primaryLight"
|
class="bg-primaryLight rounded font-mono w-full py-2 px-4 text-red-400 overflow-auto whitespace-normal"
|
||||||
>
|
>
|
||||||
{{ response.error.name }}: {{ response.error.message }}<br />
|
{{ response.error.name }}: {{ response.error.message }}<br />
|
||||||
{{ response.error.stack }}
|
{{ response.error.stack }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="response.type === 'success' || response.type === 'fail'"
|
v-if="response.type === 'success' || 'fail'"
|
||||||
class="flex items-center font-semibold text-tiny"
|
:class="statusCategory.className"
|
||||||
|
class="font-semibold space-x-4"
|
||||||
>
|
>
|
||||||
<div
|
<span v-if="response.statusCode">
|
||||||
:class="statusCategory.className"
|
<span class="text-secondary"> {{ t("response.status") }}: </span>
|
||||||
class="inline-flex flex-1 space-x-4"
|
{{ response.statusCode || t("state.waiting_send_request") }}
|
||||||
>
|
</span>
|
||||||
<span v-if="response.statusCode">
|
<span v-if="response.meta && response.meta.responseDuration">
|
||||||
<span class="text-secondary"> {{ t("response.status") }}: </span>
|
<span class="text-secondary"> {{ t("response.time") }}: </span>
|
||||||
{{ `${response.statusCode}\xA0 • \xA0`
|
{{ `${response.meta.responseDuration} ms` }}
|
||||||
}}{{ getStatusCodeReasonPhrase(response.statusCode) }}
|
</span>
|
||||||
</span>
|
<span v-if="response.meta && response.meta.responseSize">
|
||||||
<span v-if="response.meta && response.meta.responseDuration">
|
<span class="text-secondary"> {{ t("response.size") }}: </span>
|
||||||
<span class="text-secondary"> {{ t("response.time") }}: </span>
|
{{ `${response.meta.responseSize} B` }}
|
||||||
{{ `${response.meta.responseDuration} ms` }}
|
</span>
|
||||||
</span>
|
|
||||||
<span v-if="response.meta && response.meta.responseSize">
|
|
||||||
<span class="text-secondary"> {{ t("response.size") }}: </span>
|
|
||||||
{{ `${response.meta.responseSize} B` }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,7 +128,6 @@ import findStatusGroup from "~/helpers/findStatusGroup"
|
|||||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||||
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
||||||
import { useI18n } from "~/helpers/utils/composables"
|
import { useI18n } from "~/helpers/utils/composables"
|
||||||
import { getStatusCodeReasonPhrase } from "~/helpers/utils/statusCodes"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -145,13 +139,20 @@ const statusCategory = computed(() => {
|
|||||||
if (
|
if (
|
||||||
props.response.type === "loading" ||
|
props.response.type === "loading" ||
|
||||||
props.response.type === "network_fail" ||
|
props.response.type === "network_fail" ||
|
||||||
props.response.type === "script_fail" ||
|
props.response.type === "script_fail"
|
||||||
props.response.type === "fail"
|
|
||||||
)
|
)
|
||||||
return {
|
return ""
|
||||||
name: "error",
|
|
||||||
className: "text-red-500",
|
|
||||||
}
|
|
||||||
return findStatusGroup(props.response.statusCode)
|
return findStatusGroup(props.response.statusCode)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.shortcut-key {
|
||||||
|
@apply bg-dividerLight;
|
||||||
|
@apply rounded;
|
||||||
|
@apply ml-2;
|
||||||
|
@apply py-1;
|
||||||
|
@apply px-2;
|
||||||
|
@apply inline-flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection :label="`${t('test.results')}`">
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
testResults &&
|
testResults &&
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-lowerSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-lowerSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("test.report") }}
|
{{ t("test.report") }}
|
||||||
@@ -19,8 +19,8 @@
|
|||||||
@click.native="clearContent()"
|
@click.native="clearContent()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-b divide-y-4 divide-dividerLight border-dividerLight">
|
<div class="divide-dividerLight border-b border-dividerLight divide-y-4">
|
||||||
<div v-if="testResults.tests" class="divide-y-4 divide-dividerLight">
|
<div v-if="testResults.tests" class="divide-dividerLight divide-y-4">
|
||||||
<HttpTestResultEntry
|
<HttpTestResultEntry
|
||||||
v-for="(result, index) in testResults.tests"
|
v-for="(result, index) in testResults.tests"
|
||||||
:key="`result-${index}`"
|
:key="`result-${index}`"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="testResults.expectResults"
|
v-if="testResults.expectResults"
|
||||||
class="divide-y divide-dividerLight"
|
class="divide-dividerLight divide-y"
|
||||||
>
|
>
|
||||||
<HttpTestResultReport
|
<HttpTestResultReport
|
||||||
v-if="testResults.expectResults.length"
|
v-if="testResults.expectResults.length"
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(result, index) in testResults.expectResults"
|
v-for="(result, index) in testResults.expectResults"
|
||||||
:key="`result-${index}`"
|
:key="`result-${index}`"
|
||||||
class="flex items-center px-4 py-2"
|
class="flex py-2 px-4 items-center"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="mr-4 material-icons"
|
class="mr-4 material-icons"
|
||||||
@@ -64,18 +64,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/images/states/${$colorMode.value}/validation.svg`"
|
:src="`/images/states/${$colorMode.value}/validation.svg`"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
:alt="`${t('empty.tests')}`"
|
:alt="`${t('empty.tests')}`"
|
||||||
/>
|
/>
|
||||||
<span class="pb-2 text-center">
|
<span class="text-center pb-2">
|
||||||
{{ t("empty.tests") }}
|
{{ t("empty.tests") }}
|
||||||
</span>
|
</span>
|
||||||
<span class="pb-4 text-center">
|
<span class="text-center pb-4">
|
||||||
{{ t("helpers.tests") }}
|
{{ t("helpers.tests") }}
|
||||||
</span>
|
</span>
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
class="my-4"
|
class="my-4"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
v-if="testResults.description"
|
v-if="testResults.description"
|
||||||
class="flex items-center px-4 py-2 font-bold text-secondaryDark"
|
class="flex font-bold text-secondaryDark py-2 px-4 items-center"
|
||||||
>
|
>
|
||||||
{{ testResults.description }}
|
{{ testResults.description }}
|
||||||
</span>
|
</span>
|
||||||
<div v-if="testResults.expectResults" class="divide-y divide-dividerLight">
|
<div v-if="testResults.expectResults" class="divide-dividerLight divide-y">
|
||||||
<HttpTestResultReport
|
<HttpTestResultReport
|
||||||
v-if="testResults.expectResults.length"
|
v-if="testResults.expectResults.length"
|
||||||
:test-results="testResults"
|
:test-results="testResults"
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(result, index) in testResults.expectResults"
|
v-for="(result, index) in testResults.expectResults"
|
||||||
:key="`result-${index}`"
|
:key="`result-${index}`"
|
||||||
class="flex items-center px-4 py-2"
|
class="flex py-2 px-4 items-center"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="mr-4 material-icons"
|
class="mr-4 material-icons"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center p-2">
|
<div class="flex p-2 items-center">
|
||||||
<SmartProgressRing
|
<SmartProgressRing
|
||||||
class="text-red-500"
|
class="text-red-500"
|
||||||
:radius="16"
|
:radius="16"
|
||||||
@@ -20,16 +20,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, PropType } from "@nuxtjs/composition-api"
|
import { computed, PropType } from "@nuxtjs/composition-api"
|
||||||
import {
|
import { HoppTestResult } from "~/helpers/types/HoppTestResult"
|
||||||
HoppTestExpectResult,
|
|
||||||
HoppTestResult,
|
|
||||||
} from "~/helpers/types/HoppTestResult"
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
testResults: {
|
testResults: {
|
||||||
type: Object as PropType<HoppTestResult>,
|
type: Object as PropType<HoppTestResult>,
|
||||||
required: true,
|
required: true,
|
||||||
expectResults: [] as HoppTestExpectResult[],
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<AppSection id="script" :label="`${t('test.script')}`">
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("test.javascript_code") }}
|
{{ t("test.javascript_code") }}
|
||||||
@@ -29,14 +29,14 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex border-b border-dividerLight">
|
<div class="border-b border-dividerLight flex">
|
||||||
<div class="w-2/3 border-r border-dividerLight">
|
<div class="border-r border-dividerLight w-2/3">
|
||||||
<div ref="testScriptEditor" class="h-full"></div>
|
<div ref="testScriptEditor"></div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="sticky h-full p-4 overflow-auto bg-primary top-upperTertiaryStickyFold min-w-46 max-w-1/3 z-9"
|
class="bg-primary h-full top-upperTertiaryStickyFold min-w-46 max-w-1/3 p-4 z-9 sticky overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="pb-2 text-secondaryLight">
|
<div class="text-secondaryLight pb-2">
|
||||||
{{ t("helpers.post_request_tests") }}
|
{{ t("helpers.post_request_tests") }}
|
||||||
</div>
|
</div>
|
||||||
<SmartAnchor
|
<SmartAnchor
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
to="https://docs.hoppscotch.io/features/tests"
|
to="https://docs.hoppscotch.io/features/tests"
|
||||||
blank
|
blank
|
||||||
/>
|
/>
|
||||||
<h4 class="pt-6 font-bold text-secondaryLight">
|
<h4 class="font-bold text-secondaryLight pt-6">
|
||||||
{{ t("test.snippets") }}
|
{{ t("test.snippets") }}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="flex flex-col pt-4">
|
<div class="flex flex-col pt-4">
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -88,7 +88,6 @@ useCodemirror(
|
|||||||
},
|
},
|
||||||
linter,
|
linter,
|
||||||
completer,
|
completer,
|
||||||
environmentHighlights: false,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,355 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperTertiaryStickyFold"
|
|
||||||
>
|
|
||||||
<label class="font-semibold text-secondaryLight">
|
|
||||||
{{ t("request.body") }}
|
|
||||||
</label>
|
|
||||||
<div class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
to="https://docs.hoppscotch.io/features/body"
|
|
||||||
blank
|
|
||||||
:title="t('app.wiki')"
|
|
||||||
svg="help-circle"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.clear_all')"
|
|
||||||
svg="trash-2"
|
|
||||||
@click.native="clearContent()"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('state.bulk_mode')"
|
|
||||||
svg="edit"
|
|
||||||
:class="{ '!text-accent': bulkMode }"
|
|
||||||
@click.native="bulkMode = !bulkMode"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('add.new')"
|
|
||||||
svg="plus"
|
|
||||||
:disabled="bulkMode"
|
|
||||||
@click.native="addUrlEncodedParam"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="bulkMode" ref="bulkEditor"></div>
|
|
||||||
<div v-else>
|
|
||||||
<div
|
|
||||||
v-for="(param, index) in workingUrlEncodedParams"
|
|
||||||
:key="`param-${index}`"
|
|
||||||
class="flex border-b divide-x divide-dividerLight border-dividerLight"
|
|
||||||
>
|
|
||||||
<SmartEnvInput
|
|
||||||
v-model="param.key"
|
|
||||||
:placeholder="`${t('count.parameter', { count: index + 1 })}`"
|
|
||||||
styles="
|
|
||||||
bg-transparent
|
|
||||||
flex
|
|
||||||
flex-1
|
|
||||||
py-1
|
|
||||||
px-4
|
|
||||||
"
|
|
||||||
@change="
|
|
||||||
updateUrlEncodedParam(index, {
|
|
||||||
key: $event,
|
|
||||||
value: param.value,
|
|
||||||
active: param.active,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartEnvInput
|
|
||||||
v-model="param.value"
|
|
||||||
:placeholder="`${t('count.value', { count: index + 1 })}`"
|
|
||||||
styles="
|
|
||||||
bg-transparent
|
|
||||||
flex
|
|
||||||
flex-1
|
|
||||||
py-1
|
|
||||||
px-4
|
|
||||||
"
|
|
||||||
@change="
|
|
||||||
updateUrlEncodedParam(index, {
|
|
||||||
key: param.key,
|
|
||||||
value: $event,
|
|
||||||
active: param.active,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="
|
|
||||||
param.hasOwnProperty('active')
|
|
||||||
? param.active
|
|
||||||
? t('action.turn_off')
|
|
||||||
: t('action.turn_on')
|
|
||||||
: t('action.turn_off')
|
|
||||||
"
|
|
||||||
:svg="
|
|
||||||
param.hasOwnProperty('active')
|
|
||||||
? param.active
|
|
||||||
? 'check-circle'
|
|
||||||
: 'circle'
|
|
||||||
: 'check-circle'
|
|
||||||
"
|
|
||||||
color="green"
|
|
||||||
@click.native="
|
|
||||||
updateUrlEncodedParam(index, {
|
|
||||||
key: param.key,
|
|
||||||
value: param.value,
|
|
||||||
active: !param.active,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="t('action.remove')"
|
|
||||||
svg="trash"
|
|
||||||
color="red"
|
|
||||||
@click.native="deleteUrlEncodedParam(index)"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="workingUrlEncodedParams.length === 0"
|
|
||||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${$colorMode.value}/add_category.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
|
||||||
:alt="`${t('empty.body')}`"
|
|
||||||
/>
|
|
||||||
<span class="pb-4 text-center">
|
|
||||||
{{ t("empty.body") }}
|
|
||||||
</span>
|
|
||||||
<ButtonSecondary
|
|
||||||
filled
|
|
||||||
:label="`${t('add.new')}`"
|
|
||||||
svg="plus"
|
|
||||||
class="mb-4"
|
|
||||||
@click.native="addUrlEncodedParam"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed, Ref, ref, watch } from "@nuxtjs/composition-api"
|
|
||||||
import isEqual from "lodash/isEqual"
|
|
||||||
import clone from "lodash/clone"
|
|
||||||
import { HoppRESTReqBody } from "@hoppscotch/data"
|
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
|
||||||
import { useRESTRequestBody } from "~/newstore/RESTSession"
|
|
||||||
import { pluckRef, useI18n, useToast } from "~/helpers/utils/composables"
|
|
||||||
import {
|
|
||||||
parseRawKeyValueEntries,
|
|
||||||
rawKeyValueEntriesToString,
|
|
||||||
RawKeyValueEntry,
|
|
||||||
} from "~/helpers/rawKeyValue"
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
const bulkMode = ref(false)
|
|
||||||
const bulkUrlEncodedParams = ref("")
|
|
||||||
const bulkEditor = ref<any | null>(null)
|
|
||||||
|
|
||||||
const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
|
|
||||||
|
|
||||||
useCodemirror(bulkEditor, bulkUrlEncodedParams, {
|
|
||||||
extendedEditorConfig: {
|
|
||||||
mode: "text/x-yaml",
|
|
||||||
placeholder: `${t("state.bulk_mode_placeholder")}`,
|
|
||||||
},
|
|
||||||
linter: null,
|
|
||||||
completer: null,
|
|
||||||
environmentHighlights: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// The functional urlEncodedParams list (the urlEncodedParams actually in the system)
|
|
||||||
const urlEncodedParamsRaw = pluckRef(
|
|
||||||
useRESTRequestBody() as Ref<
|
|
||||||
HoppRESTReqBody & { contentType: "application/x-www-form-urlencoded" }
|
|
||||||
>,
|
|
||||||
"body"
|
|
||||||
)
|
|
||||||
|
|
||||||
const urlEncodedParams = computed<RawKeyValueEntry[]>({
|
|
||||||
get() {
|
|
||||||
return parseRawKeyValueEntries(urlEncodedParamsRaw.value)
|
|
||||||
},
|
|
||||||
set(newValue) {
|
|
||||||
urlEncodedParamsRaw.value = rawKeyValueEntriesToString(newValue)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// The UI representation of the urlEncodedParams list (has the empty end urlEncodedParam)
|
|
||||||
const workingUrlEncodedParams = ref<RawKeyValueEntry[]>([
|
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// Rule: Working urlEncodedParams always have one empty urlEncodedParam or the last element is always an empty urlEncodedParams
|
|
||||||
watch(workingUrlEncodedParams, (urlEncodedParamList) => {
|
|
||||||
if (
|
|
||||||
urlEncodedParamList.length > 0 &&
|
|
||||||
urlEncodedParamList[urlEncodedParamList.length - 1].key !== ""
|
|
||||||
) {
|
|
||||||
workingUrlEncodedParams.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sync logic between urlEncodedParams and working urlEncodedParams
|
|
||||||
watch(
|
|
||||||
urlEncodedParams,
|
|
||||||
(newurlEncodedParamList) => {
|
|
||||||
const filteredWorkingUrlEncodedParams =
|
|
||||||
workingUrlEncodedParams.value.filter((e) => e.key !== "")
|
|
||||||
|
|
||||||
if (!isEqual(newurlEncodedParamList, filteredWorkingUrlEncodedParams)) {
|
|
||||||
workingUrlEncodedParams.value = newurlEncodedParamList
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(workingUrlEncodedParams, (newWorkingUrlEncodedParams) => {
|
|
||||||
const fixedUrlEncodedParams = newWorkingUrlEncodedParams.filter(
|
|
||||||
(e) => e.key !== ""
|
|
||||||
)
|
|
||||||
if (!isEqual(urlEncodedParams.value, fixedUrlEncodedParams)) {
|
|
||||||
urlEncodedParams.value = fixedUrlEncodedParams
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Bulk Editor Syncing with Working urlEncodedParams
|
|
||||||
watch(bulkUrlEncodedParams, () => {
|
|
||||||
try {
|
|
||||||
const transformation = bulkUrlEncodedParams.value
|
|
||||||
.split("\n")
|
|
||||||
.filter((x) => x.trim().length > 0 && x.includes(":"))
|
|
||||||
.map((item) => ({
|
|
||||||
key: item.substring(0, item.indexOf(":")).trimLeft().replace(/^#/, ""),
|
|
||||||
value: item.substring(item.indexOf(":") + 1).trimLeft(),
|
|
||||||
active: !item.trim().startsWith("#"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const filteredUrlEncodedParams = workingUrlEncodedParams.value.filter(
|
|
||||||
(x) => x.key !== ""
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!isEqual(filteredUrlEncodedParams, transformation)) {
|
|
||||||
workingUrlEncodedParams.value = transformation
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`${t("error.something_went_wrong")}`)
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(workingUrlEncodedParams, (newurlEncodedParamList) => {
|
|
||||||
if (bulkMode.value) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const currentBulkUrlEncodedParams = bulkUrlEncodedParams.value
|
|
||||||
.split("\n")
|
|
||||||
.map((item) => ({
|
|
||||||
key: item.substring(0, item.indexOf(":")).trimLeft().replace(/^#/, ""),
|
|
||||||
value: item.substring(item.indexOf(":") + 1).trimLeft(),
|
|
||||||
active: !item.trim().startsWith("#"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const filteredUrlEncodedParams = newurlEncodedParamList.filter(
|
|
||||||
(x) => x.key !== ""
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!isEqual(currentBulkUrlEncodedParams, filteredUrlEncodedParams)) {
|
|
||||||
bulkUrlEncodedParams.value = filteredUrlEncodedParams
|
|
||||||
.map((param) => {
|
|
||||||
return `${param.active ? "" : "#"}${param.key}: ${param.value}`
|
|
||||||
})
|
|
||||||
.join("\n")
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`${t("error.something_went_wrong")}`)
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const addUrlEncodedParam = () => {
|
|
||||||
workingUrlEncodedParams.value.push({
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateUrlEncodedParam = (index: number, param: RawKeyValueEntry) => {
|
|
||||||
workingUrlEncodedParams.value = workingUrlEncodedParams.value.map((p, i) =>
|
|
||||||
i === index ? param : p
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteUrlEncodedParam = (index: number) => {
|
|
||||||
const urlEncodedParamsBeforeDeletion = clone(workingUrlEncodedParams.value)
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
urlEncodedParamsBeforeDeletion.length > 0 &&
|
|
||||||
index === urlEncodedParamsBeforeDeletion.length - 1
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (deletionToast.value) {
|
|
||||||
deletionToast.value.goAway(0)
|
|
||||||
deletionToast.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
deletionToast.value = toast.success(`${t("state.deleted")}`, {
|
|
||||||
action: [
|
|
||||||
{
|
|
||||||
text: `${t("action.undo")}`,
|
|
||||||
onClick: (_, toastObject) => {
|
|
||||||
workingUrlEncodedParams.value = urlEncodedParamsBeforeDeletion
|
|
||||||
toastObject.goAway(0)
|
|
||||||
deletionToast.value = null
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
onComplete: () => {
|
|
||||||
deletionToast.value = null
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
workingUrlEncodedParams.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearContent = () => {
|
|
||||||
// set urlEncodedParams list to the initial state
|
|
||||||
workingUrlEncodedParams.value = [
|
|
||||||
{
|
|
||||||
key: "",
|
|
||||||
value: "",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
bulkUrlEncodedParams.value = ""
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-lowerSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-lowerSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("request.header_list") }}
|
{{ t("request.header_list") }}
|
||||||
@@ -20,19 +20,19 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(header, index) in headers"
|
v-for="(header, index) in headers"
|
||||||
:key="`header-${index}`"
|
:key="`header-${index}`"
|
||||||
class="flex border-b divide-x divide-dividerLight border-dividerLight group"
|
class="divide-dividerLight divide-x border-b border-dividerLight flex group"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="flex flex-1 min-w-0 px-4 py-2 transition group-hover:text-secondaryDark"
|
class="flex flex-1 min-w-0 py-2 px-4 transition group-hover:text-secondaryDark"
|
||||||
>
|
>
|
||||||
<span class="truncate rounded-sm select-all">
|
<span class="rounded-sm truncate select-all">
|
||||||
{{ header.key }}
|
{{ header.key }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="flex flex-1 min-w-0 px-4 py-2 transition group-hover:text-secondaryDark"
|
class="flex flex-1 min-w-0 py-2 px-4 transition group-hover:text-secondaryDark"
|
||||||
>
|
>
|
||||||
<span class="truncate rounded-sm select-all">
|
<span class="rounded-sm truncate select-all">
|
||||||
{{ header.value }}
|
{{ header.value }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col flex-1">
|
<div class="flex flex-col flex-1">
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-lowerSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-lowerSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("response.body") }}
|
{{ t("response.body") }}
|
||||||
@@ -49,20 +49,16 @@
|
|||||||
class="covers-response"
|
class="covers-response"
|
||||||
src="about:blank"
|
src="about:blank"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
sandbox=""
|
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from "@nuxtjs/composition-api"
|
import { computed, ref, reactive } from "@nuxtjs/composition-api"
|
||||||
import usePreview from "~/helpers/lenses/composables/usePreview"
|
|
||||||
import useResponseBody from "~/helpers/lenses/composables/useResponseBody"
|
|
||||||
import useDownloadResponse from "~/helpers/lenses/composables/useDownloadResponse"
|
|
||||||
import useCopyResponse from "~/helpers/lenses/composables/useCopyResponse"
|
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||||
import { useI18n } from "~/helpers/utils/composables"
|
import { useI18n, useToast } from "~/helpers/utils/composables"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -70,20 +66,31 @@ const props = defineProps<{
|
|||||||
response: HoppRESTResponse
|
response: HoppRESTResponse
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const responseBodyText = computed(() => {
|
||||||
|
if (
|
||||||
|
props.response.type === "loading" ||
|
||||||
|
props.response.type === "network_fail"
|
||||||
|
)
|
||||||
|
return ""
|
||||||
|
if (typeof props.response.body === "string") return props.response.body
|
||||||
|
else {
|
||||||
|
const res = new TextDecoder("utf-8").decode(props.response.body)
|
||||||
|
// HACK: Temporary trailing null character issue from the extension fix
|
||||||
|
return res.replace(/\0+$/, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const downloadIcon = ref("download")
|
||||||
|
const copyIcon = ref("copy")
|
||||||
|
const previewEnabled = ref(false)
|
||||||
|
const previewFrame = ref<any | null>(null)
|
||||||
|
const url = ref("")
|
||||||
|
|
||||||
const htmlResponse = ref<any | null>(null)
|
const htmlResponse = ref<any | null>(null)
|
||||||
const linewrapEnabled = ref(true)
|
const linewrapEnabled = ref(true)
|
||||||
|
|
||||||
const { responseBodyText } = useResponseBody(props.response)
|
|
||||||
const { downloadIcon, downloadResponse } = useDownloadResponse(
|
|
||||||
"text/html",
|
|
||||||
responseBodyText
|
|
||||||
)
|
|
||||||
const { previewFrame, previewEnabled, togglePreview } = usePreview(
|
|
||||||
false,
|
|
||||||
responseBodyText
|
|
||||||
)
|
|
||||||
const { copyIcon, copyResponse } = useCopyResponse(responseBodyText)
|
|
||||||
|
|
||||||
useCodemirror(
|
useCodemirror(
|
||||||
htmlResponse,
|
htmlResponse,
|
||||||
responseBodyText,
|
responseBodyText,
|
||||||
@@ -95,9 +102,53 @@ useCodemirror(
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: true,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const downloadResponse = () => {
|
||||||
|
const dataToWrite = responseBodyText.value
|
||||||
|
const file = new Blob([dataToWrite], { type: "text/html" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
// TODO get uri from meta
|
||||||
|
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
downloadIcon.value = "check"
|
||||||
|
toast.success(`${t("state.download_started")}`)
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
downloadIcon.value = "download"
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyResponse = () => {
|
||||||
|
copyToClipboard(responseBodyText.value)
|
||||||
|
copyIcon.value = "check"
|
||||||
|
toast.success(`${t("state.copied_to_clipboard")}`)
|
||||||
|
setTimeout(() => (copyIcon.value = "copy"), 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const togglePreview = () => {
|
||||||
|
previewEnabled.value = !previewEnabled.value
|
||||||
|
if (previewEnabled.value) {
|
||||||
|
if (previewFrame.value.getAttribute("data-previewing-url") === url.value)
|
||||||
|
return
|
||||||
|
// Use DOMParser to parse document HTML.
|
||||||
|
const previewDocument = new DOMParser().parseFromString(
|
||||||
|
responseBodyText.value,
|
||||||
|
"text/html"
|
||||||
|
)
|
||||||
|
// Inject <base href="..."> tag to head, to fix relative CSS/HTML paths.
|
||||||
|
previewDocument.head.innerHTML =
|
||||||
|
`<base href="${url.value}">` + previewDocument.head.innerHTML
|
||||||
|
// Finally, set the iframe source to the resulting HTML.
|
||||||
|
previewFrame.value.srcdoc = previewDocument.documentElement.outerHTML
|
||||||
|
previewFrame.value.setAttribute("data-previewing-url", url.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-lowerSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-lowerSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ $t("response.body") }}
|
{{ $t("response.body") }}
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
class="flex flex-1 max-w-full border-b border-dividerLight"
|
class="border-b border-dividerLight flex max-w-full flex-1"
|
||||||
:src="imageSource"
|
:src="imageSource"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
:alt="imageSource"
|
:alt="imageSource"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-lowerSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-lowerSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">{{
|
<label class="font-semibold text-secondaryLight">{{
|
||||||
t("response.body")
|
t("response.body")
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<div ref="jsonResponse"></div>
|
<div ref="jsonResponse"></div>
|
||||||
<div
|
<div
|
||||||
v-if="outlinePath"
|
v-if="outlinePath"
|
||||||
class="sticky bottom-0 z-10 flex flex-1 px-2 overflow-auto border-t bg-primaryLight border-dividerLight flex-nowrap hide-scrollbar"
|
class="bg-primaryLight border-t border-dividerLight flex flex-nowrap flex-1 px-2 bottom-0 z-10 sticky overflow-auto hide-scrollbar"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in outlinePath"
|
v-for="(item, index) in outlinePath"
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
</tippy>
|
</tippy>
|
||||||
<i
|
<i
|
||||||
v-if="index + 1 !== outlinePath.length"
|
v-if="index + 1 !== outlinePath.length"
|
||||||
class="opacity-50 text-secondaryLight material-icons"
|
class="text-secondaryLight opacity-50 material-icons"
|
||||||
>chevron_right</i
|
>chevron_right</i
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,6 +126,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, reactive } from "@nuxtjs/composition-api"
|
import { computed, ref, reactive } from "@nuxtjs/composition-api"
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||||
import jsonParse, { JSONObjectMember, JSONValue } from "~/helpers/jsonParse"
|
import jsonParse, { JSONObjectMember, JSONValue } from "~/helpers/jsonParse"
|
||||||
import { getJSONOutlineAtPos } from "~/helpers/newOutline"
|
import { getJSONOutlineAtPos } from "~/helpers/newOutline"
|
||||||
@@ -133,10 +134,7 @@ import {
|
|||||||
convertIndexToLineCh,
|
convertIndexToLineCh,
|
||||||
convertLineChToIndex,
|
convertLineChToIndex,
|
||||||
} from "~/helpers/editor/utils"
|
} from "~/helpers/editor/utils"
|
||||||
import { useI18n } from "~/helpers/utils/composables"
|
import { useI18n, useToast } from "~/helpers/utils/composables"
|
||||||
import useCopyResponse from "~/helpers/lenses/composables/useCopyResponse"
|
|
||||||
import useResponseBody from "~/helpers/lenses/composables/useResponseBody"
|
|
||||||
import useDownloadResponse from "~/helpers/lenses/composables/useDownloadResponse"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -144,14 +142,24 @@ const props = defineProps<{
|
|||||||
response: HoppRESTResponse
|
response: HoppRESTResponse
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { responseBodyText } = useResponseBody(props.response)
|
const toast = useToast()
|
||||||
|
|
||||||
const { copyIcon, copyResponse } = useCopyResponse(responseBodyText)
|
const responseBodyText = computed(() => {
|
||||||
|
if (
|
||||||
|
props.response.type === "loading" ||
|
||||||
|
props.response.type === "network_fail"
|
||||||
|
)
|
||||||
|
return ""
|
||||||
|
if (typeof props.response.body === "string") return props.response.body
|
||||||
|
else {
|
||||||
|
const res = new TextDecoder("utf-8").decode(props.response.body)
|
||||||
|
// HACK: Temporary trailing null character issue from the extension fix
|
||||||
|
return res.replace(/\0+$/, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const { downloadIcon, downloadResponse } = useDownloadResponse(
|
const downloadIcon = ref("download")
|
||||||
"application/json",
|
const copyIcon = ref("copy")
|
||||||
responseBodyText
|
|
||||||
)
|
|
||||||
|
|
||||||
const jsonBodyText = computed(() => {
|
const jsonBodyText = computed(() => {
|
||||||
try {
|
try {
|
||||||
@@ -185,7 +193,6 @@ const { cursor } = useCodemirror(
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: true,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -195,6 +202,25 @@ const jumpCursor = (ast: JSONValue | JSONObjectMember) => {
|
|||||||
cursor.value = pos
|
cursor.value = pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const downloadResponse = () => {
|
||||||
|
const dataToWrite = responseBodyText.value
|
||||||
|
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
// TODO get uri from meta
|
||||||
|
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
downloadIcon.value = "check"
|
||||||
|
toast.success(`${t("state.download_started")}`)
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
downloadIcon.value = "download"
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
const outlinePath = computed(() => {
|
const outlinePath = computed(() => {
|
||||||
if (ast.value) {
|
if (ast.value) {
|
||||||
return getJSONOutlineAtPos(
|
return getJSONOutlineAtPos(
|
||||||
@@ -203,6 +229,13 @@ const outlinePath = computed(() => {
|
|||||||
)
|
)
|
||||||
} else return null
|
} else return null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const copyResponse = () => {
|
||||||
|
copyToClipboard(responseBodyText.value)
|
||||||
|
copyIcon.value = "check"
|
||||||
|
toast.success(`${t("state.copied_to_clipboard")}`)
|
||||||
|
setTimeout(() => (copyIcon.value = "copy"), 1000)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-lowerSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-lowerSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("response.body") }}
|
{{ t("response.body") }}
|
||||||
@@ -40,11 +40,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, reactive } from "@nuxtjs/composition-api"
|
import { ref, computed, reactive } from "@nuxtjs/composition-api"
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||||
import { useI18n } from "~/helpers/utils/composables"
|
import { useI18n, useToast } from "~/helpers/utils/composables"
|
||||||
import useResponseBody from "~/helpers/lenses/composables/useResponseBody"
|
|
||||||
import useDownloadResponse from "~/helpers/lenses/composables/useDownloadResponse"
|
|
||||||
import useCopyResponse from "~/helpers/lenses/composables/useCopyResponse"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -52,7 +50,24 @@ const props = defineProps<{
|
|||||||
response: HoppRESTResponse
|
response: HoppRESTResponse
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { responseBodyText } = useResponseBody(props.response)
|
const toast = useToast()
|
||||||
|
|
||||||
|
const responseBodyText = computed(() => {
|
||||||
|
if (
|
||||||
|
props.response.type === "loading" ||
|
||||||
|
props.response.type === "network_fail"
|
||||||
|
)
|
||||||
|
return ""
|
||||||
|
if (typeof props.response.body === "string") return props.response.body
|
||||||
|
else {
|
||||||
|
const res = new TextDecoder("utf-8").decode(props.response.body)
|
||||||
|
// HACK: Temporary trailing null character issue from the extension fix
|
||||||
|
return res.replace(/\0+$/, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const downloadIcon = ref("download")
|
||||||
|
const copyIcon = ref("copy")
|
||||||
|
|
||||||
const responseType = computed(() => {
|
const responseType = computed(() => {
|
||||||
return (
|
return (
|
||||||
@@ -63,13 +78,6 @@ const responseType = computed(() => {
|
|||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
})
|
})
|
||||||
|
|
||||||
const { downloadIcon, downloadResponse } = useDownloadResponse(
|
|
||||||
responseType.value,
|
|
||||||
responseBodyText
|
|
||||||
)
|
|
||||||
|
|
||||||
const { copyIcon, copyResponse } = useCopyResponse(responseBodyText)
|
|
||||||
|
|
||||||
const rawResponse = ref<any | null>(null)
|
const rawResponse = ref<any | null>(null)
|
||||||
const linewrapEnabled = ref(true)
|
const linewrapEnabled = ref(true)
|
||||||
|
|
||||||
@@ -84,7 +92,32 @@ useCodemirror(
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: true,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const downloadResponse = () => {
|
||||||
|
const dataToWrite = responseBodyText.value
|
||||||
|
const file = new Blob([dataToWrite], { type: responseType.value })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
// TODO get uri from meta
|
||||||
|
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
downloadIcon.value = "check"
|
||||||
|
toast.success(`${t("state.download_started")}`)
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
downloadIcon.value = "download"
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyResponse = () => {
|
||||||
|
copyToClipboard(responseBodyText.value)
|
||||||
|
copyIcon.value = "check"
|
||||||
|
toast.success(`${t("state.copied_to_clipboard")}`)
|
||||||
|
setTimeout(() => (copyIcon.value = "copy"), 1000)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="sticky z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight top-lowerSecondaryStickyFold"
|
class="bg-primary border-b border-dividerLight flex flex-1 top-lowerSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label class="font-semibold text-secondaryLight">
|
<label class="font-semibold text-secondaryLight">
|
||||||
{{ t("response.body") }}
|
{{ t("response.body") }}
|
||||||
@@ -40,11 +40,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, reactive } from "@nuxtjs/composition-api"
|
import { computed, ref, reactive } from "@nuxtjs/composition-api"
|
||||||
import { useCodemirror } from "~/helpers/editor/codemirror"
|
import { useCodemirror } from "~/helpers/editor/codemirror"
|
||||||
|
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||||
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
|
||||||
import { useI18n } from "~/helpers/utils/composables"
|
import { useI18n, useToast } from "~/helpers/utils/composables"
|
||||||
import useResponseBody from "~/helpers/lenses/composables/useResponseBody"
|
|
||||||
import useDownloadResponse from "~/helpers/lenses/composables/useDownloadResponse"
|
|
||||||
import useCopyResponse from "~/helpers/lenses/composables/useCopyResponse"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
@@ -52,7 +50,24 @@ const props = defineProps<{
|
|||||||
response: HoppRESTResponse
|
response: HoppRESTResponse
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { responseBodyText } = useResponseBody(props.response)
|
const toast = useToast()
|
||||||
|
|
||||||
|
const responseBodyText = computed(() => {
|
||||||
|
if (
|
||||||
|
props.response.type === "loading" ||
|
||||||
|
props.response.type === "network_fail"
|
||||||
|
)
|
||||||
|
return ""
|
||||||
|
if (typeof props.response.body === "string") return props.response.body
|
||||||
|
else {
|
||||||
|
const res = new TextDecoder("utf-8").decode(props.response.body)
|
||||||
|
// HACK: Temporary trailing null character issue from the extension fix
|
||||||
|
return res.replace(/\0+$/, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const downloadIcon = ref("download")
|
||||||
|
const copyIcon = ref("copy")
|
||||||
|
|
||||||
const responseType = computed(() => {
|
const responseType = computed(() => {
|
||||||
return (
|
return (
|
||||||
@@ -63,13 +78,6 @@ const responseType = computed(() => {
|
|||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
})
|
})
|
||||||
|
|
||||||
const { downloadIcon, downloadResponse } = useDownloadResponse(
|
|
||||||
responseType.value,
|
|
||||||
responseBodyText
|
|
||||||
)
|
|
||||||
|
|
||||||
const { copyIcon, copyResponse } = useCopyResponse(responseBodyText)
|
|
||||||
|
|
||||||
const xmlResponse = ref<any | null>(null)
|
const xmlResponse = ref<any | null>(null)
|
||||||
const linewrapEnabled = ref(true)
|
const linewrapEnabled = ref(true)
|
||||||
|
|
||||||
@@ -84,7 +92,32 @@ useCodemirror(
|
|||||||
},
|
},
|
||||||
linter: null,
|
linter: null,
|
||||||
completer: null,
|
completer: null,
|
||||||
environmentHighlights: true,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const downloadResponse = () => {
|
||||||
|
const dataToWrite = responseBodyText.value
|
||||||
|
const file = new Blob([dataToWrite], { type: responseType.value })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
a.href = url
|
||||||
|
// TODO get uri from meta
|
||||||
|
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
downloadIcon.value = "check"
|
||||||
|
toast.success(`${t("state.download_started")}`)
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
downloadIcon.value = "download"
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyResponse = () => {
|
||||||
|
copyToClipboard(responseBodyText.value)
|
||||||
|
copyIcon.value = "check"
|
||||||
|
toast.success(`${t("state.copied_to_clipboard")}`)
|
||||||
|
setTimeout(() => (copyIcon.value = "copy"), 1000)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="cursor-pointer flex h-5 w-5 relative items-center justify-center">
|
||||||
tabindex="0"
|
|
||||||
class="relative flex items-center justify-center w-5 h-5 rounded-full cursor-pointer focus:outline-none focus-visible:ring focus-visible:ring-primaryDark"
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
class="absolute object-cover object-center w-5 h-5 transition rounded-full bg-primaryDark"
|
class="bg-primaryDark rounded-full object-cover object-center h-5 transition w-5 absolute"
|
||||||
:src="url"
|
:src="url"
|
||||||
:alt="alt"
|
:alt="alt"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<div class="absolute inset-0 rounded-full shadow-inner"></div>
|
<div class="rounded-full shadow-inner inset-0 absolute"></div>
|
||||||
<span
|
<span
|
||||||
v-if="indicator"
|
v-if="indicator"
|
||||||
class="border-primary rounded-full border-2 h-2.5 -top-0.5 -right-0.5 w-2.5 absolute"
|
class="border-primary rounded-full border-2 h-2.5 -top-0.5 -right-0.5 w-2.5 absolute"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div
|
<div
|
||||||
class="sticky top-0 z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight"
|
class="bg-primary border-b border-dividerLight flex flex-1 pl-4 top-0 z-10 sticky items-center justify-between"
|
||||||
>
|
>
|
||||||
<label for="log" class="py-2 font-semibold text-secondaryLight">
|
<label for="log" class="font-semibold text-secondaryLight py-2">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,57 +13,63 @@
|
|||||||
:size="COLUMN_LAYOUT ? 45 : 50"
|
:size="COLUMN_LAYOUT ? 45 : 50"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="sticky top-0 z-10 flex flex-col p-4 space-y-4 bg-primary">
|
<AppSection label="request">
|
||||||
<div class="inline-flex flex-1 space-x-2">
|
<div
|
||||||
<input
|
class="bg-primary flex flex-col space-y-4 p-4 top-0 z-10 sticky"
|
||||||
id="mqtt-url"
|
>
|
||||||
v-model="url"
|
<div class="space-x-2 flex-1 inline-flex">
|
||||||
type="url"
|
<input
|
||||||
autocomplete="off"
|
id="mqtt-url"
|
||||||
spellcheck="false"
|
v-model="url"
|
||||||
class="w-full px-4 py-2 border rounded bg-primaryLight border-divider text-secondaryDark hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
type="url"
|
||||||
:placeholder="$t('mqtt.url')"
|
autocomplete="off"
|
||||||
:disabled="connectionState"
|
spellcheck="false"
|
||||||
@keyup.enter="validUrl ? toggleConnection() : null"
|
class="bg-primaryLight border border-divider rounded text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
/>
|
:placeholder="$t('mqtt.url')"
|
||||||
<ButtonPrimary
|
:disabled="connectionState"
|
||||||
id="connect"
|
@keyup.enter="validUrl ? toggleConnection() : null"
|
||||||
:disabled="!validUrl"
|
/>
|
||||||
class="w-32"
|
<ButtonPrimary
|
||||||
:label="
|
id="connect"
|
||||||
connectionState
|
:disabled="!validUrl"
|
||||||
? $t('action.disconnect')
|
class="w-32"
|
||||||
: $t('action.connect')
|
:label="
|
||||||
"
|
connectionState
|
||||||
:loading="connectingState"
|
? $t('action.disconnect')
|
||||||
@click.native="toggleConnection"
|
: $t('action.connect')
|
||||||
/>
|
"
|
||||||
|
:loading="connectingState"
|
||||||
|
@click.native="toggleConnection"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex space-x-4">
|
||||||
|
<input
|
||||||
|
id="mqtt-username"
|
||||||
|
v-model="username"
|
||||||
|
type="text"
|
||||||
|
spellcheck="false"
|
||||||
|
class="input"
|
||||||
|
:placeholder="$t('authorization.username')"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
id="mqtt-password"
|
||||||
|
v-model="password"
|
||||||
|
type="password"
|
||||||
|
spellcheck="false"
|
||||||
|
class="input"
|
||||||
|
:placeholder="$t('authorization.password')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-4">
|
</AppSection>
|
||||||
<input
|
|
||||||
id="mqtt-username"
|
|
||||||
v-model="username"
|
|
||||||
type="text"
|
|
||||||
spellcheck="false"
|
|
||||||
class="input"
|
|
||||||
:placeholder="$t('authorization.username')"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
id="mqtt-password"
|
|
||||||
v-model="password"
|
|
||||||
type="password"
|
|
||||||
spellcheck="false"
|
|
||||||
class="input"
|
|
||||||
:placeholder="$t('authorization.password')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Pane>
|
</Pane>
|
||||||
<Pane
|
<Pane
|
||||||
:size="COLUMN_LAYOUT ? 65 : 50"
|
:size="COLUMN_LAYOUT ? 65 : 50"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<RealtimeLog :title="$t('mqtt.log')" :log="log" />
|
<AppSection label="response">
|
||||||
|
<RealtimeLog :title="$t('mqtt.log')" :log="log" />
|
||||||
|
</AppSection>
|
||||||
</Pane>
|
</Pane>
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
</Pane>
|
</Pane>
|
||||||
@@ -73,71 +79,75 @@
|
|||||||
min-size="20"
|
min-size="20"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col flex-1 p-4">
|
<AppSection label="messages">
|
||||||
<label for="pub_topic" class="font-semibold text-secondaryLight">
|
<div class="flex flex-col flex-1 p-4 inline-flex">
|
||||||
{{ $t("mqtt.topic") }}
|
<label for="pub_topic" class="font-semibold text-secondaryLight">
|
||||||
</label>
|
{{ $t("mqtt.topic") }}
|
||||||
</div>
|
</label>
|
||||||
<div class="flex px-4">
|
</div>
|
||||||
<input
|
<div class="flex px-4">
|
||||||
id="pub_topic"
|
<input
|
||||||
v-model="pub_topic"
|
id="pub_topic"
|
||||||
class="input"
|
v-model="pub_topic"
|
||||||
:placeholder="$t('mqtt.topic_name')"
|
class="input"
|
||||||
type="text"
|
:placeholder="$t('mqtt.topic_name')"
|
||||||
autocomplete="off"
|
type="text"
|
||||||
spellcheck="false"
|
autocomplete="off"
|
||||||
/>
|
spellcheck="false"
|
||||||
</div>
|
/>
|
||||||
<div class="flex items-center justify-between flex-1 p-4">
|
</div>
|
||||||
<label for="mqtt-message" class="font-semibold text-secondaryLight">
|
<div class="flex flex-1 p-4 items-center justify-between">
|
||||||
{{ $t("mqtt.communication") }}
|
<label for="mqtt-message" class="font-semibold text-secondaryLight">
|
||||||
</label>
|
{{ $t("mqtt.communication") }}
|
||||||
</div>
|
</label>
|
||||||
<div class="flex px-4 space-x-2">
|
</div>
|
||||||
<input
|
<div class="flex space-x-2 px-4">
|
||||||
id="mqtt-message"
|
<input
|
||||||
v-model="msg"
|
id="mqtt-message"
|
||||||
class="input"
|
v-model="msg"
|
||||||
type="text"
|
class="input"
|
||||||
autocomplete="off"
|
type="text"
|
||||||
:placeholder="$t('mqtt.message')"
|
autocomplete="off"
|
||||||
spellcheck="false"
|
:placeholder="$t('mqtt.message')"
|
||||||
/>
|
spellcheck="false"
|
||||||
<ButtonPrimary
|
/>
|
||||||
id="publish"
|
<ButtonPrimary
|
||||||
name="get"
|
id="publish"
|
||||||
:disabled="!canpublish"
|
name="get"
|
||||||
:label="$t('mqtt.publish')"
|
:disabled="!canpublish"
|
||||||
@click.native="publish"
|
:label="$t('mqtt.publish')"
|
||||||
/>
|
@click.native="publish"
|
||||||
</div>
|
/>
|
||||||
<div class="flex flex-col flex-1 p-4 mt-4 border-t border-dividerLight">
|
</div>
|
||||||
<label for="sub_topic" class="font-semibold text-secondaryLight">
|
<div
|
||||||
{{ $t("mqtt.topic") }}
|
class="border-t border-dividerLight flex flex-col flex-1 mt-4 p-4 inline-flex"
|
||||||
</label>
|
>
|
||||||
</div>
|
<label for="sub_topic" class="font-semibold text-secondaryLight">
|
||||||
<div class="flex px-4 space-x-2">
|
{{ $t("mqtt.topic") }}
|
||||||
<input
|
</label>
|
||||||
id="sub_topic"
|
</div>
|
||||||
v-model="sub_topic"
|
<div class="flex space-x-2 px-4">
|
||||||
type="text"
|
<input
|
||||||
autocomplete="off"
|
id="sub_topic"
|
||||||
:placeholder="$t('mqtt.topic_name')"
|
v-model="sub_topic"
|
||||||
spellcheck="false"
|
type="text"
|
||||||
class="input"
|
autocomplete="off"
|
||||||
/>
|
:placeholder="$t('mqtt.topic_name')"
|
||||||
<ButtonPrimary
|
spellcheck="false"
|
||||||
id="subscribe"
|
class="input"
|
||||||
name="get"
|
/>
|
||||||
:disabled="!cansubscribe"
|
<ButtonPrimary
|
||||||
:label="
|
id="subscribe"
|
||||||
subscriptionState ? $t('mqtt.unsubscribe') : $t('mqtt.subscribe')
|
name="get"
|
||||||
"
|
:disabled="!cansubscribe"
|
||||||
reverse
|
:label="
|
||||||
@click.native="toggleSubscription"
|
subscriptionState ? $t('mqtt.unsubscribe') : $t('mqtt.subscribe')
|
||||||
/>
|
"
|
||||||
</div>
|
reverse
|
||||||
|
@click.native="toggleSubscription"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</AppSection>
|
||||||
</Pane>
|
</Pane>
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -13,209 +13,84 @@
|
|||||||
:size="COLUMN_LAYOUT ? 45 : 50"
|
:size="COLUMN_LAYOUT ? 45 : 50"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="sticky top-0 z-10 flex p-4 bg-primary">
|
<AppSection label="request">
|
||||||
<div class="inline-flex flex-1 space-x-2">
|
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||||
<div class="flex flex-1">
|
<div class="space-x-2 flex-1 inline-flex">
|
||||||
<label for="client-version">
|
<div class="flex flex-1">
|
||||||
<tippy
|
<label for="client-version">
|
||||||
ref="versionOptions"
|
<tippy
|
||||||
interactive
|
ref="versionOptions"
|
||||||
trigger="click"
|
interactive
|
||||||
theme="popover"
|
trigger="click"
|
||||||
arrow
|
theme="popover"
|
||||||
>
|
arrow
|
||||||
<template #trigger>
|
>
|
||||||
<span class="select-wrapper">
|
<template #trigger>
|
||||||
<input
|
<span class="select-wrapper">
|
||||||
id="client-version"
|
<input
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
id="client-version"
|
||||||
title="socket.io-client version"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
class="flex px-4 py-2 font-semibold border rounded-l cursor-pointer bg-primaryLight border-divider text-secondaryDark w-26 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
title="socket.io-client version"
|
||||||
:value="`Client ${clientVersion}`"
|
class="bg-primaryLight border border-divider rounded-l cursor-pointer flex font-semibold text-secondaryDark py-2 px-4 w-26 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
readonly
|
:value="`Client ${clientVersion}`"
|
||||||
:disabled="connectionState"
|
readonly
|
||||||
/>
|
:disabled="connectionState"
|
||||||
</span>
|
/>
|
||||||
</template>
|
</span>
|
||||||
<SmartItem
|
</template>
|
||||||
v-for="(_, version) in socketIoClients"
|
<SmartItem
|
||||||
:key="`client-${version}`"
|
v-for="(_, version) in socketIoClients"
|
||||||
:label="`Client ${version}`"
|
:key="`client-${version}`"
|
||||||
@click.native="onSelectVersion(version)"
|
:label="`Client ${version}`"
|
||||||
/>
|
@click.native="onSelectVersion(version)"
|
||||||
</tippy>
|
/>
|
||||||
</label>
|
</tippy>
|
||||||
<input
|
</label>
|
||||||
id="socketio-url"
|
<input
|
||||||
v-model="url"
|
id="socketio-url"
|
||||||
type="url"
|
v-model="url"
|
||||||
autocomplete="off"
|
type="url"
|
||||||
spellcheck="false"
|
autocomplete="off"
|
||||||
:class="{ error: !urlValid }"
|
spellcheck="false"
|
||||||
class="flex flex-1 w-full px-4 py-2 border bg-primaryLight border-divider text-secondaryDark hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
:class="{ error: !urlValid }"
|
||||||
:placeholder="$t('socketio.url')"
|
class="bg-primaryLight border border-divider flex flex-1 text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
:disabled="connectionState"
|
:placeholder="$t('socketio.url')"
|
||||||
@keyup.enter="urlValid ? toggleConnection() : null"
|
:disabled="connectionState"
|
||||||
/>
|
@keyup.enter="urlValid ? toggleConnection() : null"
|
||||||
<input
|
/>
|
||||||
id="socketio-path"
|
<input
|
||||||
v-model="path"
|
id="socketio-path"
|
||||||
class="flex flex-1 w-full px-4 py-2 border rounded-r bg-primaryLight border-divider text-secondaryDark hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
v-model="path"
|
||||||
spellcheck="false"
|
class="bg-primaryLight border border-divider rounded-r flex flex-1 text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
:disabled="connectionState"
|
spellcheck="false"
|
||||||
@keyup.enter="urlValid ? toggleConnection() : null"
|
:disabled="connectionState"
|
||||||
/>
|
@keyup.enter="urlValid ? toggleConnection() : null"
|
||||||
</div>
|
/>
|
||||||
<ButtonPrimary
|
|
||||||
id="connect"
|
|
||||||
:disabled="!urlValid"
|
|
||||||
name="connect"
|
|
||||||
class="w-32"
|
|
||||||
:label="
|
|
||||||
!connectionState
|
|
||||||
? $t('action.connect')
|
|
||||||
: $t('action.disconnect')
|
|
||||||
"
|
|
||||||
:loading="connectingState"
|
|
||||||
@click.native="toggleConnection"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperPrimaryStickyFold"
|
|
||||||
>
|
|
||||||
<span class="flex items-center">
|
|
||||||
<label class="font-semibold text-secondaryLight">
|
|
||||||
{{ $t("authorization.type") }}
|
|
||||||
</label>
|
|
||||||
<tippy
|
|
||||||
ref="authTypeOptions"
|
|
||||||
interactive
|
|
||||||
trigger="click"
|
|
||||||
theme="popover"
|
|
||||||
arrow
|
|
||||||
>
|
|
||||||
<template #trigger>
|
|
||||||
<span class="select-wrapper">
|
|
||||||
<ButtonSecondary
|
|
||||||
class="pr-8 ml-2 rounded-none"
|
|
||||||
:label="authType"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<SmartItem
|
|
||||||
label="None"
|
|
||||||
:icon="
|
|
||||||
authType === 'None'
|
|
||||||
? 'radio_button_checked'
|
|
||||||
: 'radio_button_unchecked'
|
|
||||||
"
|
|
||||||
:active="authType === 'None'"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
authType = 'None'
|
|
||||||
authTypeOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<SmartItem
|
|
||||||
label="Bearer Token"
|
|
||||||
:icon="
|
|
||||||
authType === 'Bearer'
|
|
||||||
? 'radio_button_checked'
|
|
||||||
: 'radio_button_unchecked'
|
|
||||||
"
|
|
||||||
:active="authType === 'Bearer'"
|
|
||||||
@click.native="
|
|
||||||
() => {
|
|
||||||
authType = 'Bearer'
|
|
||||||
authTypeOptions.tippy().hide()
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</tippy>
|
|
||||||
</span>
|
|
||||||
<div class="flex">
|
|
||||||
<SmartCheckbox
|
|
||||||
:on="authActive"
|
|
||||||
class="px-2"
|
|
||||||
@change="authActive = !authActive"
|
|
||||||
>
|
|
||||||
{{ $t("state.enabled") }}
|
|
||||||
</SmartCheckbox>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
to="https://docs.hoppscotch.io/features/authorization"
|
|
||||||
blank
|
|
||||||
:title="$t('app.wiki')"
|
|
||||||
svg="help-circle"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="$t('action.clear')"
|
|
||||||
svg="trash-2"
|
|
||||||
@click.native="clearContent"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="authType === 'None'"
|
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${$colorMode.value}/login.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
|
||||||
:alt="$t('empty.authorization')"
|
|
||||||
/>
|
|
||||||
<span class="pb-4 text-center">
|
|
||||||
This SocketIO connection does not use any authentication.
|
|
||||||
</span>
|
|
||||||
<ButtonSecondary
|
|
||||||
outline
|
|
||||||
:label="$t('app.documentation')"
|
|
||||||
to="https://docs.hoppscotch.io/features/authorization"
|
|
||||||
blank
|
|
||||||
svg="external-link"
|
|
||||||
reverse
|
|
||||||
class="mb-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="authType === 'Bearer'"
|
|
||||||
class="flex border-b border-dividerLight"
|
|
||||||
>
|
|
||||||
<div class="w-2/3 border-r border-dividerLight">
|
|
||||||
<div class="flex border-b border-dividerLight">
|
|
||||||
<SmartEnvInput
|
|
||||||
v-model="bearerToken"
|
|
||||||
placeholder="Token"
|
|
||||||
styles="bg-transparent flex flex-1 py-1 px-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="sticky h-full p-4 overflow-auto bg-primary top-upperTertiaryStickyFold min-w-46 max-w-1/3 z-9"
|
|
||||||
>
|
|
||||||
<div class="p-2">
|
|
||||||
<div class="pb-2 text-secondaryLight">
|
|
||||||
{{ $t("helpers.authorization") }}
|
|
||||||
</div>
|
</div>
|
||||||
<SmartAnchor
|
<ButtonPrimary
|
||||||
class="link"
|
id="connect"
|
||||||
:label="`${$t('authorization.learn')} \xA0 →`"
|
:disabled="!urlValid"
|
||||||
to="https://docs.hoppscotch.io/features/authorization"
|
name="connect"
|
||||||
blank
|
class="w-32"
|
||||||
|
:label="
|
||||||
|
!connectionState
|
||||||
|
? $t('action.connect')
|
||||||
|
: $t('action.disconnect')
|
||||||
|
"
|
||||||
|
:loading="connectingState"
|
||||||
|
@click.native="toggleConnection"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppSection>
|
||||||
</Pane>
|
</Pane>
|
||||||
<Pane
|
<Pane
|
||||||
:size="COLUMN_LAYOUT ? 65 : 50"
|
:size="COLUMN_LAYOUT ? 65 : 50"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<RealtimeLog :title="$t('socketio.log')" :log="log" />
|
<AppSection label="response">
|
||||||
|
<RealtimeLog :title="$t('socketio.log')" :log="log" />
|
||||||
|
</AppSection>
|
||||||
</Pane>
|
</Pane>
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
</Pane>
|
</Pane>
|
||||||
@@ -225,78 +100,80 @@
|
|||||||
min-size="20"
|
min-size="20"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col flex-1 p-4">
|
<AppSection label="messages">
|
||||||
<label for="events" class="font-semibold text-secondaryLight">
|
<div class="flex flex-col flex-1 p-4 inline-flex">
|
||||||
{{ $t("socketio.events") }}
|
<label for="events" class="font-semibold text-secondaryLight">
|
||||||
</label>
|
{{ $t("socketio.events") }}
|
||||||
</div>
|
</label>
|
||||||
<div class="flex px-4">
|
</div>
|
||||||
<input
|
<div class="flex px-4">
|
||||||
id="event_name"
|
<input
|
||||||
v-model="communication.eventName"
|
id="event_name"
|
||||||
class="input"
|
v-model="communication.eventName"
|
||||||
name="event_name"
|
class="input"
|
||||||
:placeholder="$t('socketio.event_name')"
|
name="event_name"
|
||||||
type="text"
|
:placeholder="$t('socketio.event_name')"
|
||||||
autocomplete="off"
|
type="text"
|
||||||
:disabled="!connectionState"
|
autocomplete="off"
|
||||||
/>
|
:disabled="!connectionState"
|
||||||
</div>
|
|
||||||
<div class="flex items-center justify-between flex-1 p-4">
|
|
||||||
<label class="font-semibold text-secondaryLight">
|
|
||||||
{{ $t("socketio.communication") }}
|
|
||||||
</label>
|
|
||||||
<div class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="$t('add.new')"
|
|
||||||
svg="plus"
|
|
||||||
@click.native="addCommunicationInput"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="flex flex-1 p-4 items-center justify-between">
|
||||||
<div class="flex flex-col px-4 pb-4 space-y-2">
|
<label class="font-semibold text-secondaryLight">
|
||||||
<div
|
{{ $t("socketio.communication") }}
|
||||||
v-for="(input, index) of communication.inputs"
|
</label>
|
||||||
:key="`input-${index}`"
|
<div class="flex">
|
||||||
>
|
|
||||||
<div class="flex space-x-2">
|
|
||||||
<input
|
|
||||||
v-model="communication.inputs[index]"
|
|
||||||
class="input"
|
|
||||||
name="message"
|
|
||||||
:placeholder="$t('count.message', { count: index + 1 })"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off"
|
|
||||||
:disabled="!connectionState"
|
|
||||||
@keyup.enter="connectionState ? sendMessage() : null"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
<ButtonSecondary
|
||||||
v-if="index + 1 !== communication.inputs.length"
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
:title="$t('action.remove')"
|
:title="$t('add.new')"
|
||||||
svg="trash"
|
svg="plus"
|
||||||
color="red"
|
@click.native="addCommunicationInput"
|
||||||
outline
|
|
||||||
@click.native="removeCommunicationInput({ index })"
|
|
||||||
/>
|
|
||||||
<ButtonPrimary
|
|
||||||
v-if="index + 1 === communication.inputs.length"
|
|
||||||
id="send"
|
|
||||||
name="send"
|
|
||||||
:disabled="!connectionState"
|
|
||||||
:label="$t('action.send')"
|
|
||||||
@click.native="sendMessage"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="flex flex-col space-y-2 px-4 pb-4">
|
||||||
|
<div
|
||||||
|
v-for="(input, index) of communication.inputs"
|
||||||
|
:key="`input-${index}`"
|
||||||
|
>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<input
|
||||||
|
v-model="communication.inputs[index]"
|
||||||
|
class="input"
|
||||||
|
name="message"
|
||||||
|
:placeholder="$t('count.message', { count: index + 1 })"
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
:disabled="!connectionState"
|
||||||
|
@keyup.enter="connectionState ? sendMessage() : null"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-if="index + 1 !== communication.inputs.length"
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.remove')"
|
||||||
|
svg="trash"
|
||||||
|
color="red"
|
||||||
|
outline
|
||||||
|
@click.native="removeCommunicationInput({ index })"
|
||||||
|
/>
|
||||||
|
<ButtonPrimary
|
||||||
|
v-if="index + 1 === communication.inputs.length"
|
||||||
|
id="send"
|
||||||
|
name="send"
|
||||||
|
:disabled="!connectionState"
|
||||||
|
:label="$t('action.send')"
|
||||||
|
@click.native="sendMessage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AppSection>
|
||||||
</Pane>
|
</Pane>
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
import { defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { Splitpanes, Pane } from "splitpanes"
|
import { Splitpanes, Pane } from "splitpanes"
|
||||||
import "splitpanes/dist/splitpanes.css"
|
import "splitpanes/dist/splitpanes.css"
|
||||||
// All Socket.IO client version imports
|
// All Socket.IO client version imports
|
||||||
@@ -358,7 +235,6 @@ export default defineComponent({
|
|||||||
),
|
),
|
||||||
io: useStream(SIOSocket$, null, setSIOSocket),
|
io: useStream(SIOSocket$, null, setSIOSocket),
|
||||||
log: useStream(SIOLog$, [], setSIOLog),
|
log: useStream(SIOLog$, [], setSIOLog),
|
||||||
authTypeOptions: ref(null),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -368,9 +244,6 @@ export default defineComponent({
|
|||||||
eventName: "",
|
eventName: "",
|
||||||
inputs: [""],
|
inputs: [""],
|
||||||
},
|
},
|
||||||
authType: "None",
|
|
||||||
bearerToken: "",
|
|
||||||
authActive: true,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -430,17 +303,7 @@ export default defineComponent({
|
|||||||
this.path = "/socket.io"
|
this.path = "/socket.io"
|
||||||
}
|
}
|
||||||
const Client = socketIoClients[this.clientVersion]
|
const Client = socketIoClients[this.clientVersion]
|
||||||
if (this.authActive && this.authType === "Bearer") {
|
this.io = new Client(this.url, { path: this.path })
|
||||||
this.io = new Client(this.url, {
|
|
||||||
path: this.path,
|
|
||||||
auth: {
|
|
||||||
token: this.bearerToken,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.io = new Client(this.url, { path: this.path })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add ability to listen to all events
|
// Add ability to listen to all events
|
||||||
wildcard(Client.Manager)(this.io)
|
wildcard(Client.Manager)(this.io)
|
||||||
this.io.on("connect", () => {
|
this.io.on("connect", () => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
|
<Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
|
||||||
<Pane :size="COLUMN_LAYOUT ? 45 : 50" class="hide-scrollbar !overflow-auto">
|
<Pane :size="COLUMN_LAYOUT ? 45 : 50" class="hide-scrollbar !overflow-auto">
|
||||||
<div class="sticky top-0 z-10 flex p-4 bg-primary">
|
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||||
<div class="inline-flex flex-1 space-x-2">
|
<div class="space-x-2 flex-1 inline-flex">
|
||||||
<div class="flex flex-1">
|
<div class="flex flex-1">
|
||||||
<input
|
<input
|
||||||
id="server"
|
id="server"
|
||||||
@@ -10,21 +10,21 @@
|
|||||||
type="url"
|
type="url"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:class="{ error: !serverValid }"
|
:class="{ error: !serverValid }"
|
||||||
class="flex flex-1 w-full px-4 py-2 border rounded-l bg-primaryLight border-divider text-secondaryDark hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
class="bg-primaryLight border border-divider rounded-l flex flex-1 text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
:placeholder="$t('sse.url')"
|
:placeholder="$t('sse.url')"
|
||||||
:disabled="connectionSSEState"
|
:disabled="connectionSSEState"
|
||||||
@keyup.enter="serverValid ? toggleSSEConnection() : null"
|
@keyup.enter="serverValid ? toggleSSEConnection() : null"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="event-type"
|
for="event-type"
|
||||||
class="px-4 py-2 font-semibold truncate border-t border-b bg-primaryLight border-divider text-secondaryLight"
|
class="bg-primaryLight border-t border-b border-divider font-semibold text-secondaryLight py-2 px-4 truncate"
|
||||||
>
|
>
|
||||||
{{ $t("sse.event_type") }}
|
{{ $t("sse.event_type") }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="event-type"
|
id="event-type"
|
||||||
v-model="eventType"
|
v-model="eventType"
|
||||||
class="flex flex-1 w-full px-4 py-2 border rounded-r bg-primaryLight border-divider text-secondaryDark hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
class="bg-primaryLight border border-divider rounded-r flex flex-1 text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
:disabled="connectionSSEState"
|
:disabled="connectionSSEState"
|
||||||
@keyup.enter="serverValid ? toggleSSEConnection() : null"
|
@keyup.enter="serverValid ? toggleSSEConnection() : null"
|
||||||
@@ -45,7 +45,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</Pane>
|
</Pane>
|
||||||
<Pane :size="COLUMN_LAYOUT ? 65 : 50" class="hide-scrollbar !overflow-auto">
|
<Pane :size="COLUMN_LAYOUT ? 65 : 50" class="hide-scrollbar !overflow-auto">
|
||||||
<RealtimeLog :title="$t('sse.log')" :log="log" />
|
<AppSection label="response">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<RealtimeLog :title="$t('sse.log')" :log="log" />
|
||||||
|
<div id="result"></div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</AppSection>
|
||||||
</Pane>
|
</Pane>
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -13,131 +13,135 @@
|
|||||||
:size="COLUMN_LAYOUT ? 45 : 50"
|
:size="COLUMN_LAYOUT ? 45 : 50"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="sticky top-0 z-10 flex p-4 bg-primary">
|
<AppSection label="request">
|
||||||
<div class="inline-flex flex-1 space-x-2">
|
<div class="bg-primary flex p-4 top-0 z-10 sticky">
|
||||||
|
<div class="space-x-2 flex-1 inline-flex">
|
||||||
|
<input
|
||||||
|
id="websocket-url"
|
||||||
|
v-model="url"
|
||||||
|
class="bg-primaryLight border border-divider rounded text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
||||||
|
type="url"
|
||||||
|
autocomplete="off"
|
||||||
|
spellcheck="false"
|
||||||
|
:class="{ error: !urlValid }"
|
||||||
|
:placeholder="$t('websocket.url')"
|
||||||
|
:disabled="connectionState"
|
||||||
|
@keyup.enter="urlValid ? toggleConnection() : null"
|
||||||
|
/>
|
||||||
|
<ButtonPrimary
|
||||||
|
id="connect"
|
||||||
|
:disabled="!urlValid"
|
||||||
|
class="w-32"
|
||||||
|
name="connect"
|
||||||
|
:label="
|
||||||
|
!connectionState
|
||||||
|
? $t('action.connect')
|
||||||
|
: $t('action.disconnect')
|
||||||
|
"
|
||||||
|
:loading="connectingState"
|
||||||
|
@click.native="toggleConnection"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="bg-primary border-b border-dividerLight flex flex-1 top-upperPrimaryStickyFold pl-4 z-10 sticky items-center justify-between"
|
||||||
|
>
|
||||||
|
<label class="font-semibold text-secondaryLight">
|
||||||
|
{{ $t("websocket.protocols") }}
|
||||||
|
</label>
|
||||||
|
<div class="flex">
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.clear_all')"
|
||||||
|
svg="trash-2"
|
||||||
|
@click.native="clearContent"
|
||||||
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('add.new')"
|
||||||
|
svg="plus"
|
||||||
|
@click.native="addProtocol"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="(protocol, index) of protocols"
|
||||||
|
:key="`protocol-${index}`"
|
||||||
|
class="divide-dividerLight divide-x border-b border-dividerLight flex"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
id="websocket-url"
|
v-model="protocol.value"
|
||||||
v-model="url"
|
class="bg-transparent flex flex-1 py-2 px-4"
|
||||||
class="w-full px-4 py-2 border rounded bg-primaryLight border-divider text-secondaryDark hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
|
:placeholder="$t('count.protocol', { count: index + 1 })"
|
||||||
type="url"
|
name="message"
|
||||||
|
type="text"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
spellcheck="false"
|
@change="
|
||||||
:class="{ error: !urlValid }"
|
|
||||||
:placeholder="$t('websocket.url')"
|
|
||||||
:disabled="connectionState"
|
|
||||||
@keyup.enter="urlValid ? toggleConnection() : null"
|
|
||||||
/>
|
|
||||||
<ButtonPrimary
|
|
||||||
id="connect"
|
|
||||||
:disabled="!urlValid"
|
|
||||||
class="w-32"
|
|
||||||
name="connect"
|
|
||||||
:label="
|
|
||||||
!connectionState
|
|
||||||
? $t('action.connect')
|
|
||||||
: $t('action.disconnect')
|
|
||||||
"
|
|
||||||
:loading="connectingState"
|
|
||||||
@click.native="toggleConnection"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="sticky z-10 flex items-center justify-between flex-1 pl-4 border-b bg-primary border-dividerLight top-upperPrimaryStickyFold"
|
|
||||||
>
|
|
||||||
<label class="font-semibold text-secondaryLight">
|
|
||||||
{{ $t("websocket.protocols") }}
|
|
||||||
</label>
|
|
||||||
<div class="flex">
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="$t('action.clear_all')"
|
|
||||||
svg="trash-2"
|
|
||||||
@click.native="clearContent"
|
|
||||||
/>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="$t('add.new')"
|
|
||||||
svg="plus"
|
|
||||||
@click.native="addProtocol"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-for="(protocol, index) of protocols"
|
|
||||||
:key="`protocol-${index}`"
|
|
||||||
class="flex border-b divide-x divide-dividerLight border-dividerLight"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="protocol.value"
|
|
||||||
class="flex flex-1 px-4 py-2 bg-transparent"
|
|
||||||
:placeholder="$t('count.protocol', { count: index + 1 })"
|
|
||||||
name="message"
|
|
||||||
type="text"
|
|
||||||
autocomplete="off"
|
|
||||||
@change="
|
|
||||||
updateProtocol(index, {
|
|
||||||
value: $event.target.value,
|
|
||||||
active: protocol.active,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<ButtonSecondary
|
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
|
||||||
:title="
|
|
||||||
protocol.hasOwnProperty('active')
|
|
||||||
? protocol.active
|
|
||||||
? $t('action.turn_off')
|
|
||||||
: $t('action.turn_on')
|
|
||||||
: $t('action.turn_off')
|
|
||||||
"
|
|
||||||
:svg="
|
|
||||||
protocol.hasOwnProperty('active')
|
|
||||||
? protocol.active
|
|
||||||
? 'check-circle'
|
|
||||||
: 'circle'
|
|
||||||
: 'check-circle'
|
|
||||||
"
|
|
||||||
color="green"
|
|
||||||
@click.native="
|
|
||||||
updateProtocol(index, {
|
updateProtocol(index, {
|
||||||
value: protocol.value,
|
value: $event.target.value,
|
||||||
active: !protocol.active,
|
active: protocol.active,
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</span>
|
<span>
|
||||||
<span>
|
<ButtonSecondary
|
||||||
<ButtonSecondary
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
v-tippy="{ theme: 'tooltip' }"
|
:title="
|
||||||
:title="$t('action.remove')"
|
protocol.hasOwnProperty('active')
|
||||||
svg="trash"
|
? protocol.active
|
||||||
color="red"
|
? $t('action.turn_off')
|
||||||
@click.native="deleteProtocol({ index })"
|
: $t('action.turn_on')
|
||||||
|
: $t('action.turn_off')
|
||||||
|
"
|
||||||
|
:svg="
|
||||||
|
protocol.hasOwnProperty('active')
|
||||||
|
? protocol.active
|
||||||
|
? 'check-circle'
|
||||||
|
: 'circle'
|
||||||
|
: 'check-circle'
|
||||||
|
"
|
||||||
|
color="green"
|
||||||
|
@click.native="
|
||||||
|
updateProtocol(index, {
|
||||||
|
value: protocol.value,
|
||||||
|
active: !protocol.active,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-tippy="{ theme: 'tooltip' }"
|
||||||
|
:title="$t('action.remove')"
|
||||||
|
svg="trash"
|
||||||
|
color="red"
|
||||||
|
@click.native="deleteProtocol({ index })"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="protocols.length === 0"
|
||||||
|
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="`/images/states/${$colorMode.value}/add_category.svg`"
|
||||||
|
loading="lazy"
|
||||||
|
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
|
||||||
|
:alt="$t('empty.protocols')"
|
||||||
/>
|
/>
|
||||||
</span>
|
<span class="text-center mb-4">
|
||||||
</div>
|
{{ $t("empty.protocols") }}
|
||||||
<div
|
</span>
|
||||||
v-if="protocols.length === 0"
|
</div>
|
||||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
</AppSection>
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`/images/states/${$colorMode.value}/add_category.svg`"
|
|
||||||
loading="lazy"
|
|
||||||
class="inline-flex flex-col object-contain object-center w-16 h-16 my-4"
|
|
||||||
:alt="$t('empty.protocols')"
|
|
||||||
/>
|
|
||||||
<span class="mb-4 text-center">
|
|
||||||
{{ $t("empty.protocols") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</Pane>
|
</Pane>
|
||||||
<Pane
|
<Pane
|
||||||
:size="COLUMN_LAYOUT ? 65 : 50"
|
:size="COLUMN_LAYOUT ? 65 : 50"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<RealtimeLog :title="$t('websocket.log')" :log="log" />
|
<AppSection label="response">
|
||||||
|
<RealtimeLog :title="$t('websocket.log')" :log="log" />
|
||||||
|
</AppSection>
|
||||||
</Pane>
|
</Pane>
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
</Pane>
|
</Pane>
|
||||||
@@ -147,36 +151,38 @@
|
|||||||
min-size="20"
|
min-size="20"
|
||||||
class="hide-scrollbar !overflow-auto"
|
class="hide-scrollbar !overflow-auto"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col flex-1 p-4">
|
<AppSection label="messages">
|
||||||
<label
|
<div class="flex flex-col flex-1 p-4 inline-flex">
|
||||||
for="websocket-message"
|
<label
|
||||||
class="font-semibold text-secondaryLight"
|
for="websocket-message"
|
||||||
>
|
class="font-semibold text-secondaryLight"
|
||||||
{{ $t("websocket.communication") }}
|
>
|
||||||
</label>
|
{{ $t("websocket.communication") }}
|
||||||
</div>
|
</label>
|
||||||
<div class="flex px-4 space-x-2">
|
</div>
|
||||||
<input
|
<div class="flex space-x-2 px-4">
|
||||||
id="websocket-message"
|
<input
|
||||||
v-model="communication.input"
|
id="websocket-message"
|
||||||
name="message"
|
v-model="communication.input"
|
||||||
type="text"
|
name="message"
|
||||||
autocomplete="off"
|
type="text"
|
||||||
:disabled="!connectionState"
|
autocomplete="off"
|
||||||
:placeholder="$t('websocket.message')"
|
:disabled="!connectionState"
|
||||||
class="input"
|
:placeholder="$t('websocket.message')"
|
||||||
@keyup.enter="connectionState ? sendMessage() : null"
|
class="input"
|
||||||
@keyup.up="connectionState ? walkHistory('up') : null"
|
@keyup.enter="connectionState ? sendMessage() : null"
|
||||||
@keyup.down="connectionState ? walkHistory('down') : null"
|
@keyup.up="connectionState ? walkHistory('up') : null"
|
||||||
/>
|
@keyup.down="connectionState ? walkHistory('down') : null"
|
||||||
<ButtonPrimary
|
/>
|
||||||
id="send"
|
<ButtonPrimary
|
||||||
name="send"
|
id="send"
|
||||||
:disabled="!connectionState"
|
name="send"
|
||||||
:label="$t('action.send')"
|
:disabled="!connectionState"
|
||||||
@click.native="sendMessage"
|
:label="$t('action.send')"
|
||||||
/>
|
@click.native="sendMessage"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
</AppSection>
|
||||||
</Pane>
|
</Pane>
|
||||||
</Splitpanes>
|
</Splitpanes>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:spellcheck="spellcheck"
|
:spellcheck="spellcheck"
|
||||||
:autocapitalize="autocapitalize"
|
:autocapitalize="autocapitalize"
|
||||||
|
:autocorrect="spellcheck"
|
||||||
:class="styles"
|
:class="styles"
|
||||||
@input="updateSuggestions"
|
@input="updateSuggestions"
|
||||||
@keyup="updateSuggestions"
|
@keyup="updateSuggestions"
|
||||||
|
|||||||
@@ -16,23 +16,15 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-for="(locale, index) in $i18n.locales"
|
v-for="(locale, index) in $i18n.locales.filter(
|
||||||
:key="`locale-${index}`"
|
({ code }) => code !== $i18n.locale
|
||||||
|
)"
|
||||||
|
:key="`locale-${String(index)}`"
|
||||||
:to="switchLocalePath(locale.code)"
|
:to="switchLocalePath(locale.code)"
|
||||||
@click="language.tippy().hide()"
|
@click="$refs.language.tippy().hide()"
|
||||||
>
|
>
|
||||||
<SmartItem
|
<SmartItem :label="locale.name" />
|
||||||
:label="locale.name"
|
|
||||||
:active-info-icon="$i18n.locale === locale.code"
|
|
||||||
:info-icon="$i18n.locale === locale.code ? 'done' : null"
|
|
||||||
/>
|
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</tippy>
|
</tippy>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from "@nuxtjs/composition-api"
|
|
||||||
|
|
||||||
const language = ref<any | null>(null)
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="inline-flex items-center justify-center transition cursor-pointer flex-nowrap group hover:text-secondaryDark"
|
class="cursor-pointer flex-nowrap transition inline-flex items-center justify-center group hover:text-secondaryDark"
|
||||||
@click="$emit('change')"
|
@click="$emit('change')"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="checkbox"
|
for="checkbox"
|
||||||
class="pl-0 font-semibold align-middle cursor-pointer"
|
class="cursor-pointer font-semibold pl-0 align-middle"
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<span class="chip">
|
<span class="chip">
|
||||||
<i class="opacity-75 material-icons">attachment</i>
|
<i class="opacity-75 material-icons">attachment</i>
|
||||||
<span class="px-2 truncate max-w-32"><slot></slot></span>
|
<span class="max-w-64 px-2 truncate"><slot></slot></span>
|
||||||
|
<ButtonSecondary
|
||||||
|
class="rounded close-button"
|
||||||
|
svg="x"
|
||||||
|
@click.native="$emit('chip-delete')"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -13,6 +18,11 @@
|
|||||||
@apply rounded;
|
@apply rounded;
|
||||||
@apply pl-2;
|
@apply pl-2;
|
||||||
@apply pr-0.5;
|
@apply pr-0.5;
|
||||||
@apply bg-primaryDark;
|
@apply bg-transparent;
|
||||||
|
@apply border border-divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
@apply p-0.5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
@keyup="$emit('keyup', $event)"
|
@keyup="$emit('keyup', $event)"
|
||||||
@click="$emit('click', $event)"
|
@click="$emit('click', $event)"
|
||||||
@keydown="$emit('keydown', $event)"
|
@keydown="$emit('keydown', $event)"
|
||||||
@paste="handlePaste"
|
|
||||||
@compositionend="handleCompositionEnd"
|
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -73,7 +71,7 @@ export default defineComponent({
|
|||||||
highlightEnabled: true,
|
highlightEnabled: true,
|
||||||
highlightStyle: "",
|
highlightStyle: "",
|
||||||
caseSensitive: true,
|
caseSensitive: true,
|
||||||
fireOn: "input",
|
fireOn: "keydown",
|
||||||
fireOnEnabled: true,
|
fireOnEnabled: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -115,23 +113,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
handleCompositionEnd() {
|
handleChange() {
|
||||||
this.handleChange()
|
|
||||||
},
|
|
||||||
handlePaste(ev) {
|
|
||||||
this.handleChange()
|
|
||||||
this.$emit("paste", { event: ev, previousValue: this.internalValue })
|
|
||||||
},
|
|
||||||
handleChange(e = null) {
|
|
||||||
if (e && "inputType" in e && e.inputType === "insertCompositionText") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.debouncedHandler = debounce(function () {
|
this.debouncedHandler = debounce(function () {
|
||||||
if (this.$refs.editor) {
|
if (this.internalValue !== this.$refs.editor.textContent) {
|
||||||
if (this.internalValue !== this.$refs.editor.textContent) {
|
this.internalValue = this.$refs.editor.textContent
|
||||||
this.internalValue = this.$refs.editor.textContent
|
this.processHighlights()
|
||||||
this.processHighlights()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 5)
|
}, 5)
|
||||||
this.debouncedHandler()
|
this.debouncedHandler()
|
||||||
@@ -493,6 +479,7 @@ export default defineComponent({
|
|||||||
[contenteditable] {
|
[contenteditable] {
|
||||||
@apply select-text;
|
@apply select-text;
|
||||||
@apply text-secondaryDark;
|
@apply text-secondaryDark;
|
||||||
|
@apply font-medium;
|
||||||
|
|
||||||
&:empty {
|
&:empty {
|
||||||
@apply leading-loose;
|
@apply leading-loose;
|
||||||
|
|||||||