enhance(frontend): コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように (#15438)
* enhance(frontend): コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように * sessionStorageよりも更に短命な方法で持つように変更 * add comment --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
		@@ -13,6 +13,7 @@
 | 
			
		||||
- Enhance: 開発者モードでメニューからファイルIDをコピー出来るように `#15441'
 | 
			
		||||
- Enhance: ノートに埋め込まれたメディアのコンテキストメニューから管理者用のファイル管理画面を開けるように ( #15440 )
 | 
			
		||||
- Enhance: リアクションする際に確認ダイアログを表示できるように
 | 
			
		||||
- Enhance: コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように `#15437`
 | 
			
		||||
- Enhance: CWの注釈で入力済みの文字数を表示
 | 
			
		||||
- Fix: ノートページで、クリップ一覧が表示されないことがある問題を修正
 | 
			
		||||
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import { defineAsyncComponent, reactive, ref } from 'vue';
 | 
			
		||||
import * as Misskey from 'misskey-js';
 | 
			
		||||
import { apiUrl } from '@@/js/config.js';
 | 
			
		||||
import type { MenuItem, MenuButton } from '@/types/menu.js';
 | 
			
		||||
import { defaultMemoryStorage } from '@/memory-storage';
 | 
			
		||||
import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
 | 
			
		||||
import { i18n } from '@/i18n.js';
 | 
			
		||||
import { miLocalStorage } from '@/local-storage.js';
 | 
			
		||||
@@ -40,6 +41,8 @@ export function incNotesCount() {
 | 
			
		||||
export async function signout() {
 | 
			
		||||
	if (!$i) return;
 | 
			
		||||
 | 
			
		||||
	defaultMemoryStorage.clear();
 | 
			
		||||
 | 
			
		||||
	waiting();
 | 
			
		||||
	document.cookie.split(';').forEach((cookie) => {
 | 
			
		||||
		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> {
 | 
			
		||||
	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の認証とかで使う
 | 
			
		||||
 | 
			
		||||
	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>
 | 
			
		||||
		<MkSpacer :contentMax="900">
 | 
			
		||||
			<div class="_gaps">
 | 
			
		||||
				<div :class="$style.inputs">
 | 
			
		||||
					<MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div :class="$style.inputs">
 | 
			
		||||
					<MkSelect v-model="sort" style="flex: 1;">
 | 
			
		||||
						<template #label>{{ i18n.ts.sort }}</template>
 | 
			
		||||
@@ -57,8 +60,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed, shallowRef, ref } from 'vue';
 | 
			
		||||
import { computed, shallowRef, ref, watchEffect } from 'vue';
 | 
			
		||||
import XHeader from './_header_.vue';
 | 
			
		||||
import { defaultMemoryStorage } from '@/memory-storage';
 | 
			
		||||
import MkButton from '@/components/MkButton.vue';
 | 
			
		||||
import MkInput from '@/components/MkInput.vue';
 | 
			
		||||
import MkSelect from '@/components/MkSelect.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 { 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 state = ref('all');
 | 
			
		||||
const origin = ref('local');
 | 
			
		||||
const searchUsername = ref('');
 | 
			
		||||
const searchHost = ref('');
 | 
			
		||||
const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
 | 
			
		||||
const storedQuery = JSON.parse(defaultMemoryStorage.getItem('admin-users-query') ?? '{}') as SearchQuery;
 | 
			
		||||
 | 
			
		||||
const sort = ref(storedQuery.sort ?? '+createdAt');
 | 
			
		||||
const state = ref(storedQuery.state ?? 'all');
 | 
			
		||||
const origin = ref(storedQuery.origin ?? 'local');
 | 
			
		||||
const searchUsername = ref(storedQuery.username ?? '');
 | 
			
		||||
const searchHost = ref(storedQuery.hostname ?? '');
 | 
			
		||||
const pagination = {
 | 
			
		||||
	endpoint: 'admin/show-users' as const,
 | 
			
		||||
	limit: 10,
 | 
			
		||||
@@ -119,6 +133,14 @@ function show(user) {
 | 
			
		||||
	os.pageWindow(`/admin/user/${user.id}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function resetQuery() {
 | 
			
		||||
	sort.value = '+createdAt';
 | 
			
		||||
	state.value = 'all';
 | 
			
		||||
	origin.value = 'local';
 | 
			
		||||
	searchUsername.value = '';
 | 
			
		||||
	searchHost.value = '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const headerActions = computed(() => [{
 | 
			
		||||
	icon: 'ti ti-search',
 | 
			
		||||
	text: i18n.ts.search,
 | 
			
		||||
@@ -137,6 +159,16 @@ const headerActions = 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(() => ({
 | 
			
		||||
	title: i18n.ts.users,
 | 
			
		||||
	icon: 'ti ti-users',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user