mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-27 12:30:19 +01:00
Merge branch 'develop' into feat-12997
This commit is contained in:
commit
3c592d6db5
10 changed files with 54 additions and 23 deletions
|
@ -6,11 +6,14 @@
|
||||||
### Client
|
### Client
|
||||||
- Feat: チュートリアルと初期設定ダイアログを統合
|
- Feat: チュートリアルと初期設定ダイアログを統合
|
||||||
- 管理者は新規登録したユーザーにチュートリアルを強制することができるように
|
- 管理者は新規登録したユーザーにチュートリアルを強制することができるように
|
||||||
|
- 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 )
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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を使う場合は無名関数でラップして使用する
|
||||||
|
|
Loading…
Reference in a new issue