merge: Change the recent external url warning popup to the one from Cherrypick/MisskeyIO (!648)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/648 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Hazelnoot <acomputerdog@gmail.com>
This commit is contained in:
commit
42dbe999e1
17 changed files with 297 additions and 28 deletions
|
|
@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel ?? 'nofollow noopener'" :target="target"
|
||||
:behavior="props.navigationBehavior"
|
||||
:title="url"
|
||||
@click.prevent="self ? true : promptConfirm()"
|
||||
@click.prevent="self ? true : warningExternalWebsite(url)"
|
||||
@click.stop
|
||||
>
|
||||
<slot></slot>
|
||||
|
|
@ -23,7 +23,7 @@ import { useTooltip } from '@/scripts/use-tooltip.js';
|
|||
import * as os from '@/os.js';
|
||||
import { isEnabledUrlPreview } from '@/instance.js';
|
||||
import { MkABehavior } from '@/components/global/MkA.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
url: string;
|
||||
|
|
@ -49,16 +49,6 @@ if (isEnabledUrlPreview.value) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function promptConfirm() {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'question',
|
||||
text: i18n.tsx.confirmRemoteUrl({ x: props.url }),
|
||||
plain: true,
|
||||
});
|
||||
if (canceled) return;
|
||||
window.open(props.url, '_blank', 'nofollow noopener popup=false');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
|
|
|||
131
packages/frontend/src/components/MkUrlWarningDialog.vue
Normal file
131
packages/frontend/src/components/MkUrlWarningDialog.vue
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModal ref="modal" :preferType="'dialog'" :zPriority="'high'" @click="done(true)" @closed="emit('closed')">
|
||||
<div :class="$style.root" class="_gaps">
|
||||
<div class="_gaps_s">
|
||||
<div :class="$style.header">
|
||||
<div :class="$style.icon">
|
||||
<i class="ti ti-alert-triangle"></i>
|
||||
</div>
|
||||
<div :class="$style.title">{{ i18n.ts._externalNavigationWarning.title }}</div>
|
||||
</div>
|
||||
<div><Mfm :text="i18n.tsx._externalNavigationWarning.description({ host: instanceName })"/></div>
|
||||
<div class="_monospace" :class="$style.urlAddress">{{ url }}</div>
|
||||
<div>
|
||||
<MkSwitch v-model="trustThisDomain">{{ i18n.ts._externalNavigationWarning.trustThisDomain }}</MkSwitch>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.buttons">
|
||||
<MkButton data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||
<MkButton data-cy-modal-dialog-ok inline primary rounded @click="ok"><i class="ti ti-external-link"></i> {{ i18n.ts.open }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from 'vue';
|
||||
import { instanceName } from '@/config.js';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
||||
type Result = string | number | true | null;
|
||||
|
||||
const props = defineProps<{
|
||||
url: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'done', v: { canceled: true } | { canceled: false, result: Result }): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
const trustThisDomain = ref(false);
|
||||
|
||||
const domain = computed(() => new URL(props.url).hostname);
|
||||
|
||||
// overload function を使いたいので lint エラーを無視する
|
||||
function done(canceled: true): void;
|
||||
function done(canceled: false, result: Result): void; // eslint-disable-line no-redeclare
|
||||
function done(canceled: boolean, result?: Result): void { // eslint-disable-line no-redeclare
|
||||
emit('done', { canceled, result } as { canceled: true } | { canceled: false, result: Result });
|
||||
modal.value?.close();
|
||||
}
|
||||
|
||||
async function ok() {
|
||||
const result = true;
|
||||
if (!defaultStore.state.trustedDomains.includes(domain.value) && trustThisDomain.value) {
|
||||
await defaultStore.set('trustedDomains', defaultStore.state.trustedDomains.concat(domain.value));
|
||||
}
|
||||
done(false, result);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
done(true);
|
||||
}
|
||||
|
||||
function onKeydown(evt: KeyboardEvent) {
|
||||
if (evt.key === 'Escape') cancel();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', onKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('keydown', onKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
padding: 32px;
|
||||
width: 100%;
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
box-sizing: border-box;
|
||||
background: var(--panel);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75em;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 18px;
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.urlAddress {
|
||||
padding: 10px 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--divider);
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: right;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:is="self ? 'MkA' : 'a'" ref="el" :class="$style.root" class="_link" :[attr]="self ? props.url.substring(local.length) : props.url" :rel="rel ?? 'nofollow noopener'" :target="target"
|
||||
:behavior="props.navigationBehavior"
|
||||
@contextmenu.stop="() => {}"
|
||||
@click.prevent="self ? true : warningExternalWebsite(props.url)"
|
||||
@click.stop
|
||||
>
|
||||
<template v-if="!self">
|
||||
|
|
@ -34,6 +35,7 @@ import { useTooltip } from '@/scripts/use-tooltip.js';
|
|||
import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
|
||||
import { isEnabledUrlPreview } from '@/instance.js';
|
||||
import { MkABehavior } from '@/components/global/MkA.vue';
|
||||
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
url: string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue