Merge pull request MisskeyIO#982 from cherry-pick
cherry picked from upstream
This commit is contained in:
commit
a3b41c1caa
12 changed files with 66 additions and 28 deletions
|
@ -37,7 +37,6 @@ export class UrlPreviewService {
|
||||||
@bindThis
|
@bindThis
|
||||||
private wrap(url?: string | null): string | null {
|
private wrap(url?: string | null): string | null {
|
||||||
if (!url) return null;
|
if (!url) return null;
|
||||||
if (!RegExp(/^https?:\/\//).exec(url)) return url;
|
|
||||||
|
|
||||||
return appendQuery(
|
return appendQuery(
|
||||||
`${this.config.mediaProxy}/preview/${encodeURIComponent(omitHttps(url))}`,
|
`${this.config.mediaProxy}/preview/${encodeURIComponent(omitHttps(url))}`,
|
||||||
|
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
ref="el"
|
ref="el"
|
||||||
style="word-break: break-all;"
|
style="word-break: break-all;"
|
||||||
class="_link"
|
class="_link"
|
||||||
:[attr]="self ? url.substring(local.length) : url"
|
:[attr]="maybeRelativeUrl"
|
||||||
:rel="rel ?? 'nofollow noopener'"
|
:rel="rel ?? 'nofollow noopener'"
|
||||||
:target="target"
|
:target="target"
|
||||||
:behavior="props.navigationBehavior"
|
:behavior="props.navigationBehavior"
|
||||||
|
@ -28,7 +28,8 @@ import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||||
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { isEnabledUrlPreview } from '@/instance.js';
|
import { isEnabledUrlPreview } from '@/instance.js';
|
||||||
import { MkABehavior } from '@/components/global/MkA.vue';
|
import type { MkABehavior } from '@/components/global/MkA.vue';
|
||||||
|
import { maybeMakeRelative } from '@/scripts/url.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
url: string;
|
url: string;
|
||||||
|
@ -39,7 +40,8 @@ const props = withDefaults(defineProps<{
|
||||||
hideIcon: false,
|
hideIcon: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const self = props.url.startsWith(local);
|
const maybeRelativeUrl = maybeMakeRelative(props.url, local);
|
||||||
|
const self = maybeRelativeUrl !== props.url;
|
||||||
const attr = self ? 'to' : 'href';
|
const attr = self ? 'to' : 'href';
|
||||||
const target = self ? null : '_blank';
|
const target = self ? null : '_blank';
|
||||||
|
|
||||||
|
|
|
@ -47,13 +47,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<component
|
<component
|
||||||
:is="self ? 'MkA' : 'a'"
|
:is="self ? 'MkA' : 'a'"
|
||||||
:class="[$style.link, { [$style.compact]: compact }]"
|
:class="[$style.link, { [$style.compact]: compact }]"
|
||||||
:[attr]="self ? url.substring(local.length) : url"
|
:[attr]="maybeRelativeUrl"
|
||||||
rel="nofollow noopener"
|
rel="nofollow noopener"
|
||||||
:target="target"
|
:target="target"
|
||||||
:title="url"
|
:title="url"
|
||||||
@click="(ev: MouseEvent) => warningExternalWebsite(ev, url)"
|
@click="(ev: MouseEvent) => warningExternalWebsite(ev, url)"
|
||||||
>
|
>
|
||||||
<div v-if="thumbnail" :class="[$style.thumbnail, { [$style.thumbnailBlur]: sensitive }]" :style="defaultStore.state.dataSaver.urlPreview ? '' : `background-image: url('${thumbnail}')`">
|
<div v-if="thumbnail" :class="[$style.thumbnail, { [$style.thumbnailBlur]: sensitive }]" :style="defaultStore.state.dataSaver.urlPreview ? '' : { backgroundImage: `url('${thumbnail}')` }">
|
||||||
</div>
|
</div>
|
||||||
<article :class="$style.body">
|
<article :class="$style.body">
|
||||||
<header :class="$style.header">
|
<header :class="$style.header">
|
||||||
|
@ -101,6 +101,7 @@ import MkButton from '@/components/MkButton.vue';
|
||||||
import { versatileLang } from '@/scripts/intl-const.js';
|
import { versatileLang } from '@/scripts/intl-const.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
||||||
|
import { maybeMakeRelative } from '@/scripts/url.js';
|
||||||
|
|
||||||
type SummalyResult = Awaited<ReturnType<typeof summaly>>;
|
type SummalyResult = Awaited<ReturnType<typeof summaly>>;
|
||||||
|
|
||||||
|
@ -118,7 +119,8 @@ const props = withDefaults(defineProps<{
|
||||||
const MOBILE_THRESHOLD = 500;
|
const MOBILE_THRESHOLD = 500;
|
||||||
const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
|
const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
|
||||||
|
|
||||||
const self = props.url.startsWith(local);
|
const maybeRelativeUrl = maybeMakeRelative(props.url, local);
|
||||||
|
const self = maybeRelativeUrl !== props.url;
|
||||||
const attr = self ? 'to' : 'href';
|
const attr = self ? 'to' : 'href';
|
||||||
const target = self ? null : '_blank';
|
const target = self ? null : '_blank';
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="_panel" :class="$style.root">
|
<div class="_panel" :class="$style.root">
|
||||||
<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
|
<div :class="$style.banner" :style="user.bannerUrl ? { backgroundImage: `url(${user.bannerUrl})` } : ''"></div>
|
||||||
<MkAvatar :class="$style.avatar" :user="user" indicator/>
|
<MkAvatar :class="$style.avatar" :user="user" indicator/>
|
||||||
<div :class="$style.title">
|
<div :class="$style.title">
|
||||||
<MkA :class="$style.name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
|
<MkA :class="$style.name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
|
||||||
|
|
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
>
|
>
|
||||||
<div v-if="showing" :class="$style.root" class="_popup _shadow" :style="{ zIndex, top: top + 'px', left: left + 'px' }" @mouseover="() => { emit('mouseover'); }" @mouseleave="() => { emit('mouseleave'); }">
|
<div v-if="showing" :class="$style.root" class="_popup _shadow" :style="{ zIndex, top: top + 'px', left: left + 'px' }" @mouseover="() => { emit('mouseover'); }" @mouseleave="() => { emit('mouseleave'); }">
|
||||||
<div v-if="user != null">
|
<div v-if="user != null">
|
||||||
<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''">
|
<div :class="$style.banner" :style="user.bannerUrl ? { backgroundImage: `url(${user.bannerUrl})` } : ''">
|
||||||
<span v-if="$i && $i.id != user.id && user.isFollowed" :class="$style.followed">{{ i18n.ts.followsYou }}</span>
|
<span v-if="$i && $i.id != user.id && user.isFollowed" :class="$style.followed">{{ i18n.ts.followsYou }}</span>
|
||||||
</div>
|
</div>
|
||||||
<svg viewBox="0 0 128 128" :class="$style.avatarBack">
|
<svg viewBox="0 0 128 128" :class="$style.avatarBack">
|
||||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-adaptive-bg class="_panel" style="position: relative;">
|
<div v-adaptive-bg class="_panel" style="position: relative;">
|
||||||
<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
|
<div :class="$style.banner" :style="user.bannerUrl ? { backgroundImage: `url(${user.bannerUrl})` } : ''"></div>
|
||||||
<MkAvatar :class="$style.avatar" :user="user" indicator/>
|
<MkAvatar :class="$style.avatar" :user="user" indicator/>
|
||||||
<div :class="$style.title">
|
<div :class="$style.title">
|
||||||
<div :class="$style.name"><MkUserName :user="user" :nowrap="false"/></div>
|
<div :class="$style.name"><MkUserName :user="user" :nowrap="false"/></div>
|
||||||
|
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
ref="el"
|
ref="el"
|
||||||
:class="$style.root"
|
:class="$style.root"
|
||||||
class="_link"
|
class="_link"
|
||||||
:[attr]="self ? props.url.substring(local.length) : props.url"
|
:[attr]="maybeRelativeUrl"
|
||||||
:rel="rel ?? 'nofollow noopener'"
|
:rel="rel ?? 'nofollow noopener'"
|
||||||
:target="target"
|
:target="target"
|
||||||
:behavior="props.navigationBehavior"
|
:behavior="props.navigationBehavior"
|
||||||
|
@ -40,7 +40,8 @@ import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||||
import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
|
import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
|
||||||
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
import { warningExternalWebsite } from '@/scripts/warning-external-website.js';
|
||||||
import { isEnabledUrlPreview } from '@/instance.js';
|
import { isEnabledUrlPreview } from '@/instance.js';
|
||||||
import { MkABehavior } from '@/components/global/MkA.vue';
|
import type { MkABehavior } from '@/components/global/MkA.vue';
|
||||||
|
import { maybeMakeRelative } from '@/scripts/url.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
url: string;
|
url: string;
|
||||||
|
@ -51,7 +52,8 @@ const props = withDefaults(defineProps<{
|
||||||
showUrlPreview: true,
|
showUrlPreview: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const self = props.url.startsWith(local);
|
const maybeRelativeUrl = maybeMakeRelative(props.url, local);
|
||||||
|
const self = maybeRelativeUrl !== props.url;
|
||||||
const url = new URL(props.url);
|
const url = new URL(props.url);
|
||||||
if (!['http:', 'https:'].includes(url.protocol)) throw new Error('invalid url');
|
if (!['http:', 'https:'].includes(url.protocol)) throw new Error('invalid url');
|
||||||
const el = ref();
|
const el = ref();
|
||||||
|
|
|
@ -51,7 +51,9 @@ export function createAiScriptEnv(opts) {
|
||||||
}),
|
}),
|
||||||
'Mk:api': values.FN_NATIVE(async ([ep, param, token]) => {
|
'Mk:api': values.FN_NATIVE(async ([ep, param, token]) => {
|
||||||
utils.assertString(ep);
|
utils.assertString(ep);
|
||||||
if (ep.value.includes('://')) throw new Error('invalid endpoint');
|
if (ep.value.includes('://') || ep.value.includes('..')) {
|
||||||
|
throw new Error('invalid endpoint');
|
||||||
|
}
|
||||||
if (token) {
|
if (token) {
|
||||||
utils.assertString(token);
|
utils.assertString(token);
|
||||||
// バグがあればundefinedもあり得るため念のため
|
// バグがあればundefinedもあり得るため念のため
|
||||||
|
|
|
@ -6,16 +6,30 @@
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
|
||||||
export function shouldCollapsed(note: Misskey.entities.Note, urls: string[]): boolean {
|
export function shouldCollapsed(note: Misskey.entities.Note, urls: string[]): boolean {
|
||||||
const collapsed = note.cw == null && note.text != null && (
|
if (note.cw != null) {
|
||||||
(note.text.includes('$[x2')) ||
|
return false;
|
||||||
(note.text.includes('$[x3')) ||
|
}
|
||||||
(note.text.includes('$[x4')) ||
|
|
||||||
(note.text.includes('$[scale')) ||
|
|
||||||
(note.text.split('\n').length > 9) ||
|
|
||||||
(note.text.length > 500) ||
|
|
||||||
(note.files.length >= 5) ||
|
|
||||||
(urls.length >= 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
return collapsed;
|
if (note.text != null) {
|
||||||
|
if (
|
||||||
|
note.text.includes('$[x2') ||
|
||||||
|
note.text.includes('$[x3') ||
|
||||||
|
note.text.includes('$[x4') ||
|
||||||
|
note.text.includes('$[scale') ||
|
||||||
|
note.text.split('\n').length > 9 ||
|
||||||
|
note.text.length > 500
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urls.length >= 4) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note.files != null && note.files.length >= 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,3 +27,20 @@ export function omitHttps(url: string): string {
|
||||||
if (url.startsWith('https%3A%2F%2F')) return url.slice(14);
|
if (url.startsWith('https%3A%2F%2F')) return url.slice(14);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function maybeMakeRelative(urlStr: string, baseStr: string): string {
|
||||||
|
try {
|
||||||
|
const baseObj = new URL(baseStr);
|
||||||
|
const urlObj = new URL(urlStr);
|
||||||
|
/* in all places where maybeMakeRelative is used, baseStr is the
|
||||||
|
* instance's public URL, which can't have path components, so the
|
||||||
|
* relative URL will always have the whole path from the urlStr
|
||||||
|
*/
|
||||||
|
if (urlObj.origin === baseObj.origin) {
|
||||||
|
return urlObj.pathname + urlObj.search + urlObj.hash;
|
||||||
|
}
|
||||||
|
return urlStr;
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div
|
<div
|
||||||
v-for="(image, i) in images" :key="i"
|
v-for="(image, i) in images" :key="i"
|
||||||
:class="$style.img"
|
:class="$style.img"
|
||||||
:style="`background-image: url(${thumbnail(image)})`"
|
:style="{ backgroundImage: `url(${thumbnail(image)})` }"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<rect
|
<rect
|
||||||
x="-2" y="-2"
|
x="-2" y="-2"
|
||||||
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
||||||
:style="`stroke: none; fill: url(#${ cpuGradientId }); mask: url(#${ cpuMaskId })`"
|
:style="{ stroke: 'none', fill: `url(#${ cpuGradientId })`, mask: `url(#${ cpuMaskId })` }"
|
||||||
/>
|
/>
|
||||||
<text x="1" y="5">CPU <tspan>{{ cpuP }}%</tspan></text>
|
<text x="1" y="5">CPU <tspan>{{ cpuP }}%</tspan></text>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<rect
|
<rect
|
||||||
x="-2" y="-2"
|
x="-2" y="-2"
|
||||||
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
:width="viewBoxX + 4" :height="viewBoxY + 4"
|
||||||
:style="`stroke: none; fill: url(#${ memGradientId }); mask: url(#${ memMaskId })`"
|
:style="{ stroke: 'none', fill: `url(#${ memGradientId })`, mask: `url(#${ memMaskId })` }"
|
||||||
/>
|
/>
|
||||||
<text x="1" y="5">MEM <tspan>{{ memP }}%</tspan></text>
|
<text x="1" y="5">MEM <tspan>{{ memP }}%</tspan></text>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue