enhance(frontend): コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように ()

* enhance(frontend): コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように

* sessionStorageよりも更に短命な方法で持つように変更

* add comment

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
おさむのひと 2025-02-26 16:28:35 +09:00 committed by GitHub
parent 28b40691d5
commit 15b0345335
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 101 additions and 8 deletions
CHANGELOG.md
packages/frontend/src

@ -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) => {

@ -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',