@@ -15,6 +15,8 @@ language: node_js
|
|||||||
node_js:
|
node_js:
|
||||||
- "12"
|
- "12"
|
||||||
|
|
||||||
|
os: linux
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
|
|||||||
50
README.md
50
README.md
@@ -3,10 +3,10 @@
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
API request builder - A free, fast, and beautiful alternative to Postman
|
<b>A free, fast & beautiful API request builder</b>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Helps you create your requests faster, saving you precious time on your development - <a href="https://postwoman.launchaco.com">Subscribe</a>
|
<i>Web alternative to Postman - Helps you create requests faster, saving precious time on development - <a href="https://postwoman.launchaco.com">Subscribe</a></i>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
@@ -69,8 +69,8 @@ _Customized themes are synced with local session storage_
|
|||||||
- Instant loading with [Service Workers](https://developers.google.com/web/fundamentals/primers/service-workers)
|
- Instant loading with [Service Workers](https://developers.google.com/web/fundamentals/primers/service-workers)
|
||||||
- Offline support
|
- Offline support
|
||||||
- Low RAM/memory and CPU usage
|
- Low RAM/memory and CPU usage
|
||||||
- [Add to Home Screen](https://developers.google.com/web/fundamentals/app-install-banners) (button in footer)
|
- Add to Home Screen
|
||||||
- [Desktop PWA](https://developers.google.com/web/progressive-web-apps/desktop) support (button in footer)
|
- Desktop PWA
|
||||||
- ([full features](https://developers.google.com/web/progressive-web-apps))
|
- ([full features](https://developers.google.com/web/progressive-web-apps))
|
||||||
|
|
||||||
🚀 **Request**: Retrieve response from endpoint instantly.
|
🚀 **Request**: Retrieve response from endpoint instantly.
|
||||||
@@ -190,38 +190,36 @@ _Requests with Pre-Request Scripts are indicated in History entries_
|
|||||||
🌎 **i18n β**: Experience the app in your own language.
|
🌎 **i18n β**: Experience the app in your own language.
|
||||||
|
|
||||||
1. Scroll down to the footer
|
1. Scroll down to the footer
|
||||||
2. Click "Choose Language" button
|
2. Click "Choose Language" icon button
|
||||||
3. Select your language from the menu
|
3. Select your language from the menu
|
||||||
|
|
||||||
_Keep in mind translations aren't available for all source and target language combinations_
|
_Keep in mind: Translations aren't available for all source and target language combinations_
|
||||||
|
|
||||||
**To provide a localized experience for users around the world, you can add you own translations.**
|
**To provide a localized experience for users around the world, you can add you own translations.**
|
||||||
|
|
||||||
- Add a new locale in `lang/`
|
|
||||||
|
|
||||||
Example: `lang/es-ES.js`
|
|
||||||
- Mention `code`, `name`, `iso` and `file` in `nuxt.config.js`
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
i18n: {
|
|
||||||
locales: [{
|
|
||||||
code: 'es',
|
|
||||||
name: 'Español',
|
|
||||||
iso: 'es-ES',
|
|
||||||
file: 'es-ES.js'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
_**All `i18n` contributions are welcome to `i18n` [branch](https://github.com/liyasthomas/postwoman/tree/i18n) only!**_
|
_**All `i18n` contributions are welcome to `i18n` [branch](https://github.com/liyasthomas/postwoman/tree/i18n) only!**_
|
||||||
|
|
||||||
📦 **Add-ons**: Official add-ons for Postwoman.
|
📦 **Add-ons**: Official add-ons for Postwoman.
|
||||||
|
|
||||||
- **[Postwoman Proxy β](https://github.com/postwoman-io/postwoman-proxy)** - A simple proxy server created for Postwoman
|
- **[Proxy β](https://github.com/postwoman-io/postwoman-proxy)** - A simple proxy server created for Postwoman
|
||||||
- **[Postwoman CLI β](https://github.com/postwoman-io/postwoman-cli)** - A CLI solution for Postwoman
|
- **[CLI β](https://github.com/postwoman-io/postwoman-cli)** - A CLI solution for Postwoman
|
||||||
|
- **Browser Extensions** - Browser extensions that simplifies access to Postwoman
|
||||||
|
|
||||||
_Add-ons are developed and maintained under **[Official Postwoman Organization](https://github.com/postwoman-io)**_
|
[ **Firefox**](https://addons.mozilla.org/en-US/firefox/addon/postwoman) ([GitHub](https://github.com/AndrewBastin/postwoman-firefox)) | [ **Chrome**](https://chrome.google.com/webstore/detail/postwoman-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld) ([GitHub](https://github.com/AndrewBastin/postwoman-chrome))
|
||||||
|
|
||||||
|
>**Extensions fixes `CORS` issues.**
|
||||||
|
|
||||||
|
_Add-ons are developed and maintained under **[Official Postwoman Organization](https://github.com/postwoman-io)**._
|
||||||
|
|
||||||
|
☁️ **Auth + Sync**: Sign in and sync in real-time.
|
||||||
|
|
||||||
|
**Sign in with:**
|
||||||
|
- Google
|
||||||
|
- GitHub
|
||||||
|
|
||||||
|
**Sync:**
|
||||||
|
- History
|
||||||
|
- Collections
|
||||||
|
|
||||||
**To find out more, please check out [Postwoman Wiki](https://github.com/liyasthomas/postwoman/wiki).**
|
**To find out more, please check out [Postwoman Wiki](https://github.com/liyasthomas/postwoman/wiki).**
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
-webkit-font-feature-settings: "liga";
|
-webkit-font-feature-settings: "liga";
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
font-feature-settings: "liga";
|
font-feature-settings: "liga";
|
||||||
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* poppins-500 - latin */
|
/* poppins-500 - latin */
|
||||||
|
|||||||
@@ -592,7 +592,6 @@ pre {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
height: 37px;
|
height: 37px;
|
||||||
background-color: var(--bg-dark-color);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
@@ -698,6 +697,7 @@ ol li {
|
|||||||
|
|
||||||
.show-on-large-screen {
|
.show-on-large-screen {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-response {
|
.info-response {
|
||||||
@@ -852,10 +852,7 @@ input[type="radio"]:checked + label + .tab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $responsiveWidth) {
|
@media (max-width: $responsiveWidth) {
|
||||||
.content {
|
.content,
|
||||||
flex-flow: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.columns {
|
.columns {
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
}
|
}
|
||||||
|
|||||||
2
build.js
2
build.js
@@ -53,7 +53,7 @@ try {
|
|||||||
|
|
||||||
// Write version data into a file
|
// Write version data into a file
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
PW_BUILD_DATA_DIR + "/version.json",
|
`${PW_BUILD_DATA_DIR}/version.json`,
|
||||||
JSON.stringify(version)
|
JSON.stringify(version)
|
||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -17,7 +17,11 @@
|
|||||||
<div slot="body">
|
<div slot="body">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<input type="text" v-model="name" :placeholder="$t('my_new_collection')" />
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="name"
|
||||||
|
:placeholder="$t('my_new_collection')"
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,6 +56,10 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
addNewCollection() {
|
addNewCollection() {
|
||||||
|
if (!this.$data.name) {
|
||||||
|
this.$toast.info("Please provide a valid name for the collection");
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.$store.commit("postwoman/addNewCollection", {
|
this.$store.commit("postwoman/addNewCollection", {
|
||||||
name: this.$data.name
|
name: this.$data.name
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,11 @@
|
|||||||
<div slot="body">
|
<div slot="body">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<input type="text" v-model="name" :placeholder="$t('my_new_folder')" />
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="name"
|
||||||
|
:placeholder="$t('my_new_folder')"
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
saveCollection() {
|
saveCollection() {
|
||||||
|
if (!this.$data.name) {
|
||||||
|
this.$toast.info("Please provide a valid name for the collection");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const collectionUpdated = {
|
const collectionUpdated = {
|
||||||
...this.$props.editingCollection,
|
...this.$props.editingCollection,
|
||||||
name: this.$data.name
|
name: this.$data.name
|
||||||
|
|||||||
@@ -11,15 +11,23 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div slot="body">
|
|
||||||
<textarea v-model="collectionJson" rows="8"></textarea>
|
|
||||||
</div>
|
|
||||||
<div slot="footer">
|
|
||||||
<div class="flex-wrap">
|
<div class="flex-wrap">
|
||||||
<span>
|
<span
|
||||||
|
v-tooltip="{
|
||||||
|
content: !fb.currentUser
|
||||||
|
? $t('login_first')
|
||||||
|
: $t('replace_current')
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
:disabled="!fb.currentUser"
|
||||||
|
class="icon"
|
||||||
|
@click="syncCollections"
|
||||||
|
>
|
||||||
|
<i class="material-icons">folder_shared</i>
|
||||||
|
<span>{{ $t("import_from_sync") }}</span>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
<button
|
<button
|
||||||
class="icon"
|
class="icon"
|
||||||
@click="openDialogChooseFileToReplaceWith"
|
@click="openDialogChooseFileToReplaceWith"
|
||||||
@@ -32,6 +40,7 @@
|
|||||||
@change="replaceWithJSON"
|
@change="replaceWithJSON"
|
||||||
style="display: none;"
|
style="display: none;"
|
||||||
ref="inputChooseFileToReplaceWith"
|
ref="inputChooseFileToReplaceWith"
|
||||||
|
accept="application/json"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -39,18 +48,24 @@
|
|||||||
@click="openDialogChooseFileToImportFrom"
|
@click="openDialogChooseFileToImportFrom"
|
||||||
v-tooltip="$t('preserve_current')"
|
v-tooltip="$t('preserve_current')"
|
||||||
>
|
>
|
||||||
<i class="material-icons">folder_shared</i>
|
<i class="material-icons">folder_special</i>
|
||||||
<span>{{ $t("import_json") }}</span>
|
<span>{{ $t("import_json") }}</span>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
@change="importFromJSON"
|
@change="importFromJSON"
|
||||||
style="display: none;"
|
style="display: none;"
|
||||||
ref="inputChooseFileToImportFrom"
|
ref="inputChooseFileToImportFrom"
|
||||||
|
accept="application/json"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</span>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div slot="body">
|
||||||
|
<textarea v-model="collectionJson" rows="8"></textarea>
|
||||||
|
</div>
|
||||||
|
<div slot="footer">
|
||||||
<div class="flex-wrap">
|
<div class="flex-wrap">
|
||||||
<span></span>
|
<span></span>
|
||||||
<span>
|
<span>
|
||||||
@@ -71,7 +86,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { fb } from "../../functions/fb";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fb
|
||||||
|
};
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
show: Boolean
|
show: Boolean
|
||||||
},
|
},
|
||||||
@@ -101,6 +123,7 @@ export default {
|
|||||||
this.$store.commit("postwoman/replaceCollections", collections);
|
this.$store.commit("postwoman/replaceCollections", collections);
|
||||||
};
|
};
|
||||||
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0]);
|
reader.readAsText(this.$refs.inputChooseFileToReplaceWith.files[0]);
|
||||||
|
this.fileImported();
|
||||||
},
|
},
|
||||||
importFromJSON() {
|
importFromJSON() {
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
@@ -110,6 +133,7 @@ export default {
|
|||||||
this.$store.commit("postwoman/importCollections", collections);
|
this.$store.commit("postwoman/importCollections", collections);
|
||||||
};
|
};
|
||||||
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0]);
|
reader.readAsText(this.$refs.inputChooseFileToImportFrom.files[0]);
|
||||||
|
this.fileImported();
|
||||||
},
|
},
|
||||||
exportJSON() {
|
exportJSON() {
|
||||||
let text = this.collectionJson;
|
let text = this.collectionJson;
|
||||||
@@ -125,6 +149,18 @@ export default {
|
|||||||
document.body.appendChild(anchor);
|
document.body.appendChild(anchor);
|
||||||
anchor.click();
|
anchor.click();
|
||||||
document.body.removeChild(anchor);
|
document.body.removeChild(anchor);
|
||||||
|
this.$toast.success(this.$t("download_started"), {
|
||||||
|
icon: "done"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
syncCollections() {
|
||||||
|
this.$store.commit("postwoman/replaceCollections", fb.currentCollections);
|
||||||
|
this.fileImported();
|
||||||
|
},
|
||||||
|
fileImported() {
|
||||||
|
this.$toast.info(this.$t("file_imported"), {
|
||||||
|
icon: "folder_shared"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,12 +47,8 @@ TODO:
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button class="icon" @click="displayModalImportExport(true)">
|
||||||
class="icon"
|
{{ $t("import_export") }}
|
||||||
@click="displayModalImportExport(true)"
|
|
||||||
v-tooltip="$t('import_export')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">import_export</i>
|
|
||||||
</button>
|
</button>
|
||||||
<!-- <a
|
<!-- <a
|
||||||
href="https://github.com/liyasthomas/postwoman/wiki/Collections"
|
href="https://github.com/liyasthomas/postwoman/wiki/Collections"
|
||||||
@@ -90,12 +86,18 @@ TODO:
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</virtual-list>
|
</virtual-list>
|
||||||
|
<nuxt-link :to="localePath('doc')" :aria-label="$t('documentation')">
|
||||||
|
<button class="icon">
|
||||||
|
<i class="material-icons">books</i>
|
||||||
|
<span>{{ $t("generate_docs") }}</span>
|
||||||
|
</button>
|
||||||
|
</nuxt-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.virtual-list {
|
.virtual-list {
|
||||||
max-height: calc(100vh - 232px);
|
max-height: calc(100vh - 276px);
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
@@ -106,6 +108,7 @@ ul {
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import collection from "./collection";
|
import collection from "./collection";
|
||||||
|
import { fb } from "../../functions/fb";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -170,11 +173,13 @@ export default {
|
|||||||
this.$data.editingCollection = collection;
|
this.$data.editingCollection = collection;
|
||||||
this.$data.editingCollectionIndex = collectionIndex;
|
this.$data.editingCollectionIndex = collectionIndex;
|
||||||
this.displayModalEdit(true);
|
this.displayModalEdit(true);
|
||||||
|
this.syncCollections();
|
||||||
},
|
},
|
||||||
addFolder(collection, collectionIndex) {
|
addFolder(collection, collectionIndex) {
|
||||||
this.$data.editingCollection = collection;
|
this.$data.editingCollection = collection;
|
||||||
this.$data.editingCollectionIndex = collectionIndex;
|
this.$data.editingCollectionIndex = collectionIndex;
|
||||||
this.displayModalAddFolder(true);
|
this.displayModalAddFolder(true);
|
||||||
|
this.syncCollections();
|
||||||
},
|
},
|
||||||
editFolder(payload) {
|
editFolder(payload) {
|
||||||
const { collectionIndex, folder, folderIndex } = payload;
|
const { collectionIndex, folder, folderIndex } = payload;
|
||||||
@@ -183,6 +188,7 @@ export default {
|
|||||||
this.$data.editingFolder = folder;
|
this.$data.editingFolder = folder;
|
||||||
this.$data.editingFolderIndex = folderIndex;
|
this.$data.editingFolderIndex = folderIndex;
|
||||||
this.displayModalEditFolder(true);
|
this.displayModalEditFolder(true);
|
||||||
|
this.syncCollections();
|
||||||
},
|
},
|
||||||
editRequest(payload) {
|
editRequest(payload) {
|
||||||
const { request, collectionIndex, folderIndex, requestIndex } = payload;
|
const { request, collectionIndex, folderIndex, requestIndex } = payload;
|
||||||
@@ -191,6 +197,7 @@ export default {
|
|||||||
this.$data.editingRequest = request;
|
this.$data.editingRequest = request;
|
||||||
this.$data.editingRequestIndex = requestIndex;
|
this.$data.editingRequestIndex = requestIndex;
|
||||||
this.displayModalEditRequest(true);
|
this.displayModalEditRequest(true);
|
||||||
|
this.syncCollections();
|
||||||
},
|
},
|
||||||
resetSelectedData() {
|
resetSelectedData() {
|
||||||
this.$data.editingCollection = undefined;
|
this.$data.editingCollection = undefined;
|
||||||
@@ -199,6 +206,15 @@ export default {
|
|||||||
this.$data.editingFolderIndex = undefined;
|
this.$data.editingFolderIndex = undefined;
|
||||||
this.$data.editingRequest = undefined;
|
this.$data.editingRequest = undefined;
|
||||||
this.$data.editingRequestIndex = undefined;
|
this.$data.editingRequestIndex = undefined;
|
||||||
|
},
|
||||||
|
syncCollections() {
|
||||||
|
if (fb.currentUser !== null) {
|
||||||
|
if (fb.currentSettings[0].value) {
|
||||||
|
fb.writeCollections(
|
||||||
|
JSON.parse(JSON.stringify(this.$store.state.postwoman.collections))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-wrap">
|
<div class="flex-wrap">
|
||||||
<div>
|
<div>
|
||||||
<button class="icon" @click="selectRequest()" v-tooltip="$t('use_request')">
|
<button
|
||||||
|
class="icon"
|
||||||
|
@click="selectRequest()"
|
||||||
|
v-tooltip="$t('use_request')"
|
||||||
|
>
|
||||||
<i class="material-icons">insert_drive_file</i>
|
<i class="material-icons">insert_drive_file</i>
|
||||||
<span>{{ request.name }}</span>
|
<span>{{ request.name }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
85
components/firebase/feeds.vue
Normal file
85
components/firebase/feeds.vue
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<virtual-list
|
||||||
|
v-if="fb.currentFeeds.length !== 0"
|
||||||
|
class="virtual-list"
|
||||||
|
:class="{ filled: fb.currentFeeds.length }"
|
||||||
|
:size="56"
|
||||||
|
:remain="Math.min(8, fb.currentFeeds.length)"
|
||||||
|
>
|
||||||
|
<ul v-for="feed in fb.currentFeeds" :key="feed.id">
|
||||||
|
<div class="show-on-large-screen">
|
||||||
|
<li>
|
||||||
|
<input
|
||||||
|
:aria-label="$t('label')"
|
||||||
|
type="text"
|
||||||
|
readonly
|
||||||
|
:value="feed.label"
|
||||||
|
:placeholder="$t('no_label')"
|
||||||
|
class="bg-color"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<button class="icon" @click="saveFeed(feed)">
|
||||||
|
<i class="material-icons">get_app</i>
|
||||||
|
</button>
|
||||||
|
<button class="icon" @click="deleteFeed(feed)">
|
||||||
|
<i class="material-icons">delete</i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</virtual-list>
|
||||||
|
<ul v-else>
|
||||||
|
<li>
|
||||||
|
<label class="info">{{ $t("empty") }}</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.virtual-list {
|
||||||
|
max-height: calc(100vh - 288px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-color {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { fb } from "../../functions/fb";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
VirtualList: () => import("vue-virtual-scroll-list")
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fb
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
deleteFeed(feed) {
|
||||||
|
fb.deleteFeed(feed.id);
|
||||||
|
this.$toast.error(this.$t("deleted"), {
|
||||||
|
icon: "delete"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
saveFeed(feed) {
|
||||||
|
const dataToWrite = JSON.stringify(feed.message, null, 2);
|
||||||
|
const file = new Blob([dataToWrite], { type: "application/json" });
|
||||||
|
const a = document.createElement("a"),
|
||||||
|
url = URL.createObjectURL(file);
|
||||||
|
a.href = url;
|
||||||
|
a.download = (feed.label + " on " + Date()).replace(/\./g, "[dot]");
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
this.$toast.success(this.$t("download_started"), {
|
||||||
|
icon: "done"
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
55
components/firebase/inputform.vue
Normal file
55
components/firebase/inputform.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<input
|
||||||
|
:aria-label="$t('label')"
|
||||||
|
type="text"
|
||||||
|
autofocus
|
||||||
|
v-model="message"
|
||||||
|
:placeholder="$t('paste_a_collection')"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<input
|
||||||
|
:aria-label="$t('label')"
|
||||||
|
type="text"
|
||||||
|
autofocus
|
||||||
|
v-model="label"
|
||||||
|
:placeholder="$t('label')"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<button
|
||||||
|
class="icon"
|
||||||
|
:disabled="!(this.message && this.label)"
|
||||||
|
value="Save"
|
||||||
|
@click="formPost"
|
||||||
|
>
|
||||||
|
<i class="material-icons">add</i>
|
||||||
|
<span>Add</span>
|
||||||
|
</button>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { fb } from "../../functions/fb";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
message: null,
|
||||||
|
label: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formPost() {
|
||||||
|
fb.writeFeeds(this.message, this.label);
|
||||||
|
this.message = null;
|
||||||
|
this.label = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
115
components/firebase/login.vue
Normal file
115
components/firebase/login.vue
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<v-popover>
|
||||||
|
<button class="icon" v-tooltip="$t('login_with')">
|
||||||
|
<i class="material-icons">vpn_key</i>
|
||||||
|
</button>
|
||||||
|
<template slot="popover">
|
||||||
|
<div>
|
||||||
|
<button class="icon" @click="signInWithGoogle" v-close-popover>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="material-icons"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>Google</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="icon" @click="signInWithGithub" v-close-popover>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="material-icons"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>GitHub</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import firebase from "firebase/app";
|
||||||
|
import { fb } from "../../functions/fb";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fb
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
signInWithGoogle() {
|
||||||
|
const provider = new firebase.auth.GoogleAuthProvider();
|
||||||
|
firebase
|
||||||
|
.auth()
|
||||||
|
.signInWithPopup(provider)
|
||||||
|
.then(res => {
|
||||||
|
if (res.additionalUserInfo.isNewUser) {
|
||||||
|
this.$toast.info(this.$t("turn_on") + " " + this.$t("sync"), {
|
||||||
|
icon: "sync",
|
||||||
|
duration: null,
|
||||||
|
closeOnSwipe: false,
|
||||||
|
action: {
|
||||||
|
text: this.$t("yes"),
|
||||||
|
onClick: (e, toastObject) => {
|
||||||
|
fb.writeSettings("syncHistory", false);
|
||||||
|
fb.writeSettings("syncCollections", true);
|
||||||
|
this.$router.push({ path: "/settings" });
|
||||||
|
toastObject.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$toast.show(err.message || err, {
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
signInWithGithub() {
|
||||||
|
const provider = new firebase.auth.GithubAuthProvider();
|
||||||
|
firebase
|
||||||
|
.auth()
|
||||||
|
.signInWithPopup(provider)
|
||||||
|
.then(res => {
|
||||||
|
if (res.additionalUserInfo.isNewUser) {
|
||||||
|
this.$toast.info(this.$t("turn_on") + " " + this.$t("sync"), {
|
||||||
|
icon: "sync",
|
||||||
|
duration: null,
|
||||||
|
closeOnSwipe: false,
|
||||||
|
action: {
|
||||||
|
text: this.$t("yes"),
|
||||||
|
onClick: (e, toastObject) => {
|
||||||
|
fb.writeSettings("syncHistory", false);
|
||||||
|
fb.writeSettings("syncCollections", true);
|
||||||
|
this.$router.push({ path: "/settings" });
|
||||||
|
toastObject.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$toast.show(err.message || err, {
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
135
components/graphql/queryeditor.vue
Normal file
135
components/graphql/queryeditor.vue
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<pre ref="editor"></pre>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const DEFAULT_THEME = "twilight";
|
||||||
|
|
||||||
|
import ace from "ace-builds";
|
||||||
|
import * as gql from "graphql";
|
||||||
|
import "ace-builds/webpack-resolver";
|
||||||
|
import debounce from "../../functions/utils/debounce";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
lang: {
|
||||||
|
type: String,
|
||||||
|
default: "json"
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
editor: null,
|
||||||
|
cacheValue: "",
|
||||||
|
validationSchema: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
value(value) {
|
||||||
|
if (value !== this.cacheValue) {
|
||||||
|
this.editor.session.setValue(value, 1);
|
||||||
|
this.cacheValue = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
theme() {
|
||||||
|
this.editor.setTheme("ace/theme/" + this.defineTheme());
|
||||||
|
},
|
||||||
|
lang(value) {
|
||||||
|
this.editor.getSession().setMode("ace/mode/" + value);
|
||||||
|
},
|
||||||
|
options(value) {
|
||||||
|
this.editor.setOptions(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
const editor = ace.edit(this.$refs.editor, {
|
||||||
|
theme: "ace/theme/" + this.defineTheme(),
|
||||||
|
mode: "ace/mode/" + this.lang,
|
||||||
|
...this.options
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.value) editor.setValue(this.value, 1);
|
||||||
|
|
||||||
|
this.editor = editor;
|
||||||
|
this.cacheValue = this.value;
|
||||||
|
|
||||||
|
editor.on("change", () => {
|
||||||
|
const content = editor.getValue();
|
||||||
|
this.$emit("input", content);
|
||||||
|
this.parseContents(content);
|
||||||
|
this.cacheValue = content;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.parseContents(this.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
defineTheme() {
|
||||||
|
if (this.theme) {
|
||||||
|
return this.theme;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
this.$store.state.postwoman.settings.THEME_ACE_EDITOR || DEFAULT_THEME
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setValidationSchema(schema) {
|
||||||
|
this.validationSchema = schema;
|
||||||
|
this.parseContents(this.cacheValue);
|
||||||
|
},
|
||||||
|
|
||||||
|
parseContents: debounce(function(content) {
|
||||||
|
if (content !== "") {
|
||||||
|
try {
|
||||||
|
const doc = gql.parse(content);
|
||||||
|
|
||||||
|
if (this.validationSchema) {
|
||||||
|
this.editor.session.setAnnotations(
|
||||||
|
gql.validate(this.validationSchema, doc).map(err => {
|
||||||
|
return {
|
||||||
|
row: err.locations[0].line - 1,
|
||||||
|
column: err.locations[0].column - 1,
|
||||||
|
text: err.message,
|
||||||
|
type: "error"
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.editor.session.setAnnotations([
|
||||||
|
{
|
||||||
|
row: e.locations[0].line - 1,
|
||||||
|
column: e.locations[0].column - 1,
|
||||||
|
text: e.message,
|
||||||
|
type: "error"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.editor.session.setAnnotations([]);
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.editor.destroy();
|
||||||
|
this.editor.container.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<pw-section
|
<pw-section class="green" icon="history" :label="$t('history')" ref="history">
|
||||||
class="green"
|
|
||||||
icon="history"
|
|
||||||
:label="$t('history')"
|
|
||||||
ref="history"
|
|
||||||
>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li id="filter-history">
|
<li id="filter-history">
|
||||||
<input
|
<input
|
||||||
@@ -26,8 +21,10 @@
|
|||||||
<button
|
<button
|
||||||
class="icon"
|
class="icon"
|
||||||
:class="{ stared: entry.star }"
|
:class="{ stared: entry.star }"
|
||||||
@click="toggleStar(index)"
|
@click="toggleStar(entry)"
|
||||||
v-tooltip="{ content: !entry.star ? $t('add_star') : $t('remove_star') }"
|
v-tooltip="{
|
||||||
|
content: !entry.star ? $t('add_star') : $t('remove_star')
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<i class="material-icons">
|
<i class="material-icons">
|
||||||
{{ entry.star ? "star" : "star_border" }}
|
{{ entry.star ? "star" : "star_border" }}
|
||||||
@@ -238,7 +235,9 @@
|
|||||||
</v-popover>
|
</v-popover>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-wrap" v-else>
|
<div class="flex-wrap" v-else>
|
||||||
<label for="clear-history-button" class="info">{{ $t("are_you_sure") }}</label>
|
<label for="clear-history-button" class="info">
|
||||||
|
{{ $t("are_you_sure") }}
|
||||||
|
</label>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="icon"
|
class="icon"
|
||||||
@@ -332,6 +331,7 @@ ol li {
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { findStatusGroup } from "../pages/index";
|
import { findStatusGroup } from "../pages/index";
|
||||||
|
import { fb } from "../functions/fb";
|
||||||
|
|
||||||
const updateOnLocalStorage = (propertyName, property) =>
|
const updateOnLocalStorage = (propertyName, property) =>
|
||||||
window.localStorage.setItem(propertyName, JSON.stringify(property));
|
window.localStorage.setItem(propertyName, JSON.stringify(property));
|
||||||
@@ -342,11 +342,11 @@ export default {
|
|||||||
VirtualList: () => import("vue-virtual-scroll-list")
|
VirtualList: () => import("vue-virtual-scroll-list")
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const localStorageHistory = JSON.parse(
|
|
||||||
window.localStorage.getItem("history")
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
history: localStorageHistory || [],
|
history:
|
||||||
|
fb.currentUser !== null
|
||||||
|
? fb.currentHistory
|
||||||
|
: JSON.parse(window.localStorage.getItem("history")) || [],
|
||||||
filterText: "",
|
filterText: "",
|
||||||
showFilter: false,
|
showFilter: false,
|
||||||
isClearingHistory: false,
|
isClearingHistory: false,
|
||||||
@@ -360,6 +360,10 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
filteredHistory() {
|
filteredHistory() {
|
||||||
|
this.history =
|
||||||
|
fb.currentUser !== null
|
||||||
|
? fb.currentHistory
|
||||||
|
: JSON.parse(window.localStorage.getItem("history")) || [];
|
||||||
return this.history.filter(entry => {
|
return this.history.filter(entry => {
|
||||||
const filterText = this.filterText.toLowerCase();
|
const filterText = this.filterText.toLowerCase();
|
||||||
return Object.keys(entry).some(key => {
|
return Object.keys(entry).some(key => {
|
||||||
@@ -372,6 +376,9 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clearHistory() {
|
clearHistory() {
|
||||||
|
if (fb.currentUser !== null) {
|
||||||
|
fb.clearHistory();
|
||||||
|
}
|
||||||
this.history = [];
|
this.history = [];
|
||||||
this.filterText = "";
|
this.filterText = "";
|
||||||
this.disableHistoryClearing();
|
this.disableHistoryClearing();
|
||||||
@@ -392,6 +399,9 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
deleteHistory(entry) {
|
deleteHistory(entry) {
|
||||||
|
if (fb.currentUser !== null) {
|
||||||
|
fb.deleteHistory(entry);
|
||||||
|
}
|
||||||
this.history.splice(this.history.indexOf(entry), 1);
|
this.history.splice(this.history.indexOf(entry), 1);
|
||||||
if (this.history.length === 0) {
|
if (this.history.length === 0) {
|
||||||
this.filterText = "";
|
this.filterText = "";
|
||||||
@@ -498,8 +508,11 @@ export default {
|
|||||||
toggleCollapse() {
|
toggleCollapse() {
|
||||||
this.showMore = !this.showMore;
|
this.showMore = !this.showMore;
|
||||||
},
|
},
|
||||||
toggleStar(index) {
|
toggleStar(entry) {
|
||||||
this.history[index]["star"] = !this.history[index]["star"];
|
if (fb.currentUser !== null) {
|
||||||
|
fb.toggleStar(entry, !entry.star);
|
||||||
|
}
|
||||||
|
entry.star = !entry.star;
|
||||||
updateOnLocalStorage("history", this.history);
|
updateOnLocalStorage("history", this.history);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
service cloud.firestore {
|
service cloud.firestore {
|
||||||
match /databases/{database}/documents {
|
match /databases/{database}/documents {
|
||||||
match /{document=**} {
|
match /{document=**} {
|
||||||
allow read, write;
|
allow read, write: if request.auth.uid != null;
|
||||||
|
}
|
||||||
|
// Make sure the uid of the requesting user matches name of the user
|
||||||
|
// document. The wildcard expression {userId} makes the userId variable
|
||||||
|
// available in rules.
|
||||||
|
match /users/{userId} {
|
||||||
|
allow read, update, delete: if request.auth.uid == userId;
|
||||||
|
allow create: if request.auth.uid != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
192
functions/fb.js
Normal file
192
functions/fb.js
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import firebase from "firebase/app";
|
||||||
|
import "firebase/firestore";
|
||||||
|
import "firebase/auth";
|
||||||
|
|
||||||
|
// Initialize Firebase, copied from cloud console
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyCMsFreESs58-hRxTtiqQrIcimh4i1wbsM",
|
||||||
|
authDomain: "postwoman-api.firebaseapp.com",
|
||||||
|
databaseURL: "https://postwoman-api.firebaseio.com",
|
||||||
|
projectId: "postwoman-api",
|
||||||
|
storageBucket: "postwoman-api.appspot.com",
|
||||||
|
messagingSenderId: "421993993223",
|
||||||
|
appId: "1:421993993223:web:ec0baa8ee8c02ffa1fc6a2",
|
||||||
|
measurementId: "G-ERJ6025CEB"
|
||||||
|
};
|
||||||
|
firebase.initializeApp(firebaseConfig);
|
||||||
|
|
||||||
|
// a reference to the users collection
|
||||||
|
const usersCollection = firebase.firestore().collection("users");
|
||||||
|
|
||||||
|
// the shared state object that any vue component
|
||||||
|
// can get access to
|
||||||
|
export const fb = {
|
||||||
|
currentUser: {},
|
||||||
|
currentFeeds: [],
|
||||||
|
currentSettings: [],
|
||||||
|
currentHistory: [],
|
||||||
|
currentCollections: [],
|
||||||
|
writeFeeds: async (message, label) => {
|
||||||
|
const dt = {
|
||||||
|
createdOn: new Date(),
|
||||||
|
author: fb.currentUser.uid,
|
||||||
|
author_name: fb.currentUser.displayName,
|
||||||
|
author_image: fb.currentUser.photoURL,
|
||||||
|
message,
|
||||||
|
label
|
||||||
|
};
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("feeds")
|
||||||
|
.add(dt)
|
||||||
|
.catch(e => console.error("error inserting", dt, e));
|
||||||
|
},
|
||||||
|
deleteFeed: id => {
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("feeds")
|
||||||
|
.doc(id)
|
||||||
|
.delete()
|
||||||
|
.catch(e => console.error("error deleting", id, e));
|
||||||
|
},
|
||||||
|
writeSettings: async (setting, value) => {
|
||||||
|
const st = {
|
||||||
|
updatedOn: new Date(),
|
||||||
|
author: fb.currentUser.uid,
|
||||||
|
author_name: fb.currentUser.displayName,
|
||||||
|
author_image: fb.currentUser.photoURL,
|
||||||
|
name: setting,
|
||||||
|
value
|
||||||
|
};
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("settings")
|
||||||
|
.doc(setting)
|
||||||
|
.set(st)
|
||||||
|
.catch(e => console.error("error updating", st, e));
|
||||||
|
},
|
||||||
|
writeHistory: async entry => {
|
||||||
|
const hs = entry;
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("history")
|
||||||
|
.add(hs)
|
||||||
|
.catch(e => console.error("error inserting", hs, e));
|
||||||
|
},
|
||||||
|
deleteHistory: entry => {
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("history")
|
||||||
|
.doc(entry.id)
|
||||||
|
.delete()
|
||||||
|
.catch(e => console.error("error deleting", entry, e));
|
||||||
|
},
|
||||||
|
clearHistory: () => {
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("history")
|
||||||
|
.get()
|
||||||
|
.then(({ docs }) => {
|
||||||
|
docs.forEach(e => fb.deleteHistory(e));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggleStar: (entry, value) => {
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("history")
|
||||||
|
.doc(entry.id)
|
||||||
|
.update({ star: value })
|
||||||
|
.catch(e => console.error("error deleting", entry, e));
|
||||||
|
},
|
||||||
|
writeCollections: async collection => {
|
||||||
|
const cl = {
|
||||||
|
updatedOn: new Date(),
|
||||||
|
author: fb.currentUser.uid,
|
||||||
|
author_name: fb.currentUser.displayName,
|
||||||
|
author_image: fb.currentUser.photoURL,
|
||||||
|
collection: collection
|
||||||
|
};
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("collections")
|
||||||
|
.doc("sync")
|
||||||
|
.set(cl)
|
||||||
|
.catch(e => console.error("error updating", cl, e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// When a user logs in or out, save that in the store
|
||||||
|
firebase.auth().onAuthStateChanged(user => {
|
||||||
|
if (user) {
|
||||||
|
fb.currentUser = user;
|
||||||
|
fb.currentUser.providerData.forEach(profile => {
|
||||||
|
let us = {
|
||||||
|
updatedOn: new Date(),
|
||||||
|
provider: profile.providerId,
|
||||||
|
name: profile.displayName,
|
||||||
|
email: profile.email,
|
||||||
|
photoUrl: profile.photoURL,
|
||||||
|
uid: profile.uid
|
||||||
|
};
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.set(us)
|
||||||
|
.catch(e => console.error("error updating", us, e));
|
||||||
|
});
|
||||||
|
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("feeds")
|
||||||
|
.orderBy("createdOn", "desc")
|
||||||
|
.onSnapshot(feedsRef => {
|
||||||
|
const feeds = [];
|
||||||
|
feedsRef.forEach(doc => {
|
||||||
|
const feed = doc.data();
|
||||||
|
feed.id = doc.id;
|
||||||
|
feeds.push(feed);
|
||||||
|
});
|
||||||
|
fb.currentFeeds = feeds;
|
||||||
|
});
|
||||||
|
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("settings")
|
||||||
|
.onSnapshot(settingsRef => {
|
||||||
|
const settings = [];
|
||||||
|
settingsRef.forEach(doc => {
|
||||||
|
const setting = doc.data();
|
||||||
|
setting.id = doc.id;
|
||||||
|
settings.push(setting);
|
||||||
|
});
|
||||||
|
fb.currentSettings = settings;
|
||||||
|
});
|
||||||
|
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("history")
|
||||||
|
.onSnapshot(historyRef => {
|
||||||
|
const history = [];
|
||||||
|
historyRef.forEach(doc => {
|
||||||
|
const entry = doc.data();
|
||||||
|
entry.id = doc.id;
|
||||||
|
history.push(entry);
|
||||||
|
});
|
||||||
|
fb.currentHistory = history;
|
||||||
|
});
|
||||||
|
|
||||||
|
usersCollection
|
||||||
|
.doc(fb.currentUser.uid)
|
||||||
|
.collection("collections")
|
||||||
|
.onSnapshot(collectionsRef => {
|
||||||
|
const collections = [];
|
||||||
|
collectionsRef.forEach(doc => {
|
||||||
|
const collection = doc.data();
|
||||||
|
collection.id = doc.id;
|
||||||
|
collections.push(collection);
|
||||||
|
});
|
||||||
|
fb.currentCollections = collections[0].collection;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fb.currentUser = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
24
functions/network.js
Normal file
24
functions/network.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import AxiosStrategy from "./strategies/AxiosStrategy";
|
||||||
|
import FirefoxStrategy from "./strategies/FirefoxStrategy";
|
||||||
|
import ChromeStrategy, { hasChromeExtensionInstalled } from "./strategies/ChromeStrategy";
|
||||||
|
|
||||||
|
const runAppropriateStrategy = (req, store) => {
|
||||||
|
// Chrome Provides a chrome object for scripts to access
|
||||||
|
// Check its availability to say whether you are in Google Chrome
|
||||||
|
if (window.chrome && hasChromeExtensionInstalled()) {
|
||||||
|
return ChromeStrategy(req, store);
|
||||||
|
}
|
||||||
|
// The firefox plugin injects a function to send requests through it
|
||||||
|
// If that is available, then we can use the FirefoxStrategy
|
||||||
|
if (window.firefoxExtSendRequest) {
|
||||||
|
return FirefoxStrategy(req, store);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AxiosStrategy(req, store);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendNetworkRequest = (req, store) =>
|
||||||
|
runAppropriateStrategy(req, store)
|
||||||
|
.finally(() => window.$nuxt.$loading.finish());
|
||||||
|
|
||||||
|
export { sendNetworkRequest };
|
||||||
23
functions/strategies/AxiosStrategy.js
Normal file
23
functions/strategies/AxiosStrategy.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const axiosWithProxy = async (req, { state }) => {
|
||||||
|
const { data } = await axios.post(
|
||||||
|
state.postwoman.settings.PROXY_URL || "https://postwoman.apollotv.xyz/",
|
||||||
|
req
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const axiosWithoutProxy = async (req, _store) => {
|
||||||
|
const res = await axios(req);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const axiosStrategy = (req, store) => {
|
||||||
|
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||||
|
return axiosWithProxy(req, store);
|
||||||
|
}
|
||||||
|
return axiosWithoutProxy(req, store);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default axiosStrategy;
|
||||||
56
functions/strategies/ChromeStrategy.js
Normal file
56
functions/strategies/ChromeStrategy.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const EXTENSION_ID = "amknoiejhlmhancpahfcfcfhllgkpbld";
|
||||||
|
|
||||||
|
// Check if the Chrome Extension is present
|
||||||
|
// The Chrome extension injects an empty span to help detection.
|
||||||
|
// Also check for the presence of window.chrome object to confirm smooth operations
|
||||||
|
export const hasChromeExtensionInstalled = () => {
|
||||||
|
return document.getElementById("chromePWExtensionDetect") !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chromeWithoutProxy = (req, _store) => new Promise((resolve, reject) => {
|
||||||
|
chrome.runtime.sendMessage(
|
||||||
|
EXTENSION_ID, {
|
||||||
|
messageType: "send-req",
|
||||||
|
data: {
|
||||||
|
config: req
|
||||||
|
}
|
||||||
|
}, (message) => {
|
||||||
|
if (message.data.error) {
|
||||||
|
reject(message.data.error);
|
||||||
|
} else {
|
||||||
|
resolve(message.data.response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const chromeWithProxy = (req, { state }) => new Promise((resolve, reject) => {
|
||||||
|
chrome.runtime.sendMessage(
|
||||||
|
EXTENSION_ID, {
|
||||||
|
messageType: "send-req",
|
||||||
|
data: {
|
||||||
|
config: {
|
||||||
|
method: "post",
|
||||||
|
url: state.postwoman.settings.PROXY_URL || "https://postwoman.apollotv.xyz/",
|
||||||
|
data: req
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (message) => {
|
||||||
|
if (message.data.error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(message.data.response.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
const chromeStrategy = (req, store) => {
|
||||||
|
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||||
|
return chromeWithProxy(req, store);
|
||||||
|
} else {
|
||||||
|
return chromeWithoutProxy(req, store);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default chromeStrategy;
|
||||||
50
functions/strategies/FirefoxStrategy.js
Normal file
50
functions/strategies/FirefoxStrategy.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
const firefoxWithProxy = (req, { state }) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const eventListener = event => {
|
||||||
|
window.removeEventListener("firefoxExtSendRequestComplete", event);
|
||||||
|
|
||||||
|
if (event.detail.error) {
|
||||||
|
reject(JSON.parse(event.detail.error));
|
||||||
|
} else {
|
||||||
|
resolve(JSON.parse(event.detail.response).data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("firefoxExtSendRequestComplete", eventListener);
|
||||||
|
|
||||||
|
window.firefoxExtSendRequest({
|
||||||
|
method: "post",
|
||||||
|
url:
|
||||||
|
state.postwoman.settings.PROXY_URL || "https://postwoman.apollotv.xyz/",
|
||||||
|
data: req
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const firefoxWithoutProxy = (req, _store) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const eventListener = ({ detail }) => {
|
||||||
|
window.removeEventListener(
|
||||||
|
"firefoxExtSendRequestComplete",
|
||||||
|
eventListener
|
||||||
|
);
|
||||||
|
|
||||||
|
if (detail.error) {
|
||||||
|
reject(JSON.parse(detail.error));
|
||||||
|
} else {
|
||||||
|
resolve(JSON.parse(detail.response));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("firefoxExtSendRequestComplete", eventListener);
|
||||||
|
|
||||||
|
window.firefoxExtSendRequest(req);
|
||||||
|
});
|
||||||
|
|
||||||
|
const firefoxStrategy = (req, store) => {
|
||||||
|
if (store.state.postwoman.settings.PROXY_ENABLED) {
|
||||||
|
return firefoxWithProxy(req, store);
|
||||||
|
}
|
||||||
|
return firefoxWithoutProxy(req, store);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default firefoxStrategy;
|
||||||
15
functions/utils/debounce.js
Normal file
15
functions/utils/debounce.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Debounce is a higher order function which makes its enclosed function be executed
|
||||||
|
// only if the function wasn't called again till 'delay' time has passed, this helps reduce impact of heavy working
|
||||||
|
// functions which might be called frequently
|
||||||
|
// NOTE : Don't use lambda functions as this doesn't get bound properly in them, use the 'function (args) {}' format
|
||||||
|
const debounce = (func, delay) => {
|
||||||
|
let inDebounce;
|
||||||
|
return function() {
|
||||||
|
const context = this;
|
||||||
|
const args = arguments;
|
||||||
|
clearTimeout(inDebounce);
|
||||||
|
inDebounce = setTimeout(() => func.apply(context, args), delay);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default debounce;
|
||||||
@@ -154,7 +154,8 @@ export default {
|
|||||||
disconnected_from: "Disconnected from {name}",
|
disconnected_from: "Disconnected from {name}",
|
||||||
something_went_wrong: "Something went wrong!",
|
something_went_wrong: "Something went wrong!",
|
||||||
error_occurred: "An error has occurred.",
|
error_occurred: "An error has occurred.",
|
||||||
browser_support_sse: "This browser doesn't seems to have Server Sent Events support.",
|
browser_support_sse:
|
||||||
|
"This browser doesn't seems to have Server Sent Events support.",
|
||||||
log: "Log",
|
log: "Log",
|
||||||
no_url: "No URL",
|
no_url: "No URL",
|
||||||
run_query: "Run Query",
|
run_query: "Run Query",
|
||||||
@@ -207,7 +208,8 @@ export default {
|
|||||||
value_count: "value {count}",
|
value_count: "value {count}",
|
||||||
send_request_first: "Send a request first",
|
send_request_first: "Send a request first",
|
||||||
generate_docs: "Generate Documentation",
|
generate_docs: "Generate Documentation",
|
||||||
generate_docs_message: "Import any Postwoman Collection to Generate Documentation on-the-go.",
|
generate_docs_message:
|
||||||
|
"Import any Postwoman Collection to Generate Documentation on-the-go.",
|
||||||
generate_docs_first: "Generate documentation first",
|
generate_docs_first: "Generate documentation first",
|
||||||
docs_generated: "Documentation generated",
|
docs_generated: "Documentation generated",
|
||||||
import_collections: "Import collections",
|
import_collections: "Import collections",
|
||||||
@@ -231,8 +233,10 @@ export default {
|
|||||||
enable_proxy: "Try enabling Proxy",
|
enable_proxy: "Try enabling Proxy",
|
||||||
complete_config_urls: "Please complete configuration urls.",
|
complete_config_urls: "Please complete configuration urls.",
|
||||||
token_request_saved: "Token request saved",
|
token_request_saved: "Token request saved",
|
||||||
donate_info1: "If you have enjoyed the productivity of using Postwoman, consider donating as a sign of appreciation.",
|
donate_info1:
|
||||||
donate_info2: "You can support Postwoman development via the following methods:",
|
"If you have enjoyed the productivity of using Postwoman, consider donating as a sign of appreciation.",
|
||||||
|
donate_info2:
|
||||||
|
"You can support Postwoman development via the following methods:",
|
||||||
one_time_recurring: "One-time or recurring",
|
one_time_recurring: "One-time or recurring",
|
||||||
one_time: "One-time",
|
one_time: "One-time",
|
||||||
recurring: "Recurring",
|
recurring: "Recurring",
|
||||||
@@ -241,5 +245,20 @@ export default {
|
|||||||
go_home: "Go Home",
|
go_home: "Go Home",
|
||||||
reload: "Reload",
|
reload: "Reload",
|
||||||
enter_curl: "Enter cURL",
|
enter_curl: "Enter cURL",
|
||||||
empty: "Empty"
|
empty: "Empty",
|
||||||
|
extensions: "Extensions",
|
||||||
|
extensions_info1: "Browser extension that simplifies access to Postwoman",
|
||||||
|
extensions_info2: "Get Postwoman browser extension!",
|
||||||
|
installed: "Installed",
|
||||||
|
login_with: "Login with",
|
||||||
|
logged_out: "Logged out",
|
||||||
|
logout: "Logout",
|
||||||
|
account: "Account",
|
||||||
|
sync: "Sync",
|
||||||
|
syncHistory: "History",
|
||||||
|
syncCollections: "Collections",
|
||||||
|
turn_on: "Turn on",
|
||||||
|
login_first: "Login first",
|
||||||
|
paste_a_collection: "Paste a Collection",
|
||||||
|
import_from_sync: "Import from Sync"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -237,6 +237,11 @@
|
|||||||
<div v-else-if="$route.path === '/settings'">
|
<div v-else-if="$route.path === '/settings'">
|
||||||
<nav class="secondary-nav">
|
<nav class="secondary-nav">
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="#account" v-tooltip.right="$t('account')">
|
||||||
|
<i class="material-icons">person</i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#theme" v-tooltip.right="$t('theme')">
|
<a href="#theme" v-tooltip.right="$t('theme')">
|
||||||
<i class="material-icons">brush</i>
|
<i class="material-icons">brush</i>
|
||||||
@@ -288,11 +293,61 @@
|
|||||||
>
|
>
|
||||||
<i class="material-icons">offline_bolt</i>
|
<i class="material-icons">offline_bolt</i>
|
||||||
</button>
|
</button>
|
||||||
|
<login v-if="!fb.currentUser" />
|
||||||
|
<span v-if="fb.currentUser">
|
||||||
|
<v-popover>
|
||||||
|
<button
|
||||||
|
class="icon"
|
||||||
|
v-tooltip="
|
||||||
|
(fb.currentUser.displayName ||
|
||||||
|
'<label><i>Name not found</i></label>') +
|
||||||
|
'<br>' +
|
||||||
|
(fb.currentUser.email ||
|
||||||
|
'<label><i>Email not found</i></label>')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="fb.currentUser.photoURL"
|
||||||
|
:src="fb.currentUser.photoURL"
|
||||||
|
class="material-icons"
|
||||||
|
/>
|
||||||
|
<i v-else class="material-icons">account_circle</i>
|
||||||
|
</button>
|
||||||
|
<template slot="popover">
|
||||||
|
<div>
|
||||||
|
<nuxt-link :to="localePath('settings')" v-close-popover>
|
||||||
|
<button class="icon">
|
||||||
|
<i class="material-icons">settings</i>
|
||||||
|
<span>
|
||||||
|
{{ $t("settings") }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</nuxt-link>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="icon" @click="logout" v-close-popover>
|
||||||
|
<i class="material-icons">exit_to_app</i>
|
||||||
|
<span>{{ $t("logout") }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-popover>
|
||||||
|
</span>
|
||||||
<v-popover>
|
<v-popover>
|
||||||
<button class="icon" v-tooltip="$t('more')">
|
<button class="icon" v-tooltip="$t('more')">
|
||||||
<i class="material-icons">more_vert</i>
|
<i class="material-icons">more_vert</i>
|
||||||
</button>
|
</button>
|
||||||
<template slot="popover">
|
<template slot="popover">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="icon"
|
||||||
|
@click="showExtensions = true"
|
||||||
|
v-close-popover
|
||||||
|
>
|
||||||
|
<i class="material-icons">extension</i>
|
||||||
|
<span>{{ $t("extensions") }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="icon"
|
class="icon"
|
||||||
@@ -342,15 +397,7 @@
|
|||||||
<div class="flex-wrap">
|
<div class="flex-wrap">
|
||||||
<span v-if="version.name" class="mono">
|
<span v-if="version.name" class="mono">
|
||||||
<a
|
<a
|
||||||
href="https://liyasthomas.web.app"
|
class="link"
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<button class="icon" v-tooltip="'Liyas Thomas'">
|
|
||||||
🦄
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
:href="
|
:href="
|
||||||
'https://github.com/liyasthomas/postwoman/releases/tag/' +
|
'https://github.com/liyasthomas/postwoman/releases/tag/' +
|
||||||
version.name
|
version.name
|
||||||
@@ -361,6 +408,14 @@
|
|||||||
>
|
>
|
||||||
{{ version.name }}
|
{{ version.name }}
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
class="link"
|
||||||
|
href="https://www.netlify.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
Powered by Netlify
|
||||||
|
</a>
|
||||||
<!-- <span v-if="version.hash">
|
<!-- <span v-if="version.hash">
|
||||||
-
|
-
|
||||||
<a
|
<a
|
||||||
@@ -372,6 +427,15 @@
|
|||||||
<!-- <span v-if="version.variant">({{version.variant}})</span> -->
|
<!-- <span v-if="version.variant">({{version.variant}})</span> -->
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
|
<a
|
||||||
|
href="https://liyasthomas.web.app"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
<button class="icon" v-tooltip="'Liyas Thomas'">
|
||||||
|
🦄
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
<a
|
<a
|
||||||
href="mailto:liyascthomas@gmail.com"
|
href="mailto:liyascthomas@gmail.com"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -402,6 +466,86 @@
|
|||||||
<aside class="nav-second"></aside>
|
<aside class="nav-second"></aside>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<modal v-if="showExtensions" @close="showExtensions = false">
|
||||||
|
<div slot="header">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<div class="flex-wrap">
|
||||||
|
<h3 class="title">{{ $t("extensions") }}</h3>
|
||||||
|
<div>
|
||||||
|
<button class="icon" @click="showExtensions = false">
|
||||||
|
<i class="material-icons">close</i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div slot="body">
|
||||||
|
<p class="info">
|
||||||
|
{{ $t("extensions_info1") }}
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="https://addons.mozilla.org/en-US/firefox/addon/postwoman"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
<button class="icon">
|
||||||
|
<svg
|
||||||
|
class="material-icons"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm8.003 8.657c-1.276-3.321-4.46-4.605-5.534-4.537 3.529 1.376 4.373 6.059 4.06 7.441-.307-1.621-1.286-3.017-1.872-3.385 3.417 8.005-4.835 10.465-7.353 7.687.649.168 1.931.085 2.891-.557.898-.602.983-.638 1.56-.683.686-.053-.041-1.406-1.539-1.177-.616.094-1.632.819-2.88.341-1.508-.576-1.46-2.634.096-2.015.337-.437.088-1.263.088-1.263.452-.414 1.022-.706 1.37-.911.228-.135.829-.507.795-1.23-.123-.096-.32-.219-.766-.193-1.736.11-1.852-.518-1.967-.808.078-.668.524-1.534 1.361-1.931-1.257-.193-2.28.397-2.789 1.154-.809-.174-1.305-.183-2.118-.031-.316-.24-.666-.67-.878-1.181 1.832-2.066 4.499-3.378 7.472-3.378 5.912 0 8.263 4.283 8.003 6.657z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>Firefox</span>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
v-if="firefoxExtInstalled"
|
||||||
|
v-tooltip="$t('installed')"
|
||||||
|
>
|
||||||
|
<i class="material-icons">done</i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="https://chrome.google.com/webstore/detail/postwoman-extension-for-c/amknoiejhlmhancpahfcfcfhllgkpbld"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
<button class="icon">
|
||||||
|
<svg
|
||||||
|
class="material-icons"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M2.897 4.181c2.43-2.828 5.763-4.181 9.072-4.181 4.288 0 8.535 2.273 10.717 6.554-2.722.001-6.984 0-9.293 0-1.674.001-2.755-.037-3.926.579-1.376.724-2.415 2.067-2.777 3.644l-3.793-6.596zm5.11 7.819c0 2.2 1.789 3.99 3.988 3.99s3.988-1.79 3.988-3.99-1.789-3.991-3.988-3.991-3.988 1.791-3.988 3.991zm5.536 5.223c-2.238.666-4.858-.073-6.293-2.549-1.095-1.891-3.989-6.933-5.305-9.225-1.33 2.04-1.945 4.294-1.945 6.507 0 5.448 3.726 10.65 9.673 11.818l3.87-6.551zm2.158-9.214c1.864 1.734 2.271 4.542 1.007 6.719-.951 1.641-3.988 6.766-5.46 9.248 7.189.443 12.752-5.36 12.752-11.972 0-1.313-.22-2.66-.69-3.995h-7.609z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>Chrome</span>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
v-if="chromeExtInstalled"
|
||||||
|
v-tooltip="$t('installed')"
|
||||||
|
>
|
||||||
|
<i class="material-icons">done</i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div slot="footer"></div>
|
||||||
|
</modal>
|
||||||
<modal v-if="showShortcuts" @close="showShortcuts = false">
|
<modal v-if="showShortcuts" @close="showShortcuts = false">
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
<ul>
|
<ul>
|
||||||
@@ -504,16 +648,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss">
|
||||||
|
.link {
|
||||||
|
margin: 8px 16px;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.link {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import intializePwa from "../assets/js/pwa";
|
import intializePwa from "../assets/js/pwa";
|
||||||
import * as version from "../.postwoman/version.json";
|
import * as version from "../.postwoman/version.json";
|
||||||
|
import { hasChromeExtensionInstalled } from "../functions/strategies/ChromeStrategy";
|
||||||
|
import firebase from "firebase/app";
|
||||||
|
import { fb } from "../functions/fb";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
logo: () => import("../components/logo"),
|
logo: () => import("../components/logo"),
|
||||||
modal: () => import("../components/modal")
|
modal: () => import("../components/modal"),
|
||||||
|
login: () => import("../components/firebase/login")
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -522,6 +680,21 @@ export default {
|
|||||||
"nuxt-link-exact-active": this.$route.path === path,
|
"nuxt-link-exact-active": this.$route.path === path,
|
||||||
"nuxt-link-active": this.$route.path === path
|
"nuxt-link-active": this.$route.path === path
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
fb.currentUser = null;
|
||||||
|
firebase
|
||||||
|
.auth()
|
||||||
|
.signOut()
|
||||||
|
.catch(err => {
|
||||||
|
this.$toast.show(err.message || err, {
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.$toast.info(this.$t("logged_out"), {
|
||||||
|
icon: "vpn_key"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -532,8 +705,12 @@ export default {
|
|||||||
// prompt.
|
// prompt.
|
||||||
showInstallPrompt: null,
|
showInstallPrompt: null,
|
||||||
version: {},
|
version: {},
|
||||||
|
showExtensions: false,
|
||||||
showShortcuts: false,
|
showShortcuts: false,
|
||||||
showSupport: false
|
showSupport: false,
|
||||||
|
firefoxExtInstalled: window.firefoxExtSendRequest,
|
||||||
|
chromeExtInstalled: window.chrome && hasChromeExtensionInstalled(),
|
||||||
|
fb
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -591,6 +768,37 @@ export default {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let showExtensionsToast =
|
||||||
|
localStorage.getItem("showExtensionsToast") === "yes";
|
||||||
|
if (
|
||||||
|
!this.firefoxExtInstalled &&
|
||||||
|
!this.chromeExtInstalled &&
|
||||||
|
!showExtensionsToast
|
||||||
|
) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$toast.show(this.$t("extensions_info2"), {
|
||||||
|
icon: "extension",
|
||||||
|
duration: 5000,
|
||||||
|
theme: "toasted-primary",
|
||||||
|
action: [
|
||||||
|
{
|
||||||
|
text: this.$t("yes"),
|
||||||
|
onClick: (e, toastObject) => {
|
||||||
|
this.showExtensions = true;
|
||||||
|
localStorage.setItem("showExtensionsToast", "yes");
|
||||||
|
toastObject.goAway(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: this.$t("no"),
|
||||||
|
onClick: (e, toastObject) => {
|
||||||
|
toastObject.goAway(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}, 15000);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
window.addEventListener("scroll", event => {
|
window.addEventListener("scroll", event => {
|
||||||
@@ -623,7 +831,7 @@ export default {
|
|||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
$route() {
|
$route() {
|
||||||
this.$toast.clear();
|
// this.$toast.clear();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page page-error">
|
<div class="page page-error">
|
||||||
<img src="~static/icons/error.svg" :alt="$t('error')" class="error_banner" />
|
<img
|
||||||
|
src="~static/icons/error.svg"
|
||||||
|
:alt="$t('error')"
|
||||||
|
class="error_banner"
|
||||||
|
/>
|
||||||
<h2>{{ error.statusCode }}</h2>
|
<h2>{{ error.statusCode }}</h2>
|
||||||
<h3>{{ error.message }}</h3>
|
<h3>{{ error.message }}</h3>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
1104
package-lock.json
generated
1104
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -18,27 +18,29 @@
|
|||||||
"test": "start-server-and-test dev http://localhost:3000 e2e"
|
"test": "start-server-and-test dev http://localhost:3000 e2e"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxtjs/axios": "^5.9.2",
|
"@nuxtjs/axios": "^5.9.3",
|
||||||
"@nuxtjs/google-analytics": "^2.2.3",
|
"@nuxtjs/google-analytics": "^2.2.3",
|
||||||
"@nuxtjs/google-tag-manager": "^2.3.1",
|
"@nuxtjs/google-tag-manager": "^2.3.1",
|
||||||
"@nuxtjs/pwa": "^3.0.0-beta.19",
|
"@nuxtjs/pwa": "^3.0.0-beta.19",
|
||||||
"@nuxtjs/robots": "^2.4.2",
|
"@nuxtjs/robots": "^2.4.2",
|
||||||
"@nuxtjs/sitemap": "^2.0.1",
|
"@nuxtjs/sitemap": "^2.0.1",
|
||||||
"@nuxtjs/toast": "^3.3.0",
|
"@nuxtjs/toast": "^3.3.0",
|
||||||
"ace-builds": "^1.4.7",
|
"ace-builds": "^1.4.8",
|
||||||
|
"firebase": "^7.7.0",
|
||||||
"graphql": "^14.5.8",
|
"graphql": "^14.5.8",
|
||||||
"nuxt": "^2.11.0",
|
"nuxt": "^2.11.0",
|
||||||
"nuxt-i18n": "^6.4.1",
|
"nuxt-i18n": "^6.5.0",
|
||||||
"v-tooltip": "^2.0.2",
|
"v-tooltip": "^2.0.3",
|
||||||
"vue-virtual-scroll-list": "^1.4.4",
|
"vue-virtual-scroll-list": "^1.4.4",
|
||||||
|
"vuefire": "^2.2.1",
|
||||||
"vuejs-auto-complete": "^0.9.0",
|
"vuejs-auto-complete": "^0.9.0",
|
||||||
"vuex-persist": "^2.2.0",
|
"vuex-persist": "^2.2.0",
|
||||||
"yargs-parser": "^16.1.0"
|
"yargs-parser": "^16.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cypress": "^3.8.2",
|
"cypress": "^3.8.3",
|
||||||
"node-sass": "^4.13.0",
|
"node-sass": "^4.13.1",
|
||||||
"sass-loader": "^8.0.1",
|
"sass-loader": "^8.0.2",
|
||||||
"start-server-and-test": "^1.10.6"
|
"start-server-and-test": "^1.10.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,12 +93,14 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.path">
|
<p class="doc-desc" v-if="request.path">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("path") }}: <code>{{ request.path || $t("none") }}</code>
|
{{ $t("path") }}:
|
||||||
|
<code>{{ request.path || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.method">
|
<p class="doc-desc" v-if="request.method">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("method") }}: <code>{{ request.method || $t("none") }}</code>
|
{{ $t("method") }}:
|
||||||
|
<code>{{ request.method || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.auth">
|
<p class="doc-desc" v-if="request.auth">
|
||||||
@@ -109,7 +111,8 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.httpUser">
|
<p class="doc-desc" v-if="request.httpUser">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("username") }}: <code>{{ request.httpUser || $t("none") }}</code>
|
{{ $t("username") }}:
|
||||||
|
<code>{{ request.httpUser || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.httpPassword">
|
<p class="doc-desc" v-if="request.httpPassword">
|
||||||
@@ -120,16 +123,17 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.bearerToken">
|
<p class="doc-desc" v-if="request.bearerToken">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("token") }}: <code>{{ request.bearerToken || $t("none") }}</code>
|
{{ $t("token") }}:
|
||||||
|
<code>{{ request.bearerToken || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<h4 v-if="request.headers.length > 0">{{ $t("headers") }}</h4>
|
<h4 v-if="request.headers.length > 0">{{ $t("headers") }}</h4>
|
||||||
<span
|
<span v-if="request.headers">
|
||||||
v-if="request.headers"
|
<p
|
||||||
v-for="header in request.headers"
|
v-for="header in request.headers"
|
||||||
:key="header.key"
|
:key="header.key"
|
||||||
|
class="doc-desc"
|
||||||
>
|
>
|
||||||
<p class="doc-desc">
|
|
||||||
<span>
|
<span>
|
||||||
{{ header.key || $t("none") }}:
|
{{ header.key || $t("none") }}:
|
||||||
<code>{{ header.value || $t("none") }}</code>
|
<code>{{ header.value || $t("none") }}</code>
|
||||||
@@ -137,12 +141,12 @@
|
|||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
<h4 v-if="request.params.length > 0">{{ $t("parameters") }}</h4>
|
<h4 v-if="request.params.length > 0">{{ $t("parameters") }}</h4>
|
||||||
<span
|
<span v-if="request.params">
|
||||||
v-if="request.params"
|
<p
|
||||||
v-for="parameter in request.params"
|
v-for="parameter in request.params"
|
||||||
:key="parameter.key"
|
:key="parameter.key"
|
||||||
|
class="doc-desc"
|
||||||
>
|
>
|
||||||
<p class="doc-desc">
|
|
||||||
<span>
|
<span>
|
||||||
{{ parameter.key || $t("none") }}:
|
{{ parameter.key || $t("none") }}:
|
||||||
<code>{{ parameter.value || $t("none") }}</code>
|
<code>{{ parameter.value || $t("none") }}</code>
|
||||||
@@ -150,12 +154,12 @@
|
|||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
<h4 v-if="request.bodyParam">{{ $t("payload") }}</h4>
|
<h4 v-if="request.bodyParam">{{ $t("payload") }}</h4>
|
||||||
<span
|
<span v-if="request.bodyParam">
|
||||||
v-if="request.bodyParam"
|
<p
|
||||||
v-for="payload in request.bodyParam"
|
v-for="payload in request.bodyParam"
|
||||||
:key="payload.key"
|
:key="payload.key"
|
||||||
|
class="doc-desc"
|
||||||
>
|
>
|
||||||
<p class="doc-desc">
|
|
||||||
<span>
|
<span>
|
||||||
{{ payload.key || $t("none") }}:
|
{{ payload.key || $t("none") }}:
|
||||||
<code>{{ payload.value || $t("none") }}</code>
|
<code>{{ payload.value || $t("none") }}</code>
|
||||||
@@ -164,7 +168,8 @@
|
|||||||
</span>
|
</span>
|
||||||
<p class="doc-desc" v-if="request.rawParams">
|
<p class="doc-desc" v-if="request.rawParams">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("parameters") }}: <code>{{ request.rawParams || $t("none") }}</code>
|
{{ $t("parameters") }}:
|
||||||
|
<code>{{ request.rawParams || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.contentType">
|
<p class="doc-desc" v-if="request.contentType">
|
||||||
@@ -202,7 +207,8 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.method">
|
<p class="doc-desc" v-if="request.method">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("method") }}: <code>{{ request.method || $t("none") }}</code>
|
{{ $t("method") }}:
|
||||||
|
<code>{{ request.method || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.auth">
|
<p class="doc-desc" v-if="request.auth">
|
||||||
@@ -213,26 +219,29 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.httpUser">
|
<p class="doc-desc" v-if="request.httpUser">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("username") }}: <code>{{ request.httpUser || $t("none") }}</code>
|
{{ $t("username") }}:
|
||||||
|
<code>{{ request.httpUser || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.httpPassword">
|
<p class="doc-desc" v-if="request.httpPassword">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("password") }}: <code>{{ request.httpPassword || $t("none") }}</code>
|
{{ $t("password") }}:
|
||||||
|
<code>{{ request.httpPassword || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.bearerToken">
|
<p class="doc-desc" v-if="request.bearerToken">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("token") }}: <code>{{ request.bearerToken || $t("none") }}</code>
|
{{ $t("token") }}:
|
||||||
|
<code>{{ request.bearerToken || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<h4 v-if="request.headers.length > 0">{{ $t("headers") }}</h4>
|
<h4 v-if="request.headers.length > 0">{{ $t("headers") }}</h4>
|
||||||
<span
|
<span v-if="request.headers">
|
||||||
v-if="request.headers"
|
<p
|
||||||
v-for="header in request.headers"
|
v-for="header in request.headers"
|
||||||
:key="header.key"
|
:key="header.key"
|
||||||
|
class="doc-desc"
|
||||||
>
|
>
|
||||||
<p class="doc-desc">
|
|
||||||
<span>
|
<span>
|
||||||
{{ header.key || $t("none") }}:
|
{{ header.key || $t("none") }}:
|
||||||
<code>{{ header.value || $t("none") }}</code>
|
<code>{{ header.value || $t("none") }}</code>
|
||||||
@@ -240,12 +249,12 @@
|
|||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
<h4 v-if="request.params.length > 0">{{ $t("parameters") }}</h4>
|
<h4 v-if="request.params.length > 0">{{ $t("parameters") }}</h4>
|
||||||
<span
|
<span v-if="request.params">
|
||||||
v-if="request.params"
|
<p
|
||||||
v-for="parameter in request.params"
|
v-for="parameter in request.params"
|
||||||
:key="parameter.key"
|
:key="parameter.key"
|
||||||
|
class="doc-desc"
|
||||||
>
|
>
|
||||||
<p class="doc-desc">
|
|
||||||
<span>
|
<span>
|
||||||
{{ parameter.key || $t("none") }}:
|
{{ parameter.key || $t("none") }}:
|
||||||
<code>{{ parameter.value || $t("none") }}</code>
|
<code>{{ parameter.value || $t("none") }}</code>
|
||||||
@@ -253,12 +262,12 @@
|
|||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
<h4 v-if="request.bodyParam">{{ $t("payload") }}</h4>
|
<h4 v-if="request.bodyParam">{{ $t("payload") }}</h4>
|
||||||
<span
|
<span v-if="request.bodyParam">
|
||||||
v-if="request.bodyParam"
|
<p
|
||||||
v-for="payload in request.bodyParam"
|
v-for="payload in request.bodyParam"
|
||||||
:key="payload.key"
|
:key="payload.key"
|
||||||
|
class="doc-desc"
|
||||||
>
|
>
|
||||||
<p class="doc-desc">
|
|
||||||
<span>
|
<span>
|
||||||
{{ payload.key || $t("none") }}:
|
{{ payload.key || $t("none") }}:
|
||||||
<code>{{ payload.value || $t("none") }}</code>
|
<code>{{ payload.value || $t("none") }}</code>
|
||||||
@@ -267,7 +276,8 @@
|
|||||||
</span>
|
</span>
|
||||||
<p class="doc-desc" v-if="request.rawParams">
|
<p class="doc-desc" v-if="request.rawParams">
|
||||||
<span>
|
<span>
|
||||||
{{ $t("parameters") }}: <code>{{ request.rawParams || $t("none") }}</code>
|
{{ $t("parameters") }}:
|
||||||
|
<code>{{ request.rawParams || $t("none") }}</code>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="doc-desc" v-if="request.contentType">
|
<p class="doc-desc" v-if="request.contentType">
|
||||||
@@ -305,9 +315,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.collection {
|
|
||||||
}
|
|
||||||
|
|
||||||
.folder {
|
.folder {
|
||||||
border-left: 1px solid var(--brd-color);
|
border-left: 1px solid var(--brd-color);
|
||||||
margin: 16px 0 0;
|
margin: 16px 0 0;
|
||||||
|
|||||||
@@ -175,7 +175,8 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Editor
|
<QueryEditor
|
||||||
|
ref="queryEditor"
|
||||||
v-model="gqlQueryString"
|
v-model="gqlQueryString"
|
||||||
:options="{
|
:options="{
|
||||||
maxLines: responseBodyMaxLines,
|
maxLines: responseBodyMaxLines,
|
||||||
@@ -391,6 +392,8 @@ import axios from "axios";
|
|||||||
import * as gql from "graphql";
|
import * as gql from "graphql";
|
||||||
import textareaAutoHeight from "../directives/textareaAutoHeight";
|
import textareaAutoHeight from "../directives/textareaAutoHeight";
|
||||||
import AceEditor from "../components/ace-editor";
|
import AceEditor from "../components/ace-editor";
|
||||||
|
import QueryEditor from "../components/graphql/queryeditor";
|
||||||
|
import { sendNetworkRequest } from "../functions/network";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
directives: {
|
directives: {
|
||||||
@@ -401,7 +404,8 @@ export default {
|
|||||||
"gql-field": () => import("../components/graphql/field"),
|
"gql-field": () => import("../components/graphql/field"),
|
||||||
"gql-type": () => import("../components/graphql/type"),
|
"gql-type": () => import("../components/graphql/type"),
|
||||||
autocomplete: () => import("../components/autocomplete"),
|
autocomplete: () => import("../components/autocomplete"),
|
||||||
Editor: AceEditor
|
Editor: AceEditor,
|
||||||
|
QueryEditor: QueryEditor
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -542,6 +546,7 @@ export default {
|
|||||||
responseBodyMaxLines: 16
|
responseBodyMaxLines: 16
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
url: {
|
url: {
|
||||||
get() {
|
get() {
|
||||||
@@ -666,14 +671,9 @@ export default {
|
|||||||
const gqlQueryString = this.gqlQueryString;
|
const gqlQueryString = this.gqlQueryString;
|
||||||
this.variables.forEach(variable => {
|
this.variables.forEach(variable => {
|
||||||
// todo: better variable type validation
|
// todo: better variable type validation
|
||||||
const intRex = new RegExp(`\$${variable.key}\: Int`);
|
if (gqlQueryString.indexOf(`\$${variable.key}: Int`) > -1) {
|
||||||
intRex.compile();
|
|
||||||
const floatRex = new RegExp(`\$${variable.key}\: Float`);
|
|
||||||
floatRex.compile();
|
|
||||||
|
|
||||||
if (intRex.test(gqlQueryString)) {
|
|
||||||
variables[variable.key] = parseInt(variable.value);
|
variables[variable.key] = parseInt(variable.value);
|
||||||
} else if (floatRex.test(gqlQueryString)) {
|
} else if (gqlQueryString.indexOf(`\$${variable.key}: Float`) > -1) {
|
||||||
variables[variable.key] = parseFloat(variable.value);
|
variables[variable.key] = parseFloat(variable.value);
|
||||||
} else {
|
} else {
|
||||||
variables[variable.key] = variable.value;
|
variables[variable.key] = variable.value;
|
||||||
@@ -690,21 +690,7 @@ export default {
|
|||||||
data: JSON.stringify({ query: gqlQueryString, variables })
|
data: JSON.stringify({ query: gqlQueryString, variables })
|
||||||
};
|
};
|
||||||
|
|
||||||
const reqConfig = this.$store.state.postwoman.settings.PROXY_ENABLED
|
const data = await sendNetworkRequest(reqOptions, this.$store);
|
||||||
? {
|
|
||||||
method: "post",
|
|
||||||
url:
|
|
||||||
this.$store.state.postwoman.settings.PROXY_URL ||
|
|
||||||
`https://postwoman.apollotv.xyz/`,
|
|
||||||
data: reqOptions
|
|
||||||
}
|
|
||||||
: reqOptions;
|
|
||||||
|
|
||||||
const res = await axios(reqConfig);
|
|
||||||
|
|
||||||
const data = this.$store.state.postwoman.settings.PROXY_ENABLED
|
|
||||||
? res.data
|
|
||||||
: res;
|
|
||||||
|
|
||||||
this.responseString = JSON.stringify(data.data, null, 2);
|
this.responseString = JSON.stringify(data.data, null, 2);
|
||||||
|
|
||||||
@@ -826,7 +812,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.gqlTypes = types;
|
this.gqlTypes = types;
|
||||||
|
this.$refs.queryEditor.setValidationSchema(schema);
|
||||||
this.$nuxt.$loading.finish();
|
this.$nuxt.$loading.finish();
|
||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
this.$toast.info(this.$t("finished_in", { duration }), {
|
this.$toast.info(this.$t("finished_in", { duration }), {
|
||||||
@@ -834,7 +820,7 @@ export default {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$nuxt.$loading.finish();
|
this.$nuxt.$loading.finish();
|
||||||
this.schemaString = `${error}. ${check_console_details}`;
|
this.schemaString = `${error}. ${this.$t("check_console_details")}`;
|
||||||
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
|
this.$toast.error(`${error} ${this.$t("f12_details")}`, {
|
||||||
icon: "error"
|
icon: "error"
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,11 +40,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</pw-section>
|
</pw-section>
|
||||||
|
|
||||||
<pw-section
|
<pw-section class="blue" :label="$t('request')" ref="request">
|
||||||
class="blue"
|
|
||||||
:label="$t('request')"
|
|
||||||
ref="request"
|
|
||||||
>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<label for="method">{{ $t("method") }}</label>
|
<label for="method">{{ $t("method") }}</label>
|
||||||
@@ -842,10 +838,34 @@
|
|||||||
<input id="collection-tab" type="radio" name="side" />
|
<input id="collection-tab" type="radio" name="side" />
|
||||||
<label for="collection-tab">{{ $t("collections") }}</label>
|
<label for="collection-tab">{{ $t("collections") }}</label>
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<pw-section class="yellow" :label="$t('collections')" ref="collections">
|
<pw-section
|
||||||
|
class="yellow"
|
||||||
|
:label="$t('collections')"
|
||||||
|
ref="collections"
|
||||||
|
>
|
||||||
<collections />
|
<collections />
|
||||||
</pw-section>
|
</pw-section>
|
||||||
</div>
|
</div>
|
||||||
|
<input id="sync-tab" type="radio" name="side" />
|
||||||
|
<label for="sync-tab">{{ $t("sync") }}</label>
|
||||||
|
<div class="tab">
|
||||||
|
<pw-section
|
||||||
|
v-if="fb.currentUser"
|
||||||
|
class="pink"
|
||||||
|
label="Sync"
|
||||||
|
ref="sync"
|
||||||
|
>
|
||||||
|
<inputform />
|
||||||
|
<ballsfeed />
|
||||||
|
</pw-section>
|
||||||
|
<pw-section v-else>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<label>{{ $t("login_first") }}</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</pw-section>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
@@ -1135,6 +1155,8 @@ import getEnvironmentVariablesFromScript from "../functions/preRequest";
|
|||||||
import parseTemplateString from "../functions/templating";
|
import parseTemplateString from "../functions/templating";
|
||||||
import AceEditor from "../components/ace-editor";
|
import AceEditor from "../components/ace-editor";
|
||||||
import { tokenRequest, oauthRedirect } from "../assets/js/oauth";
|
import { tokenRequest, oauthRedirect } from "../assets/js/oauth";
|
||||||
|
import { sendNetworkRequest } from "../functions/network";
|
||||||
|
import { fb } from "../functions/fb";
|
||||||
|
|
||||||
const statusCategories = [
|
const statusCategories = [
|
||||||
{
|
{
|
||||||
@@ -1199,7 +1221,9 @@ export default {
|
|||||||
autocomplete: () => import("../components/autocomplete"),
|
autocomplete: () => import("../components/autocomplete"),
|
||||||
collections: () => import("../components/collections"),
|
collections: () => import("../components/collections"),
|
||||||
saveRequestAs: () => import("../components/collections/saveRequestAs"),
|
saveRequestAs: () => import("../components/collections/saveRequestAs"),
|
||||||
Editor: AceEditor
|
Editor: AceEditor,
|
||||||
|
inputform: () => import("../components/firebase/inputform"),
|
||||||
|
ballsfeed: () => import("../components/firebase/feeds")
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -1369,12 +1393,12 @@ export default {
|
|||||||
],
|
],
|
||||||
showRequestModal: false,
|
showRequestModal: false,
|
||||||
editRequest: {},
|
editRequest: {},
|
||||||
|
|
||||||
urlExcludes: {},
|
urlExcludes: {},
|
||||||
responseBodyText: "",
|
responseBodyText: "",
|
||||||
responseBodyType: "text",
|
responseBodyType: "text",
|
||||||
responseBodyMaxLines: 16,
|
responseBodyMaxLines: 16,
|
||||||
activeSidebar: true
|
activeSidebar: true,
|
||||||
|
fb
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -1992,20 +2016,8 @@ export default {
|
|||||||
if (typeof requestOptions.data === "string") {
|
if (typeof requestOptions.data === "string") {
|
||||||
requestOptions.data = parseTemplateString(requestOptions.data);
|
requestOptions.data = parseTemplateString(requestOptions.data);
|
||||||
}
|
}
|
||||||
const config = this.$store.state.postwoman.settings.PROXY_ENABLED
|
|
||||||
? {
|
|
||||||
method: "POST",
|
|
||||||
url:
|
|
||||||
this.$store.state.postwoman.settings.PROXY_URL ||
|
|
||||||
"https://postwoman.apollotv.xyz/",
|
|
||||||
data: requestOptions
|
|
||||||
}
|
|
||||||
: requestOptions;
|
|
||||||
|
|
||||||
const response = await this.$axios(config);
|
return await sendNetworkRequest(requestOptions, this.$store);
|
||||||
return this.$store.state.postwoman.settings.PROXY_ENABLED
|
|
||||||
? response.data
|
|
||||||
: response;
|
|
||||||
},
|
},
|
||||||
async sendRequest() {
|
async sendRequest() {
|
||||||
this.$toast.clear();
|
this.$toast.clear();
|
||||||
@@ -2117,6 +2129,11 @@ export default {
|
|||||||
star: false
|
star: false
|
||||||
};
|
};
|
||||||
this.$refs.historyComponent.addEntry(entry);
|
this.$refs.historyComponent.addEntry(entry);
|
||||||
|
if (fb.currentUser !== null) {
|
||||||
|
if (fb.currentSettings[1].value) {
|
||||||
|
fb.writeHistory(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -2138,6 +2155,11 @@ export default {
|
|||||||
preRequestScript: this.preRequestScript
|
preRequestScript: this.preRequestScript
|
||||||
};
|
};
|
||||||
this.$refs.historyComponent.addEntry(entry);
|
this.$refs.historyComponent.addEntry(entry);
|
||||||
|
if (fb.currentUser !== null) {
|
||||||
|
if (fb.currentSettings[1].value) {
|
||||||
|
fb.writeHistory(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
this.response.status = error.message;
|
this.response.status = error.message;
|
||||||
@@ -2150,7 +2172,7 @@ export default {
|
|||||||
icon: "help",
|
icon: "help",
|
||||||
duration: 8000,
|
duration: 8000,
|
||||||
action: {
|
action: {
|
||||||
text: "Settings",
|
text: this.$t("yes"),
|
||||||
onClick: (e, toastObject) => {
|
onClick: (e, toastObject) => {
|
||||||
this.$router.push({ path: "/settings" });
|
this.$router.push({ path: "/settings" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,88 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
<pw-section class="green" :label="$t('account')" ref="account">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<div v-if="fb.currentUser">
|
||||||
|
<button class="icon">
|
||||||
|
<img
|
||||||
|
v-if="fb.currentUser.photoURL"
|
||||||
|
:src="fb.currentUser.photoURL"
|
||||||
|
class="material-icons"
|
||||||
|
/>
|
||||||
|
<i v-else class="material-icons">account_circle</i>
|
||||||
|
<span>
|
||||||
|
{{ fb.currentUser.displayName || "Name not found" }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<br />
|
||||||
|
<button class="icon">
|
||||||
|
<i class="material-icons">email</i>
|
||||||
|
<span>
|
||||||
|
{{ fb.currentUser.email || "Email not found" }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<br />
|
||||||
|
<button class="icon" @click="logout">
|
||||||
|
<i class="material-icons">exit_to_app</i>
|
||||||
|
<span>{{ $t("logout") }}</span>
|
||||||
|
</button>
|
||||||
|
<br />
|
||||||
|
<p v-for="setting in fb.currentSettings" :key="setting.id">
|
||||||
|
<pw-toggle
|
||||||
|
:key="setting.name"
|
||||||
|
:on="setting.value"
|
||||||
|
@change="toggleSettings(setting.name, setting.value)"
|
||||||
|
>
|
||||||
|
{{ $t(setting.name) + " " + $t("sync") }}
|
||||||
|
{{ setting.value ? $t("enabled") : $t("disabled") }}
|
||||||
|
</pw-toggle>
|
||||||
|
</p>
|
||||||
|
<p v-if="fb.currentSettings.length == 0">
|
||||||
|
<button class="" @click="initSettings">
|
||||||
|
<i class="material-icons">sync</i>
|
||||||
|
<span>{{ $t("turn_on") + " " + $t("sync") }}</span>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<label>{{ $t("login_with") }}</label>
|
||||||
|
<p>
|
||||||
|
<button class="icon" @click="signInWithGoogle">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="material-icons"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>Google</span>
|
||||||
|
</button>
|
||||||
|
<br />
|
||||||
|
<button class="icon" @click="signInWithGithub">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="material-icons"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>GitHub</span>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</pw-section>
|
||||||
|
|
||||||
<pw-section class="cyan" :label="$t('theme')" ref="theme">
|
<pw-section class="cyan" :label="$t('theme')" ref="theme">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@@ -137,6 +220,9 @@
|
|||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import firebase from "firebase/app";
|
||||||
|
import { fb } from "../functions/fb";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
"pw-section": () => import("../components/section"),
|
"pw-section": () => import("../components/section"),
|
||||||
@@ -238,7 +324,9 @@ export default {
|
|||||||
this.$store.state.postwoman.settings.PROXY_URL ||
|
this.$store.state.postwoman.settings.PROXY_URL ||
|
||||||
"https://postwoman.apollotv.xyz/",
|
"https://postwoman.apollotv.xyz/",
|
||||||
PROXY_KEY: this.$store.state.postwoman.settings.PROXY_KEY || ""
|
PROXY_KEY: this.$store.state.postwoman.settings.PROXY_KEY || ""
|
||||||
}
|
},
|
||||||
|
|
||||||
|
fb
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -289,8 +377,88 @@ export default {
|
|||||||
toggleSetting(key) {
|
toggleSetting(key) {
|
||||||
this.settings[key] = !this.settings[key];
|
this.settings[key] = !this.settings[key];
|
||||||
this.$store.commit("postwoman/applySetting", [key, this.settings[key]]);
|
this.$store.commit("postwoman/applySetting", [key, this.settings[key]]);
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
fb.currentUser = null;
|
||||||
|
firebase
|
||||||
|
.auth()
|
||||||
|
.signOut()
|
||||||
|
.catch(err => {
|
||||||
|
this.$toast.show(err.message || err, {
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.$toast.info(this.$t("logged_out"), {
|
||||||
|
icon: "vpn_key"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
signInWithGoogle() {
|
||||||
|
const provider = new firebase.auth.GoogleAuthProvider();
|
||||||
|
firebase
|
||||||
|
.auth()
|
||||||
|
.signInWithPopup(provider)
|
||||||
|
.then(res => {
|
||||||
|
if (res.additionalUserInfo.isNewUser) {
|
||||||
|
this.$toast.info(this.$t("turn_on") + " " + this.$t("sync"), {
|
||||||
|
icon: "sync",
|
||||||
|
duration: null,
|
||||||
|
closeOnSwipe: false,
|
||||||
|
action: {
|
||||||
|
text: this.$t("yes"),
|
||||||
|
onClick: (e, toastObject) => {
|
||||||
|
fb.writeSettings("syncHistory", true);
|
||||||
|
fb.writeSettings("syncCollections", false);
|
||||||
|
this.$router.push({ path: "/settings" });
|
||||||
|
toastObject.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$toast.show(err.message || err, {
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
signInWithGithub() {
|
||||||
|
const provider = new firebase.auth.GithubAuthProvider();
|
||||||
|
firebase
|
||||||
|
.auth()
|
||||||
|
.signInWithPopup(provider)
|
||||||
|
.then(res => {
|
||||||
|
if (res.additionalUserInfo.isNewUser) {
|
||||||
|
this.$toast.info(this.$t("turn_on") + " " + this.$t("sync"), {
|
||||||
|
icon: "sync",
|
||||||
|
duration: null,
|
||||||
|
closeOnSwipe: false,
|
||||||
|
action: {
|
||||||
|
text: this.$t("yes"),
|
||||||
|
onClick: (e, toastObject) => {
|
||||||
|
fb.writeSettings("syncHistory", true);
|
||||||
|
fb.writeSettings("syncCollections", false);
|
||||||
|
this.$router.push({ path: "/settings" });
|
||||||
|
toastObject.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$toast.show(err.message || err, {
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggleSettings(s, v) {
|
||||||
|
fb.writeSettings(s, !v);
|
||||||
|
},
|
||||||
|
initSettings() {
|
||||||
|
fb.writeSettings("syncHistory", true);
|
||||||
|
fb.writeSettings("syncCollections", false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
this.settings.THEME_COLOR = this.getActiveColor();
|
this.settings.THEME_COLOR = this.getActiveColor();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -111,6 +111,12 @@ export const mutations = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
addNewCollection({ collections }, collection) {
|
addNewCollection({ collections }, collection) {
|
||||||
|
const { name } = collection;
|
||||||
|
const duplicateCollection = collections.some(item => item.name === name);
|
||||||
|
if (duplicateCollection) {
|
||||||
|
this.$toast.info("Duplicate collection");
|
||||||
|
return;
|
||||||
|
}
|
||||||
collections.push({
|
collections.push({
|
||||||
name: "",
|
name: "",
|
||||||
folders: [],
|
folders: [],
|
||||||
@@ -126,6 +132,12 @@ export const mutations = {
|
|||||||
|
|
||||||
editCollection({ collections }, payload) {
|
editCollection({ collections }, payload) {
|
||||||
const { collection, collectionIndex } = payload;
|
const { collection, collectionIndex } = payload;
|
||||||
|
const { name } = collection;
|
||||||
|
const duplicateCollection = collections.some(item => item.name === name);
|
||||||
|
if (duplicateCollection) {
|
||||||
|
this.$toast.info("Duplicate collection");
|
||||||
|
return;
|
||||||
|
}
|
||||||
collections[collectionIndex] = collection;
|
collections[collectionIndex] = collection;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user