feat: initial cookie ui implementation

This commit is contained in:
Andrew Bastin
2023-10-23 19:01:10 +05:30
parent 9ff42dd3a4
commit 3e2d029ee3
5 changed files with 371 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
{
"action": {
"add": "Add",
"autoscroll": "Autoscroll",
"cancel": "Cancel",
"choose_file": "Choose a file",
@@ -54,9 +55,23 @@
"new": "Add new",
"star": "Add star"
},
"cookies": {
"modal": {
"new_domain_name": "New domain name",
"set": "Set a cookie",
"cookie_string": "Cookie string",
"cookie_name": "Name",
"cookie_value": "Value",
"cookie_path": "Path",
"cookie_expires": "Expires",
"managed_tab": "Managed",
"raw_tab": "Raw"
}
},
"app": {
"chat_with_us": "Chat with us",
"contact_us": "Contact us",
"cookies": "Cookies",
"copy": "Copy",
"copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options",
@@ -764,7 +779,7 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect",
"show":"Show",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",

View File

@@ -58,6 +58,9 @@ declare module 'vue' {
CollectionsRequest: typeof import('./components/collections/Request.vue')['default']
CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default']
CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.vue')['default']
CookiesAllModal: typeof import('./components/cookies/AllModal.vue')['default']
CookiesCookieJarModal: typeof import('./components/cookies/CookieJarModal.vue')['default']
CookiesEditCookie: typeof import('./components/cookies/EditCookie.vue')['default']
Environments: typeof import('./components/environments/index.vue')['default']
EnvironmentsAdd: typeof import('./components/environments/Add.vue')['default']
EnvironmentsImportExport: typeof import('./components/environments/ImportExport.vue')['default']

View File

@@ -20,6 +20,11 @@
<AppInterceptor />
</template>
</tippy>
<HoppButtonSecondary
:label="t('app.cookies')"
:icon="IconCookie"
@click="showCookiesModal = true"
/>
</div>
<div class="flex">
<tippy
@@ -195,12 +200,17 @@
:show="showDeveloperOptions"
@hide-modal="showDeveloperOptions = false"
/>
<CookiesAllModal
:show="showCookiesModal"
@hide-modal="showCookiesModal = false"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import { version } from "~/../package.json"
import IconCookie from "~icons/lucide/cookie"
import IconSidebar from "~icons/lucide/sidebar"
import IconZap from "~icons/lucide/zap"
import IconShare2 from "~icons/lucide/share-2"
@@ -223,7 +233,9 @@ import { invokeAction } from "@helpers/actions"
import { HoppSmartItem } from "@hoppscotch/ui"
const t = useI18n()
const showDeveloperOptions = ref(false)
const showCookiesModal = ref(false)
const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
const SIDEBAR = useSetting("SIDEBAR")

View File

@@ -0,0 +1,195 @@
<template>
<HoppSmartModal
v-if="show"
dialog
:title="t('app.cookies')"
aria-modal="true"
@close="hideModal"
>
<template #body>
<div class="flex gap-x-2 border-b border-dividerLight pb-3">
<HoppSmartInput
v-model="newDomainText"
class="flex-grow"
:placeholder="t('cookies.modal.new_domain_name')"
/>
<HoppButtonSecondary
outline
filled
:label="t('action.add')"
@click="addNewDomain"
/>
</div>
<div class="pt-3 flex flex-col gap-y-6">
<div
v-for="[domain, entries] in workingCookieJar.entries()"
:key="domain"
class="flex flex-col gap-y-2"
>
<div class="flex items-center justify-between">
<h3>{{ domain }}</h3>
<div>
<HoppButtonSecondary
:icon="IconTrash2"
@click="deleteDomain(domain)"
/>
<HoppButtonSecondary
:icon="IconPlus"
@click="addCookieToDomain(domain)"
/>
</div>
</div>
<div class="border rounded border-divider">
<div class="divide-y divide-dividerLight">
<div
v-for="(entry, entryIndex) in entries"
:key="`${entry}-${entryIndex}`"
class="flex divide-x divide-dividerLight"
>
<input
class="flex flex-1 px-4 py-2 bg-transparent"
:value="entry"
readonly
/>
<HoppButtonSecondary
:icon="IconEdit"
@click="editCookie(domain, entryIndex, entry)"
/>
<HoppButtonSecondary
color="red"
:icon="IconTrash"
@click="deleteCookie(domain, entryIndex)"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<template #footer>
<span class="flex space-x-2">
<HoppButtonPrimary
v-focus
:label="t('action.save')"
outline
@click="saveCookieChanges"
/>
<HoppButtonSecondary
:label="t('action.cancel')"
outline
filled
@click="cancelCookieChanges"
/>
</span>
<HoppButtonSecondary
:label="t('action.clear_all')"
outline
filled
@click="hideModal"
/>
</template>
</HoppSmartModal>
<CookiesEditCookie
:show="!!showEditModalFor"
:entry="showEditModalFor"
@save-cookie="saveCookieUpdate"
@hide-modal="showEditModalFor = null"
/>
</template>
<script setup lang="ts">
import { useI18n } from "@composables/i18n"
import { useService } from "dioc/vue"
import { CookieJarService } from "~/services/cookie-jar.service"
import IconTrash from "~icons/lucide/trash"
import IconEdit from "~icons/lucide/edit"
import IconTrash2 from "~icons/lucide/trash-2"
import IconPlus from "~icons/lucide/plus"
import { cloneDeep } from "lodash-es"
import { ref, watch } from "vue"
const props = defineProps<{
show: boolean
}>()
const emit = defineEmits<{
(e: "hide-modal"): void
}>()
const t = useI18n()
const newDomainText = ref("")
const cookieJarService = useService(CookieJarService)
const workingCookieJar = ref(cloneDeep(cookieJarService.cookieJar.value))
function addNewDomain() {
workingCookieJar.value.set(newDomainText.value, [])
newDomainText.value = ""
}
function deleteDomain(domain: string) {
workingCookieJar.value.delete(domain)
}
function addCookieToDomain(domain: string) {
const entry = workingCookieJar.value.get(domain)
if (entry) {
entry.push("")
}
}
watch(
() => props.show,
(show) => {
if (show) {
workingCookieJar.value = cloneDeep(cookieJarService.cookieJar.value)
}
}
)
// Tuple of [domain, entryIndex]
const showEditModalFor = ref<[string, number, string] | null>(null)
function saveCookieChanges() {
cookieJarService.cookieJar.value = workingCookieJar.value
hideModal()
}
function cancelCookieChanges() {
hideModal()
}
function editCookie(domain: string, entryIndex: number, cookieEntry: string) {
showEditModalFor.value = [domain, entryIndex, cookieEntry]
}
function deleteCookie(domain: string, entryIndex: number) {
const entry = workingCookieJar.value.get(domain)
if (entry) {
entry.splice(entryIndex, 1)
}
}
function saveCookieUpdate(cookie: string) {
if (!showEditModalFor.value) return
const [domain, entryIndex] = showEditModalFor.value!
const entry = workingCookieJar.value.get(domain)
if (entry) {
entry[entryIndex] = cookie
}
showEditModalFor.value = null
}
const hideModal = () => {
emit("hide-modal")
}
</script>

View File

@@ -0,0 +1,145 @@
<template>
<HoppSmartModal
v-if="show"
dialog
:title="t('cookies.modal.set')"
aria-modal="true"
@close="hideModal"
>
<template #body>
<HoppSmartTabs v-model="currentActiveTab">
<HoppSmartTab :id="'managed'" :label="t('cookies.modal.managed_tab')">
<div class="flex flex-col gap-y-4">
<div class="flex items-center">
<label class="w-20">{{ t("cookies.modal.cookie_name") }}</label>
<HoppSmartInput
v-model="cookieName"
:placeholder="t('cookies.modal.cookie_name')"
class="flex-1"
/>
</div>
<div class="flex items-center">
<label class="w-20">{{ t("cookies.modal.cookie_value") }}</label>
<HoppSmartInput
v-model="cookieValue"
:placeholder="t('cookies.modal.cookie_value')"
class="flex-1"
/>
</div>
<div class="flex items-center">
<label class="w-20">{{ t("cookies.modal.cookie_path") }}</label>
<HoppSmartInput
v-model="cookiePath"
:placeholder="t('cookies.modal.cookie_path')"
class="flex-1"
/>
</div>
<div class="flex items-center">
<label class="w-20">{{
t("cookies.modal.cookie_expires")
}}</label>
<HoppSmartInput
v-model="cookieExpires"
:placeholder="t('cookies.modal.cookie_expires')"
:type="'datetime-local'"
class="flex-1"
/>
</div>
</div>
</HoppSmartTab>
<HoppSmartTab :id="'raw'" :label="t('cookies.modal.raw_tab')">
<HoppSmartInput
v-model="rawCookieString"
:placeholder="t('cookies.modal.cookie_string')"
/>
</HoppSmartTab>
</HoppSmartTabs>
</template>
<template #footer>
<div class="flex space-x-2">
<HoppButtonPrimary
v-focus
:label="t('action.save')"
outline
@click="saveCookieChange"
/>
<HoppButtonSecondary
:label="t('action.cancel')"
outline
filled
@click="cancelCookieChange"
/>
</div>
</template>
</HoppSmartModal>
</template>
<script setup lang="ts">
import { useI18n } from "@composables/i18n"
import { HoppSmartInput, HoppSmartTab } from "@hoppscotch/ui"
import { useService } from "dioc/vue"
import { watch, ref } from "vue"
import { CookieJarService } from "~/services/cookie-jar.service"
const cookieService = useService(CookieJarService)
// TODO: Build Managed Mode!
const props = defineProps<{
show: boolean
// Tuple of [domain, entryIndex, cookieEntry]
entry: [string, number, string] | null
}>()
const emit = defineEmits<{
(e: "save-cookie", cookie: string): void
(e: "hide-modal"): void
}>()
const t = useI18n()
const currentActiveTab = ref<"managed" | "raw">("managed")
const cookieName = ref("")
const cookieValue = ref("")
const cookiePath = ref("")
const cookieExpires = ref("")
const rawCookieString = ref("")
watch(
() => props.entry,
() => {
if (!props.entry) return
const cookieEntry = props.entry[2]
const parsedEntry = cookieService.parseSetCookieString(cookieEntry)
rawCookieString.value = cookieEntry
cookieName.value = parsedEntry.name ?? ""
cookieValue.value = parsedEntry.value ?? ""
cookiePath.value = parsedEntry.path ?? ""
cookieExpires.value = (parsedEntry.expires ?? new Date()).toISOString()
}
)
function hideModal() {
emit("hide-modal")
}
function cancelCookieChange() {
hideModal()
}
function saveCookieChange() {
emit("save-cookie", rawCookieString.value)
}
</script>