Compare commits

...

8 Commits

Author SHA1 Message Date
github-actions[bot]
e7cacee93c
Merge 0804092426 into e8518de054 2024-12-16 00:03:49 +00:00
かっこかり
0804092426
fix(frontend): serverContextの型エラーを修正 (#15131)
Some checks failed
Lint / lint (misskey-js) (push) Has been cancelled
API report (misskey.js) / report (push) Has been cancelled
Check SPDX-License-Identifier / check-spdx-license-id (push) Has been cancelled
Check copyright year / check_copyright_year (push) Has been cancelled
Publish Docker image (develop) / Build (linux/amd64) (push) Has been cancelled
Publish Docker image (develop) / Build (linux/arm64) (push) Has been cancelled
Publish Docker image (develop) / merge (push) Has been cancelled
Lint / lint (misskey-reversi) (push) Has been cancelled
Dockle / dockle (push) Has been cancelled
Lint / pnpm_install (push) Has been cancelled
Lint / lint (backend) (push) Has been cancelled
Lint / lint (frontend) (push) Has been cancelled
Lint / lint (frontend-embed) (push) Has been cancelled
Lint / lint (frontend-shared) (push) Has been cancelled
Lint / lint (misskey-bubble-game) (push) Has been cancelled
Lint / lint (sw) (push) Has been cancelled
Lint / typecheck (backend) (push) Has been cancelled
Lint / typecheck (misskey-js) (push) Has been cancelled
Lint / typecheck (sw) (push) Has been cancelled
Release Manager: sync changelog with PR / edit (push) Has been cancelled
Storybook / build (push) Has been cancelled
Test (backend) / unit (22.11.0) (push) Has been cancelled
Test (backend) / e2e (22.11.0) (push) Has been cancelled
Test (federation) / test (22.11.0) (push) Has been cancelled
Test (frontend) / vitest (22.11.0) (push) Has been cancelled
Test (frontend) / e2e (chrome, 22.11.0) (push) Has been cancelled
Test (misskey.js) / test (22.11.0) (push) Has been cancelled
Test (production install and build) / production (22.11.0) (push) Has been cancelled
* fix(frontend): serverContextの型エラーを修正

* add comment
2024-12-16 09:03:46 +09:00
かっこかり
3e0fcaeca8
fix(frontend): 絵文字管理画面で絵文字が表示されないことがある問題を修正 (#15128)
* fix(frontend): 絵文字管理画面で絵文字が表示されないことがある問題を修正

* Update Changelog

* optimize
2024-12-16 09:02:38 +09:00
かっこかり
5a2b29a3b4
enhance(frontend): PC画面でチャンネルが複数列で表示されるように (#15129)
* チャンネル一覧の列を最大3列にした (Otaku-Social#13)

* fix

* fix

* fix

* 🎨

* fix

* 🎨

* Update Changelog

* Update Changelog

* 要らない_marginを消す

---------

Co-authored-by: tmorio <morikapusan@morikapu-denki.com>
2024-12-16 08:57:37 +09:00
FineArchs
234d91a884
misskey-js: APIClientにURL末尾の/を除去する処理を追加 (#15132) 2024-12-16 08:55:34 +09:00
misskey-release-bot[bot]
e8518de054
Merge pull request #14924 from misskey-dev/develop
Release: 2024.11.0
2024-11-22 09:15:34 +00:00
misskey-release-bot[bot]
b99e13e667
Merge pull request #14741 from misskey-dev/develop
Release: 2024.10.1
2024-10-15 04:53:46 +00:00
misskey-release-bot[bot]
2518cf36d0
Merge pull request #14675 from misskey-dev/develop
Release: 2024.10.0
2024-10-09 05:17:29 +00:00
10 changed files with 54 additions and 23 deletions

View File

@ -4,11 +4,14 @@
- -
### Client ### Client
- Enhance: PC画面でチャンネルが複数列で表示されるように
(Cherry-picked from https://github.com/Otaku-Social/maniakey/pull/13)
- Fix: 画面サイズが変わった際にナビゲーションバーが自動で折りたたまれない問題を修正 - Fix: 画面サイズが変わった際にナビゲーションバーが自動で折りたたまれない問題を修正
- Fix: サーバー情報メニューに区切り線が不足していたのを修正 - Fix: サーバー情報メニューに区切り線が不足していたのを修正
- Fix: ノートがログインしているユーザーしか見れない場合にログインダイアログを閉じるとその後の動線がなくなる問題を修正 - Fix: ノートがログインしているユーザーしか見れない場合にログインダイアログを閉じるとその後の動線がなくなる問題を修正
- Fix: 公開範囲がホームのノートの埋め込みウィジェットが読み込まれない問題を修正 - Fix: 公開範囲がホームのノートの埋め込みウィジェットが読み込まれない問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/803) (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/803)
- Fix: 絵文字管理画面で一部の絵文字が表示されない問題を修正
### Server ### Server
- Fix: ユーザーのプロフィール画面をアドレス入力などで直接表示した際に概要タブの描画に失敗する問題の修正( #15032 ) - Fix: ユーザーのプロフィール画面をアドレス入力などで直接表示した際に概要タブの描画に失敗する問題の修正( #15032 )

View File

@ -125,7 +125,9 @@ const bannerStyle = computed(() => {
position: absolute; position: absolute;
top: 16px; top: 16px;
left: 16px; left: 16px;
max-width: calc(100% - 32px);
padding: 12px 16px; padding: 12px 16px;
box-sizing: border-box;
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.7);
color: #fff; color: #fff;
font-size: 1.2em; font-size: 1.2em;

View File

@ -6,9 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="700"> <MkSpacer :contentMax="1200">
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
<div v-if="tab === 'search'" key="search"> <div v-if="tab === 'search'" key="search" :class="$style.searchRoot">
<div class="_gaps"> <div class="_gaps">
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search"> <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
<template #prefix><i class="ti ti-search"></i></template> <template #prefix><i class="ti ti-search"></i></template>
@ -27,23 +27,31 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
<div v-if="tab === 'featured'" key="featured"> <div v-if="tab === 'featured'" key="featured">
<MkPagination v-slot="{items}" :pagination="featuredPagination"> <MkPagination v-slot="{items}" :pagination="featuredPagination">
<MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> <div :class="$style.root">
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
</div>
</MkPagination> </MkPagination>
</div> </div>
<div v-else-if="tab === 'favorites'" key="favorites"> <div v-else-if="tab === 'favorites'" key="favorites">
<MkPagination v-slot="{items}" :pagination="favoritesPagination"> <MkPagination v-slot="{items}" :pagination="favoritesPagination">
<MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> <div :class="$style.root">
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
</div>
</MkPagination> </MkPagination>
</div> </div>
<div v-else-if="tab === 'following'" key="following"> <div v-else-if="tab === 'following'" key="following">
<MkPagination v-slot="{items}" :pagination="followingPagination"> <MkPagination v-slot="{items}" :pagination="followingPagination">
<MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> <div :class="$style.root">
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
</div>
</MkPagination> </MkPagination>
</div> </div>
<div v-else-if="tab === 'owned'" key="owned"> <div v-else-if="tab === 'owned'" key="owned">
<MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton>
<MkPagination v-slot="{items}" :pagination="ownedPagination"> <MkPagination v-slot="{items}" :pagination="ownedPagination">
<MkChannelPreview v-for="channel in items" :key="channel.id" class="_margin" :channel="channel"/> <div :class="$style.root">
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
</div>
</MkPagination> </MkPagination>
</div> </div>
</MkHorizontalSwipe> </MkHorizontalSwipe>
@ -85,6 +93,7 @@ onMounted(() => {
const featuredPagination = { const featuredPagination = {
endpoint: 'channels/featured' as const, endpoint: 'channels/featured' as const,
limit: 10,
noPaging: true, noPaging: true,
}; };
const favoritesPagination = { const favoritesPagination = {
@ -157,3 +166,17 @@ definePageMetadata(() => ({
icon: 'ti ti-device-tv', icon: 'ti ti-device-tv',
})); }));
</script> </script>
<style lang="scss" module>
.searchRoot {
width: 100%;
max-width: 700px;
margin: 0 auto;
}
.root {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
gap: var(--MI-margin);
}
</style>

View File

@ -46,9 +46,10 @@ import { clipsCache } from '@/cache.js';
import { isSupportShare } from '@/scripts/navigator.js'; import { isSupportShare } from '@/scripts/navigator.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { genEmbedCode } from '@/scripts/get-embed-code.js'; import { genEmbedCode } from '@/scripts/get-embed-code.js';
import { getServerContext } from '@/server-context.js'; import { assertServerContext, serverContext } from '@/server-context.js';
const CTX_CLIP = getServerContext('clip'); // context
const CTX_CLIP = $i && assertServerContext(serverContext, 'clip') ? serverContext.clip : null;
const props = defineProps<{ const props = defineProps<{
clipId: string, clipId: string,

View File

@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #default="{items}"> <template #default="{items}">
<div class="ldhfsamy"> <div class="ldhfsamy">
<button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> <button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
<img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/> <img :src="emoji.url" class="img" :alt="emoji.name"/>
<div class="body"> <div class="body">
<div class="name _monospace">{{ emoji.name }}</div> <div class="name _monospace">{{ emoji.name }}</div>
<div class="info">{{ emoji.category }}</div> <div class="info">{{ emoji.category }}</div>
@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #default="{items}"> <template #default="{items}">
<div class="ldhfsamy"> <div class="ldhfsamy">
<div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)">
<img :src="`/emoji/${emoji.name}@${emoji.host}.webp`" class="img" :alt="emoji.name"/> <img :src="getProxiedImageUrl(emoji.url, 'emoji')" class="img" :alt="emoji.name"/>
<div class="body"> <div class="body">
<div class="name _monospace">{{ emoji.name }}</div> <div class="name _monospace">{{ emoji.name }}</div>
<div class="info">{{ emoji.host }}</div> <div class="info">{{ emoji.host }}</div>
@ -83,6 +83,7 @@ import FormSplit from '@/components/form/split.vue';
import { selectFile } from '@/scripts/select-file.js'; import { selectFile } from '@/scripts/select-file.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/scripts/misskey-api.js';
import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';

View File

@ -118,7 +118,7 @@ watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => misskeyApi('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => misskeyApi('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
}, { immediate: true }); }, { immediate: true });
const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? props.emoji.url : null);
async function changeImage(ev: Event) { async function changeImage(ev: Event) {
file.value = await selectFile(ev.currentTarget ?? ev.target, null); file.value = await selectFile(ev.currentTarget ?? ev.target, null);

View File

@ -63,9 +63,11 @@ import { dateString } from '@/filters/date.js';
import MkClipPreview from '@/components/MkClipPreview.vue'; import MkClipPreview from '@/components/MkClipPreview.vue';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
import { pleaseLogin } from '@/scripts/please-login.js'; import { pleaseLogin } from '@/scripts/please-login.js';
import { getServerContext } from '@/server-context.js'; import { serverContext, assertServerContext } from '@/server-context.js';
import { $i } from '@/account.js';
const CTX_NOTE = getServerContext('note'); // context
const CTX_NOTE = $i && assertServerContext(serverContext, 'note') ? serverContext.note : null;
const props = defineProps<{ const props = defineProps<{
noteId: string; noteId: string;

View File

@ -39,7 +39,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { $i } from '@/account.js'; import { $i } from '@/account.js';
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
import { getServerContext } from '@/server-context.js'; import { serverContext, assertServerContext } from '@/server-context.js';
const XHome = defineAsyncComponent(() => import('./home.vue')); const XHome = defineAsyncComponent(() => import('./home.vue'));
const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue')); const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue'));
@ -53,7 +53,8 @@ const XFlashs = defineAsyncComponent(() => import('./flashs.vue'));
const XGallery = defineAsyncComponent(() => import('./gallery.vue')); const XGallery = defineAsyncComponent(() => import('./gallery.vue'));
const XRaw = defineAsyncComponent(() => import('./raw.vue')); const XRaw = defineAsyncComponent(() => import('./raw.vue'));
const CTX_USER = getServerContext('user'); // context
const CTX_USER = $i && assertServerContext(serverContext, 'user') ? serverContext.user : null;
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
acct: string; acct: string;

View File

@ -2,22 +2,20 @@
* SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { $i } from '@/account.js';
const providedContextEl = document.getElementById('misskey_clientCtx'); const providedContextEl = document.getElementById('misskey_clientCtx');
export type ServerContext = { export type ServerContext = {
clip?: Misskey.entities.Clip; clip?: Misskey.entities.Clip;
note?: Misskey.entities.Note; note?: Misskey.entities.Note;
user?: Misskey.entities.UserLite; user?: Misskey.entities.UserDetailed;
} | null; } | null;
export const serverContext: ServerContext = (providedContextEl && providedContextEl.textContent) ? JSON.parse(providedContextEl.textContent) : null; export const serverContext: ServerContext = (providedContextEl && providedContextEl.textContent) ? JSON.parse(providedContextEl.textContent) : null;
export function getServerContext<K extends keyof NonNullable<ServerContext>>(entity: K): Required<Pick<NonNullable<ServerContext>, K>> | null { export function assertServerContext<K extends keyof NonNullable<ServerContext>>(ctx: ServerContext, entity: K): ctx is Required<Pick<NonNullable<ServerContext>, K>> {
// contextは非ログイン状態の情報しかないためログイン時は利用できない if (ctx == null) return false;
if ($i) return null; return entity in ctx && ctx[entity] != null;
return serverContext ? (serverContext[entity] ?? null) : null;
} }

View File

@ -44,7 +44,7 @@ export class APIClient {
credential?: APIClient['credential']; credential?: APIClient['credential'];
fetch?: APIClient['fetch'] | null | undefined; fetch?: APIClient['fetch'] | null | undefined;
}) { }) {
this.origin = opts.origin; this.origin = opts.origin.replace(/\/$/, '');
this.credential = opts.credential; this.credential = opts.credential;
// ネイティブ関数をそのまま変数に代入して使おうとするとChromiumではIllegal invocationエラーが発生するため、 // ネイティブ関数をそのまま変数に代入して使おうとするとChromiumではIllegal invocationエラーが発生するため、
// 環境で実装されているfetchを使う場合は無名関数でラップして使用する // 環境で実装されているfetchを使う場合は無名関数でラップして使用する