From f530fc28531c8111d2fa2b357d65b96c7e6cd1d9 Mon Sep 17 00:00:00 2001 From: Daniel Maurer Date: Wed, 26 Jul 2023 15:59:36 +0200 Subject: [PATCH] fix: inputfield to readonly + asterisk if secret --- .../hoppscotch-common/src/components.d.ts | 18 ----- .../components/environments/my/Details.vue | 27 ++++++- .../components/environments/teams/Details.vue | 26 +++++++ .../src/components/smart/EnvInput.vue | 70 +++++++++++++++---- .../editor/extensions/HoppEnvironment.ts | 2 +- 5 files changed, 109 insertions(+), 34 deletions(-) diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index 54c8a50a2..8fd45571b 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -133,24 +133,6 @@ declare module 'vue' { HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default'] HttpTests: typeof import('./components/http/Tests.vue')['default'] HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default'] - IconLucideActivity: typeof import('~icons/lucide/activity')['default'] - IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] - IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] - IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] - IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] - IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] - IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] - IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default'] - IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] - IconLucideInfo: typeof import('~icons/lucide/info')['default'] - IconLucideLayers: typeof import('~icons/lucide/layers')['default'] - IconLucideListEnd: typeof import('~icons/lucide/list-end')['default'] - IconLucideMinus: typeof import('~icons/lucide/minus')['default'] - IconLucideRss: typeof import('~icons/lucide/rss')['default'] - IconLucideSearch: typeof import('~icons/lucide/search')['default'] - IconLucideUsers: typeof import('~icons/lucide/users')['default'] - IconLucideVerified: typeof import('~icons/lucide/verified')['default'] - InterceptorsExtensionSubtitle: typeof import('./components/interceptors/ExtensionSubtitle.vue')['default'] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default'] LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default'] LensesRenderersAudioLensRenderer: typeof import('./components/lenses/renderers/AudioLensRenderer.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/environments/my/Details.vue b/packages/hoppscotch-common/src/components/environments/my/Details.vue index 06e31345c..6a163dae3 100644 --- a/packages/hoppscotch-common/src/components/environments/my/Details.vue +++ b/packages/hoppscotch-common/src/components/environments/my/Details.vue @@ -60,6 +60,7 @@ :placeholder="`${t('count.value', { count: index + 1 })}`" :envs="liveEnvs" :name="'value' + index" + :secret="env.secret" />
{ } }) +const oldEnvironments = ref(workingEnv.value) + const envList = useReadonlyStream(environments$, []) || props.envVars() const evnExpandError = computed(() => { @@ -246,6 +249,21 @@ const liveEnvs = computed(() => { } }) +watch(liveEnvs, (newLiveEnv, oldLiveEnv) => { + for (let i = 0; i < newLiveEnv.length; i++) { + const newVar = newLiveEnv[i] + const oldVar = oldLiveEnv[i] + + if (!newVar.secret && newVar.value !== oldVar.value) { + const _oldEnvironments = oldEnvironments.value + if (_oldEnvironments) { + _oldEnvironments.variables[i].value = newVar.value + } + oldEnvironments.value = _oldEnvironments + } + } +}) + watch( () => props.show, (show) => { @@ -292,7 +310,14 @@ const saveEnvironment = () => { toast.error(`${t("environment.invalid_name")}`) return } - + const _vars = vars.value + for (let i = 0; i < vars.value.length; i++) { + const value = oldEnvironments.value?.variables[i].value + if (value) { + _vars[i].env.value = value + } + } + vars.value = _vars const filterdVariables = pipe( vars.value, A.filterMap( diff --git a/packages/hoppscotch-common/src/components/environments/teams/Details.vue b/packages/hoppscotch-common/src/components/environments/teams/Details.vue index 40b5cebc1..072477903 100644 --- a/packages/hoppscotch-common/src/components/environments/teams/Details.vue +++ b/packages/hoppscotch-common/src/components/environments/teams/Details.vue @@ -63,6 +63,7 @@ :envs="liveEnvs" :name="'value' + index" :readonly="isViewer" + :secret="env.secret" />
([ { id: idTicker.value++, env: { key: "", value: "", secret: false } }, ]) +const oldEnvironments = ref(props.envVars()) const clearIcon = refAutoReset( IconTrash2, 1000 @@ -224,6 +226,21 @@ const liveEnvs = computed(() => { } }) +watch(liveEnvs, (newLiveEnv, oldLiveEnv) => { + for (let i = 0; i < newLiveEnv.length; i++) { + const newVar = newLiveEnv[i] + const oldVar = oldLiveEnv[i] + + if (!newVar.secret && newVar.value !== oldVar.value) { + const _oldEnvironments = oldEnvironments.value + if (_oldEnvironments) { + _oldEnvironments[i].value = newVar.value + } + oldEnvironments.value = _oldEnvironments + } + } +}) + watch( () => props.show, (show) => { @@ -286,6 +303,15 @@ const saveEnvironment = async () => { return } + const _vars = vars.value + for (let i = 0; i < vars.value.length; i++) { + const value = oldEnvironments.value[i].value + if (value) { + _vars[i].env.value = value + } + } + vars.value = _vars + const filterdVariables = pipe( vars.value, A.filterMap( diff --git a/packages/hoppscotch-common/src/components/smart/EnvInput.vue b/packages/hoppscotch-common/src/components/smart/EnvInput.vue index f9bcfa942..e35bbe050 100644 --- a/packages/hoppscotch-common/src/components/smart/EnvInput.vue +++ b/packages/hoppscotch-common/src/components/smart/EnvInput.vue @@ -55,7 +55,12 @@ import { keymap, tooltips, } from "@codemirror/view" -import { EditorSelection, EditorState, Extension } from "@codemirror/state" +import { + EditorSelection, + EditorState, + Extension, + StateEffect, +} from "@codemirror/state" import { clone } from "lodash-es" import { history, historyKeymap } from "@codemirror/commands" import { inputTheme } from "~/helpers/editor/themes/baseTheme" @@ -81,6 +86,8 @@ const props = withDefaults( readonly?: boolean autoCompleteSource?: string[] inspectionResults?: InspectorResult[] | undefined + defaultValue?: string + secret?: boolean }>(), { modelValue: "", @@ -93,6 +100,8 @@ const props = withDefaults( autoCompleteSource: undefined, inspectionResult: undefined, inspectionResults: undefined, + defaultValue: "", + secret: false, } ) @@ -106,6 +115,8 @@ const emit = defineEmits<{ (e: "click", ev: any): void }>() +const ASTERISK = "******" + const cachedValue = ref(props.modelValue) const view = ref() @@ -265,6 +276,23 @@ watch( } ) +const prevModelValue = ref(props.modelValue) +watch( + () => props.secret, + (newValue, oldValue) => { + if (newValue !== oldValue) { + let visibleValue = ASTERISK + if (!newValue) { + visibleValue = prevModelValue.value + } else { + prevModelValue.value = props.modelValue + } + emit("update:modelValue", visibleValue) + updateEditorViewTheme(newValue) + } + } +) + /** * Used to scroll the active suggestion into view */ @@ -286,7 +314,6 @@ watch( () => props.modelValue, (newVal) => { const singleLinedText = newVal.replaceAll("\n", "") - const currDoc = view.value?.state.doc .toJSON() .join(view.value.state.lineBreak) @@ -357,6 +384,14 @@ function handleTextSelection() { } } } +const updateEditorViewTheme = (readonly: boolean) => { + if (view.value) { + const extensions: Extension = getExtensions(readonly) + view.value.dispatch({ + effects: [StateEffect.reconfigure.of(extensions)], + }) + } +} const initView = (el: any) => { // Debounce to prevent double click from selecting the word @@ -367,17 +402,31 @@ const initView = (el: any) => { el.addEventListener("mouseup", debounceFn) el.addEventListener("keyup", debounceFn) + if (props.secret) { + emit("update:modelValue", ASTERISK) + } + const extensions: Extension = getExtensions(props.readonly || props.secret) + view.value = new EditorView({ + parent: el, + state: EditorState.create({ + doc: props.modelValue, + extensions, + }), + }) +} + +const getExtensions = (readonly: boolean): Extension => { const extensions: Extension = [ EditorView.contentAttributes.of({ "aria-label": props.placeholder }), EditorView.contentAttributes.of({ "data-enable-grammarly": "false" }), EditorView.updateListener.of((update) => { - if (props.readonly) { + if (readonly) { update.view.contentDOM.inputMode = "none" } }), - EditorState.changeFilter.of(() => !props.readonly), + EditorState.changeFilter.of(() => !readonly), inputTheme, - props.readonly + readonly ? EditorView.theme({ ".cm-content": { caretColor: "var(--secondary-dark-color)", @@ -409,7 +458,7 @@ const initView = (el: any) => { ViewPlugin.fromClass( class { update(update: ViewUpdate) { - if (props.readonly) return + if (readonly) return if (update.docChanged) { const prevValue = clone(cachedValue.value) @@ -448,14 +497,7 @@ const initView = (el: any) => { history(), keymap.of([...historyKeymap]), ] - - view.value = new EditorView({ - parent: el, - state: EditorState.create({ - doc: props.modelValue, - extensions, - }), - }) + return extensions } const triggerTextSelection = () => { diff --git a/packages/hoppscotch-common/src/helpers/editor/extensions/HoppEnvironment.ts b/packages/hoppscotch-common/src/helpers/editor/extensions/HoppEnvironment.ts index f9d0e1cad..fa0d22861 100644 --- a/packages/hoppscotch-common/src/helpers/editor/extensions/HoppEnvironment.ts +++ b/packages/hoppscotch-common/src/helpers/editor/extensions/HoppEnvironment.ts @@ -68,7 +68,7 @@ const cursorTooltipField = (aggregateEnvs: AggregateEnvironment[]) => let envValue = tooltipEnv?.value ?? "Not found" if (tooltipEnv?.secret) { - envValue = "*".repeat(envValue.length) + envValue = "******" } const result = parseTemplateStringE(envValue, aggregateEnvs)