enhance(frontend): コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように (#15438)
* enhance(frontend): コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように * sessionStorageよりも更に短命な方法で持つように変更 * add comment --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
parent
28b40691d5
commit
15b0345335
@ -13,6 +13,7 @@
|
|||||||
- Enhance: 開発者モードでメニューからファイルIDをコピー出来るように `#15441'
|
- Enhance: 開発者モードでメニューからファイルIDをコピー出来るように `#15441'
|
||||||
- Enhance: ノートに埋め込まれたメディアのコンテキストメニューから管理者用のファイル管理画面を開けるように ( #15440 )
|
- Enhance: ノートに埋め込まれたメディアのコンテキストメニューから管理者用のファイル管理画面を開けるように ( #15440 )
|
||||||
- Enhance: リアクションする際に確認ダイアログを表示できるように
|
- Enhance: リアクションする際に確認ダイアログを表示できるように
|
||||||
|
- Enhance: コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように `#15437`
|
||||||
- Enhance: CWの注釈で入力済みの文字数を表示
|
- Enhance: CWの注釈で入力済みの文字数を表示
|
||||||
- Fix: ノートページで、クリップ一覧が表示されないことがある問題を修正
|
- Fix: ノートページで、クリップ一覧が表示されないことがある問題を修正
|
||||||
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
|
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
|
||||||
|
@ -7,6 +7,7 @@ import { defineAsyncComponent, reactive, ref } from 'vue';
|
|||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { apiUrl } from '@@/js/config.js';
|
import { apiUrl } from '@@/js/config.js';
|
||||||
import type { MenuItem, MenuButton } from '@/types/menu.js';
|
import type { MenuItem, MenuButton } from '@/types/menu.js';
|
||||||
|
import { defaultMemoryStorage } from '@/memory-storage';
|
||||||
import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
|
import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
@ -40,6 +41,8 @@ export function incNotesCount() {
|
|||||||
export async function signout() {
|
export async function signout() {
|
||||||
if (!$i) return;
|
if (!$i) return;
|
||||||
|
|
||||||
|
defaultMemoryStorage.clear();
|
||||||
|
|
||||||
waiting();
|
waiting();
|
||||||
document.cookie.split(';').forEach((cookie) => {
|
document.cookie.split(';').forEach((cookie) => {
|
||||||
const cookieName = cookie.split('=')[0].trim();
|
const cookieName = cookie.split('=')[0].trim();
|
||||||
@ -107,7 +110,7 @@ export async function removeAccount(idOrToken: Account['id']) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Promise<Account> {
|
function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Promise<Account> {
|
||||||
document.cookie = "token=; path=/; max-age=0";
|
document.cookie = 'token=; path=/; max-age=0';
|
||||||
document.cookie = `token=${token}; path=/queue; max-age=86400; SameSite=Strict; Secure`; // bull dashboardの認証とかで使う
|
document.cookie = `token=${token}; path=/queue; max-age=86400; SameSite=Strict; Secure`; // bull dashboardの認証とかで使う
|
||||||
|
|
||||||
return new Promise((done, fail) => {
|
return new Promise((done, fail) => {
|
||||||
|
57
packages/frontend/src/memory-storage.ts
Normal file
57
packages/frontend/src/memory-storage.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type MemoryStorage = {
|
||||||
|
has: (key: string) => boolean;
|
||||||
|
getItem: <T>(key: string) => T | null;
|
||||||
|
setItem: (key: string, value: unknown) => void;
|
||||||
|
removeItem: (key: string) => void;
|
||||||
|
clear: () => void;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryStorageImpl implements MemoryStorage {
|
||||||
|
private readonly storage: Map<string, unknown>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.storage = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
has(key: string): boolean {
|
||||||
|
return this.storage.has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem<T>(key: string): T | null {
|
||||||
|
return this.storage.has(key) ? this.storage.get(key) as T : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(key: string, value: unknown): void {
|
||||||
|
this.storage.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItem(key: string): void {
|
||||||
|
this.storage.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.storage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return this.storage.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMemoryStorage(): MemoryStorage {
|
||||||
|
return new MemoryStorageImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SessionStorageよりも更に短い期間でクリアされるストレージです
|
||||||
|
* - ブラウザの再読み込みやタブの閉じると内容が揮発します
|
||||||
|
* - このストレージは他のタブと共有されません
|
||||||
|
* - アカウント切り替えやログアウトを行うと内容が揮発します
|
||||||
|
*/
|
||||||
|
export const defaultMemoryStorage: MemoryStorage = createMemoryStorage();
|
@ -9,6 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :contentMax="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
|
<div :class="$style.inputs">
|
||||||
|
<MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton>
|
||||||
|
</div>
|
||||||
<div :class="$style.inputs">
|
<div :class="$style.inputs">
|
||||||
<MkSelect v-model="sort" style="flex: 1;">
|
<MkSelect v-model="sort" style="flex: 1;">
|
||||||
<template #label>{{ i18n.ts.sort }}</template>
|
<template #label>{{ i18n.ts.sort }}</template>
|
||||||
@ -57,8 +60,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, shallowRef, ref } from 'vue';
|
import { computed, shallowRef, ref, watchEffect } from 'vue';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
|
import { defaultMemoryStorage } from '@/memory-storage';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
@ -69,13 +74,22 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
|||||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import { dateString } from '@/filters/date.js';
|
import { dateString } from '@/filters/date.js';
|
||||||
|
|
||||||
const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
type SearchQuery = {
|
||||||
|
sort?: string;
|
||||||
|
state?: string;
|
||||||
|
origin?: string;
|
||||||
|
username?: string;
|
||||||
|
hostname?: string;
|
||||||
|
};
|
||||||
|
|
||||||
const sort = ref('+createdAt');
|
const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
const state = ref('all');
|
const storedQuery = JSON.parse(defaultMemoryStorage.getItem('admin-users-query') ?? '{}') as SearchQuery;
|
||||||
const origin = ref('local');
|
|
||||||
const searchUsername = ref('');
|
const sort = ref(storedQuery.sort ?? '+createdAt');
|
||||||
const searchHost = ref('');
|
const state = ref(storedQuery.state ?? 'all');
|
||||||
|
const origin = ref(storedQuery.origin ?? 'local');
|
||||||
|
const searchUsername = ref(storedQuery.username ?? '');
|
||||||
|
const searchHost = ref(storedQuery.hostname ?? '');
|
||||||
const pagination = {
|
const pagination = {
|
||||||
endpoint: 'admin/show-users' as const,
|
endpoint: 'admin/show-users' as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@ -119,6 +133,14 @@ function show(user) {
|
|||||||
os.pageWindow(`/admin/user/${user.id}`);
|
os.pageWindow(`/admin/user/${user.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
sort.value = '+createdAt';
|
||||||
|
state.value = 'all';
|
||||||
|
origin.value = 'local';
|
||||||
|
searchUsername.value = '';
|
||||||
|
searchHost.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
const headerActions = computed(() => [{
|
const headerActions = computed(() => [{
|
||||||
icon: 'ti ti-search',
|
icon: 'ti ti-search',
|
||||||
text: i18n.ts.search,
|
text: i18n.ts.search,
|
||||||
@ -137,6 +159,16 @@ const headerActions = computed(() => [{
|
|||||||
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
defaultMemoryStorage.setItem('admin-users-query', JSON.stringify({
|
||||||
|
sort: sort.value,
|
||||||
|
state: state.value,
|
||||||
|
origin: origin.value,
|
||||||
|
username: searchUsername.value,
|
||||||
|
hostname: searchHost.value,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
definePageMetadata(() => ({
|
definePageMetadata(() => ({
|
||||||
title: i18n.ts.users,
|
title: i18n.ts.users,
|
||||||
icon: 'ti ti-users',
|
icon: 'ti ti-users',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user