enhance(frontend): X (formally known as Twitter) (MisskeyIO#208)

* enhance(frontend): TwitterアイコンをXに変更 (misskey-dev#11436)
cheery-picked from 007ed5c929
Co-authored-by: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com>

* fix(frontend/MkUrlPreview): allow fullscreen from tweets (misskey-dev#11712)
cheery-picked from 2896fc6cb4
Co-authored-by: Kagami Sascha Rosylight <saschanaz@outlook.com>

* enhance(frontend): x.comでも展開ができるように (misskey-dev#11757)
cheery-picked from cb80dff7df
Co-authored-by: maguroshouta <54607611+maguroshouta@users.noreply.github.com>
This commit is contained in:
まっちゃとーにゅ 2023-11-06 04:59:48 +09:00 committed by GitHub
parent 6a06711ffd
commit ae9245abdc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 12 deletions

View file

@ -23,7 +23,7 @@
"@rollup/plugin-typescript": "^11.1.2", "@rollup/plugin-typescript": "^11.1.2",
"@rollup/pluginutils": "5.0.2", "@rollup/pluginutils": "5.0.2",
"@syuilo/aiscript": "0.15.0", "@syuilo/aiscript": "0.15.0",
"@tabler/icons-webfont": "2.25.0", "@tabler/icons-webfont": "2.30.0",
"@vitejs/plugin-vue": "4.2.3", "@vitejs/plugin-vue": "4.2.3",
"@vue-macros/reactivity-transform": "0.3.15", "@vue-macros/reactivity-transform": "0.3.15",
"@vue/compiler-sfc": "3.3.4", "@vue/compiler-sfc": "3.3.4",

View file

@ -28,7 +28,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<template v-else-if="tweetId && tweetExpanded"> <template v-else-if="tweetId && tweetExpanded">
<div ref="twitter"> <div ref="twitter">
<iframe ref="tweet" scrolling="no" frameborder="no" :style="{ position: 'relative', width: '100%', height: `${tweetHeight}px` }" :src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&amp;hideCard=false&amp;hideThread=false&amp;lang=en&amp;theme=${defaultStore.state.darkMode ? 'dark' : 'light'}&amp;id=${tweetId}`"></iframe> <iframe
ref="tweet"
allow="fullscreen;web-share"
sandbox="allow-popups allow-scripts allow-same-origin"
scrolling="no"
:style="{ position: 'relative', width: '100%', height: `${tweetHeight}px`, border: 0 }"
:src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&amp;hideCard=false&amp;hideThread=false&amp;lang=en&amp;theme=${defaultStore.state.darkMode ? 'dark' : 'light'}&amp;id=${tweetId}`"
></iframe>
</div> </div>
<div :class="$style.action"> <div :class="$style.action">
<MkButton :small="true" inline @click="tweetExpanded = false"> <MkButton :small="true" inline @click="tweetExpanded = false">
@ -60,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="showActions"> <template v-if="showActions">
<div v-if="tweetId" :class="$style.action"> <div v-if="tweetId" :class="$style.action">
<MkButton :small="true" inline @click="tweetExpanded = true"> <MkButton :small="true" inline @click="tweetExpanded = true">
<i class="ti ti-brand-twitter"></i> {{ i18n.ts.expandTweet }} <i class="ti ti-brand-x"></i> {{ i18n.ts.expandTweet }}
</MkButton> </MkButton>
</div> </div>
<div v-if="!playerEnabled && player.url" :class="$style.action"> <div v-if="!playerEnabled && player.url" :class="$style.action">
@ -126,7 +133,7 @@ let unknownUrl = $ref(false);
const requestUrl = new URL(props.url); const requestUrl = new URL(props.url);
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com') { if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com' || requestUrl.hostname === 'x.com' || requestUrl.hostname === 'mobile.x.com') {
const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/); const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
if (m) tweetId = m[1]; if (m) tweetId = m[1];
} }

View file

@ -13,7 +13,7 @@ import MkUrlPreview from '@/components/MkUrlPreview.vue';
type SummalyResult = Awaited<ReturnType<typeof summaly>>; type SummalyResult = Awaited<ReturnType<typeof summaly>>;
describe('MkMediaImage', () => { describe('MkUrlPreview', () => {
const renderPreviewBy = async (summary: Partial<SummalyResult>): Promise<RenderResult> => { const renderPreviewBy = async (summary: Partial<SummalyResult>): Promise<RenderResult> => {
if (!summary.player) { if (!summary.player) {
summary.player = { summary.player = {
@ -143,4 +143,22 @@ describe('MkMediaImage', () => {
assert.exists(iframe, 'iframe should exist'); assert.exists(iframe, 'iframe should exist');
assert.strictEqual(iframe?.parentElement?.style.paddingTop, '200px'); assert.strictEqual(iframe?.parentElement?.style.paddingTop, '200px');
}); });
test('Loading a tweet in iframe', async () => {
const iframe = await renderAndOpenPreview({
url: 'https://twitter.com/i/web/status/1685072521782325249',
});
assert.exists(iframe, 'iframe should exist');
assert.strictEqual(iframe?.getAttribute('allow'), 'fullscreen;web-share');
assert.strictEqual(iframe?.getAttribute('sandbox'), 'allow-popups allow-scripts allow-same-origin');
});
test('Loading a post in iframe', async () => {
const iframe = await renderAndOpenPreview({
url: 'https://x.com/i/web/status/1685072521782325249',
});
assert.exists(iframe, 'iframe should exist');
assert.strictEqual(iframe?.getAttribute('allow'), 'fullscreen;web-share');
assert.strictEqual(iframe?.getAttribute('sandbox'), 'allow-popups allow-scripts allow-same-origin');
});
}); });

View file

@ -638,8 +638,8 @@ importers:
specifier: 0.15.0 specifier: 0.15.0
version: 0.15.0 version: 0.15.0
'@tabler/icons-webfont': '@tabler/icons-webfont':
specifier: 2.25.0 specifier: 2.30.0
version: 2.25.0 version: 2.30.0
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: 4.2.3 specifier: 4.2.3
version: 4.2.3(vite@4.4.4)(vue@3.3.4) version: 4.2.3(vite@4.4.4)(vue@3.3.4)
@ -8394,14 +8394,14 @@ packages:
dependencies: dependencies:
defer-to-connect: 2.0.1 defer-to-connect: 2.0.1
/@tabler/icons-webfont@2.25.0: /@tabler/icons-webfont@2.30.0:
resolution: {integrity: sha512-IWYVnYlCwlGC95kvpY5Hdiyn1/amXOUwsfRthtmgEtHCQly4JSLRuaD90xD0O+pQ+wZBXIVNsO3pKdg74zEaBg==} resolution: {integrity: sha512-tGGKxeATvyHJBHl5FzY4oAShbAiR4ovstG62lqb2HGlOJwz4Io9TSk4eoB88nqxg3sT5no2YsAKXcr1UnlpnNQ==}
dependencies: dependencies:
'@tabler/icons': 2.25.0 '@tabler/icons': 2.30.0
dev: false dev: false
/@tabler/icons@2.25.0: /@tabler/icons@2.30.0:
resolution: {integrity: sha512-Z+FtSZoG/CM1TMCgg7elUew2m0+qMdh5gutMhvxiIY77KIIsE6L/6fUBy+rPXWE9v7MV296fsnCvbpfgwpXupQ==} resolution: {integrity: sha512-tvtmkI4ALjKThVVORh++sB9JnkFY7eGInKxNy+Df7WVQiF7T85tlvGADzlgX4Ic+CK5MIUzZ0jhOlQ/RRlgXpg==}
dev: false dev: false
/@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0): /@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0):