diff options
Diffstat (limited to 'packages/frontend/src')
3 files changed, 107 insertions, 1 deletions
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index a14c2ecef9..029d2212eb 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -65,6 +65,13 @@ SPDX-License-Identifier: AGPL-3.0-only </footer> </article> </component> + <footer v-if="linkAttribution" :class="$style.footer" style="float: right"> + <a :href="'/@' + linkAttribution.username"> + <p :class="$style.linkAttribution">{{i18n.ts.writtenBy}}</p> + <MkImgWithBlurhash :class="$style.linkAttributionIcon" :src="linkAttribution.avatarUrl" :hash="linkAttribution.avatarBlurhash" :cover="true" :onlyAvgColor="true"/> + <b :class="$style.linkAttribution" style="color: var(--MI_THEME-accent)">{{ linkAttribution.name }}</b> + </a> + </footer> <template v-if="showActions"> <div v-if="tweetId" :class="$style.action"> <MkButton :small="true" inline @click="tweetExpanded = true"> @@ -99,6 +106,7 @@ import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; import { deviceKind } from '@/utility/device-kind.js'; import MkButton from '@/components/MkButton.vue'; +import MkImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import { transformPlayerUrl } from '@/utility/player-url-transform.js'; import { store } from '@/store.js'; import { prefer } from '@/preferences.js'; @@ -146,6 +154,12 @@ const player = ref<SummalyResult['player']>({ height: null, allow: [], }); +const linkAttribution = ref<{ + name: string, + username: string, + avatarUrl: string, + avatarBlurhash: string, +} | null>(null); const playerEnabled = ref(false); const tweetId = ref<string | null>(null); const tweetExpanded = ref(props.detail); @@ -221,7 +235,15 @@ function refresh(withFetch = false) { return res.json(); }) - .then(async (info: SummalyResult & { haveNoteLocally?: boolean } | null) => { + .then(async (info: SummalyResult & { + haveNoteLocally?: boolean, + linkAttribution?: { + name: string, + username: string, + avatarUrl: string, + avatarBlurhash: string, + } + } | null) => { unknownUrl.value = info == null; title.value = info?.title ?? null; description.value = info?.description ?? null; @@ -236,6 +258,7 @@ function refresh(withFetch = false) { }; sensitive.value = info?.sensitive ?? false; activityPub.value = info?.activityPub ?? null; + linkAttribution.value = info?.linkAttribution ?? null; theNote.value = null; if (info?.haveNoteLocally) { @@ -395,6 +418,27 @@ refresh(); vertical-align: top; } +.linkAttributionIcon { + display: inline-block; + width: 1em; + height: 1em; + margin-left: 0.5em; + margin-right: 0.25em; + vertical-align: top; + border-radius: 50%; + * { + border-radius: 4px; + } +} + +.linkAttribution { + font-size: 0.8em; + display: inline-block; + margin: 0; + line-height: 16px; + vertical-align: top; +} + .action { display: flex; gap: 6px; diff --git a/packages/frontend/src/pages/settings/attribution-domains-setting.vue b/packages/frontend/src/pages/settings/attribution-domains-setting.vue new file mode 100644 index 0000000000..3090276f4f --- /dev/null +++ b/packages/frontend/src/pages/settings/attribution-domains-setting.vue @@ -0,0 +1,57 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div class="_gaps_m"> + <SearchMarker :keywords="['attribution', 'domains', 'preview', 'url']"> + <MkTextarea v-model="attributionDomains"> + <template #label>{{ i18n.ts.attributionDomains }}</template> + <template #caption>{{ i18n.ts.attributionDomainsDescription }} + <br/> + <Mfm :text="i18n.tsx.attributionDomainsTutorial({ user: $i.username, host: host})"/> + </template> + </MkTextarea> + </SearchMarker> + <MkButton primary :disabled="!changed" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> +</div> +</template> + +<script lang="ts" setup> +import { ref, watch } from 'vue'; +import { host as hostRaw } from '@@/js/config.js' +import { toUnicode } from 'punycode.js'; +import MkTextarea from '@/components/MkTextarea.vue'; +import MkInfo from '@/components/MkInfo.vue'; +import MkButton from '@/components/MkButton.vue'; +import { ensureSignin } from '@/i.js'; +import { misskeyApi } from '@/utility/misskey-api.js'; +import { i18n } from '@/i18n.js'; + +const $i = ensureSignin(); + +const attributionDomains = ref($i.attributionDomains.join('\n')); +const changed = ref(false); +const host = toUnicode(hostRaw); + +async function save() { + let domains = attributionDomains.value + .trim().split('\n') + .map(el => el.trim()) + .filter(el => el); + + await misskeyApi('i/update', { + attributionDomains: domains, + }); + + changed.value = false; + + // Refresh filtered list to signal to the user how they've been saved + attributionDomains.value = domains.join('\n'); +} + +watch(attributionDomains, () => { + changed.value = true; +}); +</script> diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index ee26a8911e..9dc7398062 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -161,6 +161,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.flagAsBotDescription }}</template> </MkSwitch> </SearchMarker> + + <AttributionDomainsSettings/> </div> </MkFolder> </SearchMarker> @@ -170,6 +172,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, reactive, ref, watch, defineAsyncComponent } from 'vue'; +import AttributionDomainsSettings from './attribution-domains-setting.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; @@ -217,6 +220,7 @@ const profile = reactive({ isBot: $i.isBot ?? false, isCat: $i.isCat ?? false, speakAsCat: $i.speakAsCat ?? false, + attributionDomains: $i.attributionDomains, }); watch(() => profile, () => { @@ -276,6 +280,7 @@ function save() { isBot: !!profile.isBot, isCat: !!profile.isCat, speakAsCat: !!profile.speakAsCat, + attributionDomains: !!profile.attributionDomains, }, undefined, { '0b3f9f6a-2f4d-4b1f-9fb4-49d3a2fd7191': { title: i18n.ts.yourNameContainsProhibitedWords, |