feat: clean up cookies implementation
This commit is contained in:
@@ -59,13 +59,16 @@
|
||||
"modal": {
|
||||
"new_domain_name": "New domain name",
|
||||
"set": "Set a cookie",
|
||||
"cookie_string": "Cookie string",
|
||||
"cookie_string": "Set-Cookie string",
|
||||
"cookie_name": "Name",
|
||||
"cookie_value": "Value",
|
||||
"cookie_path": "Path",
|
||||
"cookie_expires": "Expires",
|
||||
"managed_tab": "Managed",
|
||||
"raw_tab": "Raw"
|
||||
"raw_tab": "Raw",
|
||||
"interceptor_no_support": "Your currently selected interceptor does not support cookies.",
|
||||
"no_domains": "No domains defined",
|
||||
"no_cookies_in_domain": "No cookies set for this domain"
|
||||
}
|
||||
},
|
||||
"app": {
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
@close="hideModal"
|
||||
>
|
||||
<template #body>
|
||||
<div
|
||||
v-if="!currentInterceptorSupportsCookies"
|
||||
class="flex flex-col gap-2 p-5 items-center"
|
||||
>
|
||||
<icon-lucide-info />
|
||||
{{ t("cookies.modal.interceptor_no_support") }}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-x-2 border-b border-dividerLight pb-3">
|
||||
<HoppSmartInput
|
||||
v-model="newDomainText"
|
||||
@@ -22,6 +30,14 @@
|
||||
</div>
|
||||
<div class="pt-3 flex flex-col gap-y-6">
|
||||
<div
|
||||
v-if="workingCookieJar.size === 0"
|
||||
class="flex flex-col items-center p-5 gap-2"
|
||||
>
|
||||
<icon-lucide-info />
|
||||
{{ t("cookies.modal.no_domains") }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
v-for="[domain, entries] in workingCookieJar.entries()"
|
||||
:key="domain"
|
||||
class="flex flex-col gap-y-2"
|
||||
@@ -43,6 +59,14 @@
|
||||
<div class="border rounded border-divider">
|
||||
<div class="divide-y divide-dividerLight">
|
||||
<div
|
||||
v-if="entries.length === 0"
|
||||
class="flex flex-col items-center p-5 gap-2"
|
||||
>
|
||||
<icon-lucide-info />
|
||||
{{ t("cookies.modal.no_cookies_in_domain") }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
v-for="(entry, entryIndex) in entries"
|
||||
:key="`${entry}-${entryIndex}`"
|
||||
class="flex divide-x divide-dividerLight"
|
||||
@@ -93,7 +117,7 @@
|
||||
<CookiesEditCookie
|
||||
:show="!!showEditModalFor"
|
||||
:entry="showEditModalFor"
|
||||
@save-cookie="saveCookieUpdate"
|
||||
@save-cookie="saveCookie"
|
||||
@hide-modal="showEditModalFor = null"
|
||||
/>
|
||||
</template>
|
||||
@@ -107,7 +131,9 @@ 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"
|
||||
import { ref, watch, computed } from "vue"
|
||||
import { InterceptorService } from "~/services/interceptor.service"
|
||||
import { EditCookieConfig } from "./EditCookie.vue"
|
||||
|
||||
const props = defineProps<{
|
||||
show: boolean
|
||||
@@ -121,10 +147,19 @@ const t = useI18n()
|
||||
|
||||
const newDomainText = ref("")
|
||||
|
||||
const interceptorService = useService(InterceptorService)
|
||||
const cookieJarService = useService(CookieJarService)
|
||||
|
||||
const workingCookieJar = ref(cloneDeep(cookieJarService.cookieJar.value))
|
||||
|
||||
const currentInterceptorSupportsCookies = computed(() => {
|
||||
const currentInterceptor = interceptorService.currentInterceptor.value
|
||||
|
||||
if (!currentInterceptor) return true
|
||||
|
||||
return currentInterceptor.supportsCookies ?? false
|
||||
})
|
||||
|
||||
function addNewDomain() {
|
||||
workingCookieJar.value.set(newDomainText.value, [])
|
||||
newDomainText.value = ""
|
||||
@@ -135,11 +170,7 @@ function deleteDomain(domain: string) {
|
||||
}
|
||||
|
||||
function addCookieToDomain(domain: string) {
|
||||
const entry = workingCookieJar.value.get(domain)
|
||||
|
||||
if (entry) {
|
||||
entry.push("")
|
||||
}
|
||||
showEditModalFor.value = { type: "create", domain }
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -151,8 +182,7 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
// Tuple of [domain, entryIndex]
|
||||
const showEditModalFor = ref<[string, number, string] | null>(null)
|
||||
const showEditModalFor = ref<EditCookieConfig | null>(null)
|
||||
|
||||
function saveCookieChanges() {
|
||||
cookieJarService.cookieJar.value = workingCookieJar.value
|
||||
@@ -164,7 +194,12 @@ function cancelCookieChanges() {
|
||||
}
|
||||
|
||||
function editCookie(domain: string, entryIndex: number, cookieEntry: string) {
|
||||
showEditModalFor.value = [domain, entryIndex, cookieEntry]
|
||||
showEditModalFor.value = {
|
||||
type: "edit",
|
||||
domain,
|
||||
entryIndex,
|
||||
currentCookieEntry: cookieEntry,
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCookie(domain: string, entryIndex: number) {
|
||||
@@ -175,10 +210,21 @@ function deleteCookie(domain: string, entryIndex: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function saveCookieUpdate(cookie: string) {
|
||||
if (!showEditModalFor.value) return
|
||||
function saveCookie(cookie: string) {
|
||||
if (showEditModalFor.value?.type === "create") {
|
||||
const { domain } = showEditModalFor.value
|
||||
|
||||
const [domain, entryIndex] = showEditModalFor.value!
|
||||
const entry = workingCookieJar.value.get(domain)!
|
||||
entry.push(cookie)
|
||||
|
||||
showEditModalFor.value = null
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (showEditModalFor.value?.type !== "edit") return
|
||||
|
||||
const { domain, entryIndex } = showEditModalFor.value!
|
||||
|
||||
const entry = workingCookieJar.value.get(domain)
|
||||
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
@close="hideModal"
|
||||
>
|
||||
<template #body>
|
||||
<HoppSmartInput
|
||||
v-model="rawCookieString"
|
||||
:placeholder="t('cookies.modal.cookie_string')"
|
||||
/>
|
||||
<div class="h-46 border rounded border-dividerLight">
|
||||
<div
|
||||
ref="cookieEditor"
|
||||
class="h-full border-t rounded-b border-dividerLight"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
@@ -28,21 +30,46 @@
|
||||
@click="cancelCookieChange"
|
||||
/>
|
||||
</div>
|
||||
<span class="flex">
|
||||
<HoppButtonSecondary
|
||||
:icon="pasteIcon"
|
||||
:label="`${t('action.paste')}`"
|
||||
filled
|
||||
outline
|
||||
@click="handlePaste"
|
||||
class="self-end"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</HoppSmartModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export type EditCookieConfig =
|
||||
| { type: "create"; domain: string }
|
||||
| {
|
||||
type: "edit"
|
||||
domain: string
|
||||
entryIndex: number
|
||||
currentCookieEntry: string
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "@composables/i18n"
|
||||
import { useCodemirror } from "~/composables/codemirror"
|
||||
import { watch, ref } from "vue"
|
||||
import { refAutoReset } from "@vueuse/core"
|
||||
import IconClipboard from "~icons/lucide/clipboard"
|
||||
import IconCheck from "~icons/lucide/check"
|
||||
import { useToast } from "~/composables/toast"
|
||||
|
||||
// TODO: Build Managed Mode!
|
||||
|
||||
const props = defineProps<{
|
||||
show: boolean
|
||||
|
||||
// Tuple of [domain, entryIndex, cookieEntry]
|
||||
entry: [string, number, string] | null
|
||||
entry: EditCookieConfig | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -52,16 +79,33 @@ const emit = defineEmits<{
|
||||
|
||||
const t = useI18n()
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const cookieEditor = ref<HTMLElement>()
|
||||
const rawCookieString = ref("")
|
||||
|
||||
useCodemirror(cookieEditor, rawCookieString, {
|
||||
extendedEditorConfig: {
|
||||
mode: "text/plain",
|
||||
placeholder: `${t("cookies.modal.cookie_string")}`,
|
||||
lineWrapping: true,
|
||||
},
|
||||
linter: null,
|
||||
completer: null,
|
||||
environmentHighlights: false,
|
||||
})
|
||||
|
||||
const pasteIcon = refAutoReset<typeof IconClipboard | typeof IconCheck>(
|
||||
IconClipboard,
|
||||
1000
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.entry,
|
||||
() => {
|
||||
if (!props.entry) return
|
||||
if (props.entry?.type !== "edit") return
|
||||
|
||||
const cookieEntry = props.entry[2]
|
||||
|
||||
rawCookieString.value = cookieEntry
|
||||
rawCookieString.value = props.entry.currentCookieEntry
|
||||
}
|
||||
)
|
||||
|
||||
@@ -73,6 +117,19 @@ function cancelCookieChange() {
|
||||
hideModal()
|
||||
}
|
||||
|
||||
async function handlePaste() {
|
||||
try {
|
||||
const text = await navigator.clipboard.readText()
|
||||
if (text) {
|
||||
rawCookieString.value = text
|
||||
pasteIcon.value = IconCheck
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to copy: ", e)
|
||||
toast.error(t("profile.no_permission").toString())
|
||||
}
|
||||
}
|
||||
|
||||
function saveCookieChange() {
|
||||
emit("save-cookie", rawCookieString.value)
|
||||
}
|
||||
|
||||
@@ -72,6 +72,61 @@ describe("InterceptorService", () => {
|
||||
expect(service.currentInterceptorID.value).not.toEqual("unknown")
|
||||
})
|
||||
|
||||
it("currentInterceptor points to the instance of the currently selected interceptor", () => {
|
||||
const container = new TestContainer()
|
||||
|
||||
const service = container.bind(InterceptorService)
|
||||
|
||||
const interceptor = {
|
||||
interceptorID: "test",
|
||||
name: () => "test interceptor",
|
||||
selectable: { type: "selectable" as const },
|
||||
runRequest: () => {
|
||||
throw new Error("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
service.registerInterceptor(interceptor)
|
||||
service.currentInterceptorID.value = "test"
|
||||
|
||||
expect(service.currentInterceptor.value).toBe(interceptor)
|
||||
})
|
||||
|
||||
it("currentInterceptor updates when the currentInterceptorID changes", () => {
|
||||
const container = new TestContainer()
|
||||
|
||||
const service = container.bind(InterceptorService)
|
||||
|
||||
const interceptor = {
|
||||
interceptorID: "test",
|
||||
name: () => "test interceptor",
|
||||
selectable: { type: "selectable" as const },
|
||||
runRequest: () => {
|
||||
throw new Error("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
const interceptor_2 = {
|
||||
interceptorID: "test2",
|
||||
name: () => "test interceptor",
|
||||
selectable: { type: "selectable" as const },
|
||||
runRequest: () => {
|
||||
throw new Error("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
service.registerInterceptor(interceptor)
|
||||
service.registerInterceptor(interceptor_2)
|
||||
|
||||
service.currentInterceptorID.value = "test"
|
||||
|
||||
expect(service.currentInterceptor.value).toBe(interceptor)
|
||||
|
||||
service.currentInterceptorID.value = "test2"
|
||||
expect(service.currentInterceptor.value).not.toBe(interceptor)
|
||||
expect(service.currentInterceptor.value).toBe(interceptor_2)
|
||||
})
|
||||
|
||||
describe("registerInterceptor", () => {
|
||||
it("should register the interceptor", () => {
|
||||
const container = new TestContainer()
|
||||
|
||||
@@ -167,6 +167,16 @@ export class InterceptorService extends Service {
|
||||
Array.from(this.interceptors.values())
|
||||
)
|
||||
|
||||
/**
|
||||
* Gives an instance to the current interceptor.
|
||||
* NOTE: Do not update from here, this is only for reading.
|
||||
*/
|
||||
public currentInterceptor = computed(() => {
|
||||
if (this.currentInterceptorID.value === null) return null
|
||||
|
||||
return this.interceptors.get(this.currentInterceptorID.value)
|
||||
})
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user