From dcfab918e9885ffd533f12d7d62e06a5072baa5c Mon Sep 17 00:00:00 2001 From: BackRunner <dev@backrunner.top> Date: Sun, 17 Mar 2024 17:47:29 +0800 Subject: [PATCH 1/6] feat: send heartbeat right after visibility changed to 'visible' (#13581) --- packages/frontend/src/stream.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts index 0c5ee06197..0d5bd78b09 100644 --- a/packages/frontend/src/stream.ts +++ b/packages/frontend/src/stream.ts @@ -8,7 +8,12 @@ import { markRaw } from 'vue'; import { $i } from '@/account.js'; import { wsOrigin } from '@/config.js'; +// heart beat interval in ms +const HEART_BEAT_INTERVAL = 1000 * 60; + let stream: Misskey.Stream | null = null; +let timeoutHeartBeat: ReturnType<typeof setTimeout> | null = null; +let lastHeartbeatCall = 0; export function useStream(): Misskey.Stream { if (stream) return stream; @@ -17,7 +22,18 @@ export function useStream(): Misskey.Stream { token: $i.token, } : null)); - window.setTimeout(heartbeat, 1000 * 60); + if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat); + timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL); + + // send heartbeat right now when last send time is over HEART_BEAT_INTERVAL + document.addEventListener('visibilitychange', () => { + if ( + !stream + || document.visibilityState !== 'visible' + || Date.now() - lastHeartbeatCall < HEART_BEAT_INTERVAL + ) return; + heartbeat(); + }); return stream; } @@ -26,5 +42,7 @@ function heartbeat(): void { if (stream != null && document.visibilityState === 'visible') { stream.heartbeat(); } - window.setTimeout(heartbeat, 1000 * 60); + lastHeartbeatCall = Date.now(); + if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat); + timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL); } From b65203c9f852a29a3a6e7ce81c6761e9ac228bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:33:33 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix(frontend):=20WebGL2=E3=82=B3=E3=83=B3?= =?UTF-8?q?=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E3=81=AE=E5=88=9D=E6=9C=9F?= =?UTF-8?q?=E5=8C=96=E3=81=AB=E5=A4=B1=E6=95=97=E3=81=99=E3=82=8B=E3=81=A8?= =?UTF-8?q?Misskey=E3=81=8C=E8=B5=B7=E5=8B=95=E3=81=A7=E3=81=8D=E3=81=AA?= =?UTF-8?q?=E3=81=8F=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20(#13587)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed startup crash with seasonal effects (cherry picked from commit eba0c2cc61512db22109e2f15604eb65f5b7d2f2) * Update Changelog * Update Changelog --------- Co-authored-by: Leah <kevinlukej@gmail.com> --- CHANGELOG.md | 2 + packages/frontend/src/boot/main-boot.ts | 44 ++++++++++--------- .../frontend/src/scripts/snowfall-effect.ts | 4 +- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa56f1a268..cbd190d714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - Fix: 一部のページ内リンクが正しく動作しない問題を修正 - Fix: 周年の実績が閏年を考慮しない問題を修正 - Fix: ローカルURLのプレビューポップアップが左上に表示される +- Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正 + (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459) ### Server - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 8016e8b0e0..5cb19f388a 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -75,27 +75,31 @@ export async function mainBoot() { mainRouter.push('/search'); }, }; - - if (defaultStore.state.enableSeasonalScreenEffect) { - const month = new Date().getMonth() + 1; - if (defaultStore.state.hemisphere === 'S') { - // ▼南半球 - if (month === 7 || month === 8) { - const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; - new SnowfallEffect({}).render(); + try { + if (defaultStore.state.enableSeasonalScreenEffect) { + const month = new Date().getMonth() + 1; + if (defaultStore.state.hemisphere === 'S') { + // ▼南半球 + if (month === 7 || month === 8) { + const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + new SnowfallEffect({}).render(); + } + } else { + // ▼北半球 + if (month === 12 || month === 1) { + const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + new SnowfallEffect({}).render(); + } else if (month === 3 || month === 4) { + const SakuraEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + new SakuraEffect({ + sakura: true, + }).render(); + } } - } else { - // ▼北半球 - if (month === 12 || month === 1) { - const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; - new SnowfallEffect({}).render(); - } else if (month === 3 || month === 4) { - const SakuraEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; - new SakuraEffect({ - sakura: true, - }).render(); - } - } + } + } catch (error) { + // console.error(error); + console.error('Failed to initialise the seasonal screen effect canvas context:', error); } if ($i) { diff --git a/packages/frontend/src/scripts/snowfall-effect.ts b/packages/frontend/src/scripts/snowfall-effect.ts index 11fcaa0716..d88bdb6660 100644 --- a/packages/frontend/src/scripts/snowfall-effect.ts +++ b/packages/frontend/src/scripts/snowfall-effect.ts @@ -155,7 +155,9 @@ export class SnowfallEffect { max: 0.125, easing: 0.0005, }; - + /** + * @throws {Error} - Thrown when it fails to get WebGL context for the canvas + */ constructor(options: { sakura?: boolean; }) { From a38646bd0f732c3f71bf9e8174baa7d66f8eae9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 18 Mar 2024 14:20:28 +0900 Subject: [PATCH 3/6] =?UTF-8?q?fix(backend):=20=E3=83=95=E3=82=A9=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E4=BD=9C=E6=88=90=E3=81=99=E3=82=8B=E9=9A=9B=E3=81=AB=E6=97=A2?= =?UTF-8?q?=E5=AD=98=E3=81=AE=E3=82=82=E3=81=AE=E3=81=AF=E5=89=8A=E9=99=A4?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13588)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: delete old follow request (if exists) before creating new (cherry picked from commit ea948ccadc7eace1fcace176c9c070b2a9b46f56) * Update Changelog * Update Changelog --------- Co-authored-by: Kaity A <kaity@atikayda.au> --- CHANGELOG.md | 2 ++ packages/backend/src/core/UserFollowingService.ts | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd190d714..09f7bba18b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ ### Server - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに +- Fix: フォローリクエストを作成する際に既存のものは削除するように + (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440) ## 2024.3.1 diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 0a492c06e4..deeecdeb1f 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -511,6 +511,12 @@ export class UserFollowingService implements OnModuleInit { if (blocking) throw new Error('blocking'); if (blocked) throw new Error('blocked'); + // Remove old follow requests before creating a new one. + await this.followRequestsRepository.delete({ + followeeId: followee.id, + followerId: follower.id, + }); + const followRequest = await this.followRequestsRepository.insert({ id: this.idService.gen(), followerId: follower.id, From 067cdf3ce422f46535c3f70be91c3b55e03248ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:21:27 +0900 Subject: [PATCH 4/6] =?UTF-8?q?enhance(frontend):=20=E3=83=9A=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=81=AE=E3=83=87=E3=82=B6=E3=82=A4=E3=83=B3=E3=82=92?= =?UTF-8?q?=E8=AA=BF=E6=95=B4=20(#13590)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance(frontend): ページのデザインを調整 * 共有ボタンを直感的な導線に変更 * Update Changelog * Update packages/frontend/src/components/page/page.image.vue --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- CHANGELOG.md | 1 + .../src/components/page/page.image.vue | 24 +- .../src/components/page/page.note.vue | 13 +- .../src/components/page/page.text.vue | 8 +- .../frontend/src/components/page/page.vue | 2 +- .../page-editor/els/page-editor.el.note.vue | 2 +- packages/frontend/src/pages/page.vue | 410 ++++++++++++------ 7 files changed, 306 insertions(+), 154 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09f7bba18b..5d74090b35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように - Enhance: 設定>プラグインのページからプラグインの簡易的なログやエラーを見られるように - 実装の都合により、プラグインは1つエラーを起こした時に即時停止するようになりました +- Enhance: ページのデザインを変更 - Fix: 一部のページ内リンクが正しく動作しない問題を修正 - Fix: 周年の実績が閏年を考慮しない問題を修正 - Fix: ローカルURLのプレビューポップアップが左上に表示される diff --git a/packages/frontend/src/components/page/page.image.vue b/packages/frontend/src/components/page/page.image.vue index ced02943db..fc1ce9fc7b 100644 --- a/packages/frontend/src/components/page/page.image.vue +++ b/packages/frontend/src/components/page/page.image.vue @@ -4,19 +4,15 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MediaImage - v-if="image" - :image="image" - :disableImageLink="true" - /> +<div :class="$style.root"> + <MkMediaList v-if="image" :mediaList="[image]" :class="$style.mediaList"/> </div> </template> <script lang="ts" setup> import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import MediaImage from '@/components/MkMediaImage.vue'; +import MkMediaList from '@/components/MkMediaList.vue'; const props = defineProps<{ block: Misskey.entities.PageBlock, @@ -28,5 +24,17 @@ const image = ref<Misskey.entities.DriveFile | null>(null); onMounted(() => { image.value = props.page.attachedFiles.find(x => x.id === props.block.fileId) ?? null; }); - </script> + +<style lang="scss" module> +.root { + border: 1px solid var(--divider); + border-radius: var(--radius); + overflow: hidden; +} +.mediaList { + // MkMediaList 内の上部マージン 4px + margin-top: -4px; + height: calc(100% + 4px); +} +</style> diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue index 7b56494a6e..b5ba407806 100644 --- a/packages/frontend/src/components/page/page.note.vue +++ b/packages/frontend/src/components/page/page.note.vue @@ -4,9 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div style="margin: 1em 0;"> - <MkNote v-if="note && !block.detailed" :key="note.id + ':normal'" v-model:note="note"/> - <MkNoteDetailed v-if="note && block.detailed" :key="note.id + ':detail'" v-model:note="note"/> +<div :class="$style.root"> + <MkNote v-if="note && !block.detailed" :key="note.id + ':normal'" :note="note"/> + <MkNoteDetailed v-if="note && block.detailed" :key="note.id + ':detail'" :note="note"/> </div> </template> @@ -32,3 +32,10 @@ onMounted(() => { }); }); </script> + +<style lang="scss" module> +.root { + border: 1px solid var(--divider); + border-radius: var(--radius); +} +</style> diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue index 81a4c4fa93..61247b381f 100644 --- a/packages/frontend/src/components/page/page.text.vue +++ b/packages/frontend/src/components/page/page.text.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps"> +<div class="_gaps" :class="$style.textRoot"> <Mfm :text="block.text ?? ''" :isNote="false"/> <MkUrlPreview v-for="url in urls" :key="url" :url="url"/> </div> @@ -25,3 +25,9 @@ const props = defineProps<{ const urls = props.block.text ? extractUrlFromMfm(mfm.parse(props.block.text)) : []; </script> + +<style lang="scss" module> +.textRoot { + font-size: 1.1rem; +} +</style> diff --git a/packages/frontend/src/components/page/page.vue b/packages/frontend/src/components/page/page.vue index 53c70b01f4..a31c5eff28 100644 --- a/packages/frontend/src/components/page/page.vue +++ b/packages/frontend/src/components/page/page.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps_s"> +<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps"> <XBlock v-for="child in page.content" :key="child.id" :page="page" :block="child" :h="2"/> </div> </template> diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue index 194a276f89..0a28386986 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <XContainer :draggable="true" @remove="() => $emit('remove')"> <template #header><i class="ti ti-note"></i> {{ i18n.ts._pages.blocks.note }}</template> - <section style="padding: 0 16px 0 16px;"> + <section style="padding: 16px;" class="_gaps_s"> <MkInput v-model="id"> <template #label>{{ i18n.ts._pages.blocks._note.id }}</template> <template #caption>{{ i18n.ts._pages.blocks._note.idDescription }}</template> diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index bece32fc11..ab44533b81 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -6,48 +6,73 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="700"> - <Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in"> - <div v-if="page" :key="page.id" class="xcukqgmh"> - <div class="main"> - <!-- - <div class="header"> - <h1>{{ page.title }}</h1> - </div> - --> - <div class="banner"> - <MkMediaImage - v-if="page.eyeCatchingImageId" - :image="page.eyeCatchingImage" - :cover="true" - :disableImageLink="true" - class="thumbnail" - /> + <MkSpacer :contentMax="800"> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.fadeEnterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.fadeLeaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.fadeEnterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.fadeLeaveTo : ''" + > + <div v-if="page" :key="page.id" class="_gaps"> + <div :class="$style.pageMain"> + <div :class="$style.pageBanner"> + <div :class="$style.pageBannerBgRoot"> + <MkImgWithBlurhash + v-if="page.eyeCatchingImageId" + :class="$style.pageBannerBg" + :hash="page.eyeCatchingImage?.blurhash" + :cover="true" + :forceBlurhash="true" + /> + <img + v-else-if="instance.backgroundImageUrl || instance.bannerUrl" + :class="[$style.pageBannerBg, $style.pageBannerBgFallback1]" + :src="getStaticImageUrl(instance.backgroundImageUrl ?? instance.bannerUrl!)" + /> + <div v-else :class="[$style.pageBannerBg, $style.pageBannerBgFallback2]"></div> + </div> + <div v-if="page.eyeCatchingImageId" :class="$style.pageBannerImage"> + <MkMediaImage + :image="page.eyeCatchingImage!" + :cover="true" + :disableImageLink="true" + :class="$style.thumbnail" + /> + </div> + <div :class="$style.pageBannerTitle" class="_gaps_s"> + <h1>{{ page.title || page.name }}</h1> + <div v-if="page.user" :class="$style.pageBannerTitleUser"> + <MkAvatar :user="page.user" :class="$style.avatar" indicator link preview/> <MkA :to="`/@${username}`"><MkUserName :user="page.user" :nowrap="false"/></MkA> + </div> + </div> </div> - <div class="content"> + <div :class="$style.pageContent"> <XPage :page="page"/> </div> - <div class="actions"> - <div class="like"> + <div :class="$style.pageActions"> + <div> <MkButton v-if="page.isLiked" v-tooltip="i18n.ts._pages.unlike" class="button" asLike primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton> <MkButton v-else v-tooltip="i18n.ts._pages.like" class="button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton> </div> - <div class="other"> - <button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></button> - <button v-tooltip="i18n.ts.copyLink" v-click-anime class="_button" @click="copyLink"><i class="ti ti-link ti-fw"></i></button> - <button v-if="isSupportShare()" v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="ti ti-share ti-fw"></i></button> + <div :class="$style.other"> + <button v-tooltip="i18n.ts.copyLink" class="_button" :class="$style.generalActionButton" @click="copyLink"><i class="ti ti-link ti-fw"></i></button> + <button v-tooltip="i18n.ts.share" class="_button" :class="$style.generalActionButton" @click="share"><i class="ti ti-share ti-fw"></i></button> </div> </div> - <div class="user"> - <MkAvatar :user="page.user" class="avatar" link preview/> - <div class="name"> - <MkUserName :user="page.user" style="display: block;"/> - <MkAcct :user="page.user"/> - </div> - <MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> + <div :class="$style.pageUser"> + <MkAvatar :user="page.user" :class="$style.avatar" link preview/> + <MkA :to="`/@${username}`"> + <MkUserName :user="page.user" :class="$style.name"/> + <MkAcct :user="page.user" :class="$style.acct"/> + </MkA> + <MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user!" :inline="true" :transparent="false" :full="true" :class="$style.follow"/> </div> - <div class="links"> - <MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ i18n.ts._pages.viewSource }}</MkA> + <div :class="$style.pageDate"> + <div><i class="ti ti-clock"></i> {{ i18n.ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div> + <div v-if="page.createdAt != page.updatedAt"><i class="ti ti-clock-edit"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div> + </div> + <div :class="$style.pageLinks"> + <MkA v-if="!$i || $i.id !== page.userId" :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ i18n.ts._pages.viewSource }}</MkA> <template v-if="$i && $i.id === page.userId"> <MkA :to="`/pages/edit/${page.id}`" class="link">{{ i18n.ts._pages.editThisPage }}</MkA> <button v-if="$i.pinnedPageId === page.id" class="link _textButton" @click="pin(false)">{{ i18n.ts.unpin }}</button> @@ -55,10 +80,6 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </div> </div> - <div class="footer"> - <div><i class="ti ti-clock"></i> {{ i18n.ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div> - <div v-if="page.createdAt != page.updatedAt"><i class="ti ti-clock"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div> - </div> <MkAd :prefer="['horizontal', 'horizontal-big']"/> <MkContainer :max-height="300" :foldable="true" class="other"> <template #icon><i class="ti ti-clock"></i></template> @@ -84,6 +105,7 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { url } from '@/config.js'; import MkMediaImage from '@/components/MkMediaImage.vue'; +import MkImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; import MkContainer from '@/components/MkContainer.vue'; import MkPagination from '@/components/MkPagination.vue'; @@ -94,6 +116,8 @@ import { pageViewInterruptors, defaultStore } from '@/store.js'; import { deepClone } from '@/scripts/clone.js'; import { $i } from '@/account.js'; import { isSupportShare } from '@/scripts/navigator.js'; +import { instance } from '@/instance.js'; +import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; const props = defineProps<{ @@ -133,35 +157,63 @@ function fetchPage() { }); } -function share() { - navigator.share({ - title: page.value.title ?? page.value.name, - text: page.value.summary, - url: `${url}/@${page.value.user.username}/pages/${page.value.name}`, - }); +function share(ev: MouseEvent) { + if (!page.value) return; + + os.popupMenu([ + { + text: i18n.ts.shareWithNote, + icon: 'ti ti-pencil', + action: shareWithNote, + }, + ...(isSupportShare() ? [{ + text: i18n.ts.share, + icon: 'ti ti-share', + action: shareWithNavigator, + }] : []), + ], ev.currentTarget ?? ev.target); } function copyLink() { + if (!page.value) return; + copyToClipboard(`${url}/@${page.value.user.username}/pages/${page.value.name}`); os.success(); } function shareWithNote() { + if (!page.value) return; + os.post({ - initialText: `${page.value.title || page.value.name} ${url}/@${page.value.user.username}/pages/${page.value.name}`, + initialText: `${page.value.title || page.value.name}\n${url}/@${page.value.user.username}/pages/${page.value.name}`, + instant: true, + }); +} + +function shareWithNavigator() { + if (!page.value) return; + + navigator.share({ + title: page.value.title ?? page.value.name, + text: page.value.summary ?? undefined, + url: `${url}/@${page.value.user.username}/pages/${page.value.name}`, }); } function like() { + if (!page.value) return; + os.apiWithDialog('pages/like', { pageId: page.value.id, }).then(() => { - page.value.isLiked = true; - page.value.likedCount++; + page.value!.isLiked = true; + page.value!.likedCount++; }); } async function unlike() { + if (!page.value) return; + const confirm = await os.confirm({ type: 'warning', text: i18n.ts.unlikeConfirm, @@ -170,12 +222,14 @@ async function unlike() { os.apiWithDialog('pages/unlike', { pageId: page.value.id, }).then(() => { - page.value.isLiked = false; - page.value.likedCount--; + page.value!.isLiked = false; + page.value!.likedCount--; }); } function pin(pin) { + if (!page.value) return; + os.apiWithDialog('i/update', { pinnedPageId: pin ? page.value.id : null, }); @@ -200,109 +254,185 @@ definePageMetadata(() => ({ })); </script> -<style lang="scss" scoped> -.fade-enter-active, -.fade-leave-active { +<style lang="scss" module> +.fadeEnterActive, +.fadeLeaveActive { transition: opacity 0.125s ease; } -.fade-enter-from, -.fade-leave-to { +.fadeEnterFrom, +.fadeLeaveTo { opacity: 0; } -.xcukqgmh { - > .main { - padding: 32px; +.generalActionButton { + height: 2.5rem; + width: 2.5rem; + text-align: center; + border-radius: 99rem; - > .header { - padding: 16px; - - > h1 { - margin: 0; - } - } - - > .banner { - > .thumbnail { - // TODO: 良い感じのアスペクト比で表示 - display: block; - width: 100%; - height: auto; - aspect-ratio: 3/1; - border-radius: var(--radius); - overflow: hidden; - object-fit: cover; - } - } - - > .content { - margin-top: 16px; - padding: 16px 0 0 0; - } - - > .actions { - display: flex; - align-items: center; - margin-top: 16px; - padding: 16px 0 0 0; - border-top: solid 0.5px var(--divider); - - > .other { - margin-left: auto; - - > button { - padding: 8px; - margin: 0 8px; - - &:hover { - color: var(--fgHighlighted); - } - } - } - } - - > .user { - margin-top: 16px; - padding: 16px 0 0 0; - border-top: solid 0.5px var(--divider); - display: flex; - align-items: center; - - > .avatar { - width: 52px; - height: 52px; - } - - > .name { - margin: 0 0 0 12px; - font-size: 90%; - } - - > .koudoku { - margin-left: auto; - } - } - - > .links { - margin-top: 16px; - padding: 24px 0 0 0; - border-top: solid 0.5px var(--divider); - - > .link { - margin-right: 0.75em; - } - } + & :global(.ti) { + line-height: 2.5rem; } - > .footer { - margin: var(--margin) 0 var(--margin) 0; - font-size: 85%; - opacity: 0.75; + &:hover, + &:focus-visible { + background-color: var(--accentedBg); + color: var(--accent); + text-decoration: none; } } -</style> -<style module> +.pageMain { + border-radius: var(--radius); + padding: 2rem; + background: var(--panel); + box-sizing: border-box; +} + +.pageBanner { + width: calc(100% + 4rem); + margin: -2rem -2rem 1.5rem; + border-radius: var(--radius) var(--radius) 0 0; + overflow: hidden; + position: relative; + + > .pageBannerBgRoot { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + + .pageBannerBg { + width: 100%; + height: 100%; + object-fit: cover; + opacity: .2; + filter: brightness(1.2); + } + + .pageBannerBgFallback1 { + filter: blur(20px); + } + + .pageBannerBgFallback2 { + background-color: var(--accentedBg); + } + + &::after { + content: ''; + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: 100px; + background: linear-gradient(0deg, var(--panel), transparent); + } + } + + > .pageBannerImage { + position: relative; + padding-top: 56.25%; + + > .thumbnail { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + } + + > .pageBannerTitle { + position: relative; + padding: 1.5rem 2rem; + + h1 { + font-size: 2rem; + font-weight: 700; + color: var(--fg); + margin: 0; + } + + .pageBannerTitleUser { + --height: 32px; + + .avatar { + height: var(--height); + width: var(--height); + } + + line-height: var(--height); + } + } +} + +.pageContent { + margin-bottom: 1.5rem; +} + +.pageActions { + display: flex; + align-items: center; + + border-top: 1px solid var(--divider); + padding-top: 1.5rem; + margin-bottom: 1.5rem; + + > .other { + margin-left: auto; + display: flex; + gap: var(--marginHalf); + } +} + +.pageUser { + display: flex; + align-items: center; + + border-top: 1px solid var(--divider); + padding-top: 1.5rem; + margin-bottom: 1.5rem; + + .avatar, + .name, + .acct { + display: block; + } + + .avatar { + width: 4rem; + height: 4rem; + margin-right: 1rem; + } + + .name { + font-size: 110%; + font-weight: 700; + } + + .acct { + font-size: 90%; + opacity: 0.7; + } + + .follow { + margin-left: auto; + } +} + +.pageDate { + margin-bottom: 1.5rem; +} + +.pageLinks { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: var(--marginHalf); +} + .relatedPagesRoot { padding: var(--margin); } From 0226a670ddb0a38dfd8b8f479885ee5e83cf970f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:34:31 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix(backend):=20=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E3=82=84=E3=83=8E=E3=83=BC=E3=83=88=E3=81=AEOGP?= =?UTF-8?q?=E3=81=A7=E3=83=AD=E3=83=BC=E3=82=AB=E3=83=AB=E3=81=A8=E3=83=AA?= =?UTF-8?q?=E3=83=A2=E3=83=BC=E3=83=88=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?= =?UTF-8?q?=E3=81=AE=E8=A6=8B=E5=88=86=E3=81=91=E3=81=8C=E4=BB=98=E3=81=8B?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20(#13586)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance(OGP): ユーザーやノートのOGPでローカルとリモートユーザーの見分けが付かない問題を修正 (MisskeyIO#528) (cherry picked from commit 0c3de462d99c47297bebc162581bac6f78f21b49) * Update Changelog --------- Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> --- CHANGELOG.md | 1 + packages/backend/src/server/web/views/note.pug | 4 ++-- packages/backend/src/server/web/views/user.pug | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d74090b35..91539a6a9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Fix: ローカルURLのプレビューポップアップが左上に表示される - Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正 (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459) +- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正 ### Server - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに diff --git a/packages/backend/src/server/web/views/note.pug b/packages/backend/src/server/web/views/note.pug index 9bc652b6a1..fb659ce171 100644 --- a/packages/backend/src/server/web/views/note.pug +++ b/packages/backend/src/server/web/views/note.pug @@ -2,7 +2,7 @@ extends ./base block vars - const user = note.user; - - const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`; + - const title = user.name ? `${user.name} (@${user.username}${user.host ? `@${user.host}` : ''})` : `@${user.username}${user.host ? `@${user.host}` : ''}`; - const url = `${config.url}/notes/${note.id}`; - const isRenote = note.renote && note.text == null && note.fileIds.length == 0 && note.poll == null; - const images = (note.files || []).filter(file => file.type.startsWith('image/') && !file.isSensitive) @@ -28,7 +28,7 @@ block og // FIXME: add embed player for Twitter if images.length meta(property='twitter:card' content='summary_large_image') - each image in images + each image in images meta(property='og:image' content= image.url) else meta(property='twitter:card' content='summary') diff --git a/packages/backend/src/server/web/views/user.pug b/packages/backend/src/server/web/views/user.pug index 83d57349a6..2b0a7bab5c 100644 --- a/packages/backend/src/server/web/views/user.pug +++ b/packages/backend/src/server/web/views/user.pug @@ -1,7 +1,7 @@ extends ./base block vars - - const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`; + - const title = user.name ? `${user.name} (@${user.username}${user.host ? `@${user.host}` : ''})` : `@${user.username}${user.host ? `@${user.host}` : ''}`; - const url = `${config.url}/@${(user.host ? `${user.username}@${user.host}` : user.username)}`; block title From 5f6863b77e9e955b2e82b9f44a63df4c1eb6e4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:04:20 +0900 Subject: [PATCH 6/6] Add missing credit (for #13586) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91539a6a9e..d948127d06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ - Fix: ローカルURLのプレビューポップアップが左上に表示される - Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正 (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459) -- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正 +- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正 + (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/528) ### Server - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに