Compare commits
12 commits
dependabot
...
develop
Author | SHA1 | Date | |
---|---|---|---|
Mizah | a858ee31c6 | ||
Mizah | 0bd7ed8191 | ||
Mizah | ee574ae154 | ||
Mizah | a505f36252 | ||
Mizah | a516383c66 | ||
Mizah | 1613dafc39 | ||
Mizah | 8457fa9b3b | ||
Mizah | 2e51e779e7 | ||
Mizah | 7f6b486976 | ||
Mizah | 8c1508fae4 | ||
Mizah | aeb568664d | ||
Mizah | ecb990fb77 |
|
@ -67,8 +67,8 @@
|
||||||
"utf-8-validate": "6.0.3"
|
"utf-8-validate": "6.0.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.689.0",
|
"@aws-sdk/client-s3": "3.620.0",
|
||||||
"@aws-sdk/lib-storage": "3.689.0",
|
"@aws-sdk/lib-storage": "3.620.0",
|
||||||
"@bull-board/api": "6.0.0",
|
"@bull-board/api": "6.0.0",
|
||||||
"@bull-board/fastify": "6.0.0",
|
"@bull-board/fastify": "6.0.0",
|
||||||
"@bull-board/ui": "6.0.0",
|
"@bull-board/ui": "6.0.0",
|
||||||
|
|
|
@ -22,23 +22,66 @@ type Account = Misskey.entities.MeDetailed & { token: string };
|
||||||
const accountData = miLocalStorage.getItem('account');
|
const accountData = miLocalStorage.getItem('account');
|
||||||
|
|
||||||
// TODO: 外部からはreadonlyに
|
// TODO: 外部からはreadonlyに
|
||||||
|
/**
|
||||||
|
* Reactive state for the current account. "I" as in "I am logged in".
|
||||||
|
* Initialized from local storage if available, otherwise null.
|
||||||
|
*
|
||||||
|
* @type {Account | null}
|
||||||
|
*/
|
||||||
export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
|
export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current account is a moderator.
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true);
|
export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current account is an administrator.
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
export const iAmAdmin = $i != null && $i.isAdmin;
|
export const iAmAdmin = $i != null && $i.isAdmin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether it is necessary to sign in; checks if the current
|
||||||
|
* account is null and throws an error if so.
|
||||||
|
*
|
||||||
|
* @throws {Error} If the current account is null
|
||||||
|
* @returns {Account} The current account
|
||||||
|
*/
|
||||||
export function signinRequired() {
|
export function signinRequired() {
|
||||||
if ($i == null) throw new Error('signin required');
|
if ($i == null) throw new Error('signin required');
|
||||||
return $i;
|
return $i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the current number of notes from the current account.
|
||||||
|
*
|
||||||
|
* Note: This appears to only be used for the "notes1" achievement.
|
||||||
|
*
|
||||||
|
* Also, separating it like this might cause counts to get out-of-sync.
|
||||||
|
*/
|
||||||
export let notesCount = $i == null ? 0 : $i.notesCount;
|
export let notesCount = $i == null ? 0 : $i.notesCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the number of notes by one.
|
||||||
|
*
|
||||||
|
* Documentation TODO: What about $i.notesCount? Why not increment that?
|
||||||
|
*/
|
||||||
export function incNotesCount() {
|
export function incNotesCount() {
|
||||||
notesCount++;
|
notesCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function signout() {
|
export async function signout() {
|
||||||
if (!$i) return;
|
|
||||||
|
// If we're not signed in, there's nothing to do.
|
||||||
|
if (!$i) {
|
||||||
|
// Error log:
|
||||||
|
console.error('signout() called when not signed in');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
waiting();
|
waiting();
|
||||||
miLocalStorage.removeItem('account');
|
miLocalStorage.removeItem('account');
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A typesafe enum of keys for localStorage.
|
||||||
|
*/
|
||||||
export type Keys =
|
export type Keys =
|
||||||
'v' |
|
'v' |
|
||||||
'lastVersion' |
|
'lastVersion' |
|
||||||
|
@ -44,16 +47,45 @@ export type Keys =
|
||||||
// セッション毎に廃棄されるLocalStorage代替(セーフモードなどで使用できそう)
|
// セッション毎に廃棄されるLocalStorage代替(セーフモードなどで使用できそう)
|
||||||
//const safeSessionStorage = new Map<Keys, string>();
|
//const safeSessionStorage = new Map<Keys, string>();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility object for interacting with the browser's localStorage.
|
||||||
|
*
|
||||||
|
* It's mostly a small wrapper around window.localStorage, but it validates
|
||||||
|
* keys with a typesafe enum, and provides a few convenience methods for JSON.
|
||||||
|
*/
|
||||||
export const miLocalStorage = {
|
export const miLocalStorage = {
|
||||||
|
/**
|
||||||
|
* Retrieves an item from localStorage.
|
||||||
|
* @param {Keys} key - The key of the item to retrieve.
|
||||||
|
* @returns {string | null} The value of the item, or null if the item does not exist.
|
||||||
|
*/
|
||||||
getItem: (key: Keys): string | null => {
|
getItem: (key: Keys): string | null => {
|
||||||
return window.localStorage.getItem(key);
|
return window.localStorage.getItem(key);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores an item in localStorage.
|
||||||
|
* @param {Keys} key - The key of the item to store.
|
||||||
|
* @param {string} value - The value of the item to store.
|
||||||
|
*/
|
||||||
setItem: (key: Keys, value: string): void => {
|
setItem: (key: Keys, value: string): void => {
|
||||||
window.localStorage.setItem(key, value);
|
window.localStorage.setItem(key, value);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item from localStorage.
|
||||||
|
* @param {Keys} key - The key of the item to remove.
|
||||||
|
*/
|
||||||
removeItem: (key: Keys): void => {
|
removeItem: (key: Keys): void => {
|
||||||
window.localStorage.removeItem(key);
|
window.localStorage.removeItem(key);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an item from localStorage and parses it as JSON.
|
||||||
|
* @param {Keys} key - The key of the item to retrieve.
|
||||||
|
* @returns {any | undefined} The parsed value of the item, or undefined if the item does not exist.
|
||||||
|
*/
|
||||||
getItemAsJson: (key: Keys): any | undefined => {
|
getItemAsJson: (key: Keys): any | undefined => {
|
||||||
const item = miLocalStorage.getItem(key);
|
const item = miLocalStorage.getItem(key);
|
||||||
if (item === null) {
|
if (item === null) {
|
||||||
|
@ -61,6 +93,12 @@ export const miLocalStorage = {
|
||||||
}
|
}
|
||||||
return JSON.parse(item);
|
return JSON.parse(item);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores an item in localStorage as a JSON string.
|
||||||
|
* @param {Keys} key - The key of the item to store.
|
||||||
|
* @param {any} value - The value of the item to store.
|
||||||
|
*/
|
||||||
setItemAsJson: (key: Keys, value: any): void => {
|
setItemAsJson: (key: Keys, value: any): void => {
|
||||||
miLocalStorage.setItem(key, JSON.stringify(value));
|
miLocalStorage.setItem(key, JSON.stringify(value));
|
||||||
},
|
},
|
||||||
|
|
|
@ -136,7 +136,16 @@ export function promiseDialog<T extends Promise<any>>(
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for generating unique popup IDs.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
let popupIdCount = 0;
|
let popupIdCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reactive list of the currently opened popups. This is used in a Vue component
|
||||||
|
* in a v-for loop to render the popups.
|
||||||
|
*/
|
||||||
export const popups = ref<{
|
export const popups = ref<{
|
||||||
id: number;
|
id: number;
|
||||||
component: Component;
|
component: Component;
|
||||||
|
@ -144,12 +153,23 @@ export const popups = ref<{
|
||||||
events: Record<string, any>;
|
events: Record<string, any>;
|
||||||
}[]>([]);
|
}[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object containing z-index values for different priority levels.
|
||||||
|
*/
|
||||||
const zIndexes = {
|
const zIndexes = {
|
||||||
veryLow: 500000,
|
veryLow: 500000,
|
||||||
low: 1000000,
|
low: 1000000,
|
||||||
middle: 2000000,
|
middle: 2000000,
|
||||||
high: 3000000,
|
high: 3000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claims a z-index value for a given priority level.
|
||||||
|
* Increments the z-index value for the specified priority by 100 and returns the new value.
|
||||||
|
*
|
||||||
|
* @param {keyof typeof zIndexes} [priority='low'] - The priority level for which to claim a z-index.
|
||||||
|
* @returns {number} The new z-index value for the specified priority.
|
||||||
|
*/
|
||||||
export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number {
|
export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number {
|
||||||
zIndexes[priority] += 100;
|
zIndexes[priority] += 100;
|
||||||
return zIndexes[priority];
|
return zIndexes[priority];
|
||||||
|
@ -177,6 +197,15 @@ type EmitsExtractor<T> = {
|
||||||
[K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K];
|
[K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a popup with the specified component, props, and events.
|
||||||
|
*
|
||||||
|
* @template T - The type of the component.
|
||||||
|
* @param {T} component - The Vue component to display in the popup.
|
||||||
|
* @param {ComponentProps<T>} props - The props to pass to the component.
|
||||||
|
* @param {ComponentEmit<T>} [events={}] - The events to bind to the component.
|
||||||
|
* @returns {{ dispose: () => void }} An object containing a dispose function to close the popup.
|
||||||
|
*/
|
||||||
export function popup<T extends Component>(
|
export function popup<T extends Component>(
|
||||||
component: T,
|
component: T,
|
||||||
props: ComponentProps<T>,
|
props: ComponentProps<T>,
|
||||||
|
@ -184,13 +213,18 @@ export function popup<T extends Component>(
|
||||||
): { dispose: () => void } {
|
): { dispose: () => void } {
|
||||||
markRaw(component);
|
markRaw(component);
|
||||||
|
|
||||||
|
// Generate a unique ID for this popup.
|
||||||
const id = ++popupIdCount;
|
const id = ++popupIdCount;
|
||||||
|
|
||||||
|
// On disposal, remove this popup from the list of open popups.
|
||||||
const dispose = () => {
|
const dispose = () => {
|
||||||
// このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ?
|
// このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ?
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
popups.value = popups.value.filter(p => p.id !== id);
|
popups.value = popups.value.filter(p => p.id !== id);
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Bundle the component, props, and events into a state object.
|
||||||
const state = {
|
const state = {
|
||||||
component,
|
component,
|
||||||
props,
|
props,
|
||||||
|
@ -198,13 +232,19 @@ export function popup<T extends Component>(
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add the popup to the list of open popups.
|
||||||
popups.value.push(state);
|
popups.value.push(state);
|
||||||
|
|
||||||
|
// Return a function that can be called to close the popup.
|
||||||
return {
|
return {
|
||||||
dispose,
|
dispose,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the page with the given path in a pop-up window.
|
||||||
|
* @param path The path of the page to open.
|
||||||
|
*/
|
||||||
export function pageWindow(path: string) {
|
export function pageWindow(path: string) {
|
||||||
const { dispose } = popup(MkPageWindow, {
|
const { dispose } = popup(MkPageWindow, {
|
||||||
initialPath: path,
|
initialPath: path,
|
||||||
|
@ -213,6 +253,11 @@ export function pageWindow(path: string) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a toast message to the user.
|
||||||
|
*
|
||||||
|
* @param {string} message - The message to display in the toast.
|
||||||
|
*/
|
||||||
export function toast(message: string) {
|
export function toast(message: string) {
|
||||||
const { dispose } = popup(MkToast, {
|
const { dispose } = popup(MkToast, {
|
||||||
message,
|
message,
|
||||||
|
@ -221,6 +266,15 @@ export function toast(message: string) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays an alert dialog to the user.
|
||||||
|
*
|
||||||
|
* @param {Object} props - The properties for the alert dialog.
|
||||||
|
* @param {'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'} [props.type] - The type of the alert.
|
||||||
|
* @param {string} [props.title] - The title of the alert dialog.
|
||||||
|
* @param {string} [props.text] - The text content of the alert dialog.
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the alert dialog is closed.
|
||||||
|
*/
|
||||||
export function alert(props: {
|
export function alert(props: {
|
||||||
type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
|
type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
|
||||||
title?: string;
|
title?: string;
|
||||||
|
|
|
@ -26,6 +26,7 @@ if (window.Cypress) {
|
||||||
console.log('Cypress detected. It will use localStorage.');
|
console.log('Cypress detected. It will use localStorage.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for the availability of indexedDB.
|
||||||
if (idbAvailable) {
|
if (idbAvailable) {
|
||||||
await iset('idb-test', 'test')
|
await iset('idb-test', 'test')
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
@ -37,16 +38,36 @@ if (idbAvailable) {
|
||||||
console.error('indexedDB is unavailable. It will use localStorage.');
|
console.error('indexedDB is unavailable. It will use localStorage.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a value from indexedDB (or localStorage as a fallback).
|
||||||
|
*
|
||||||
|
* @param key The key of the item to retrieve.
|
||||||
|
*
|
||||||
|
* @returns The value of the item.
|
||||||
|
*/
|
||||||
export async function get(key: string) {
|
export async function get(key: string) {
|
||||||
if (idbAvailable) return iget(key);
|
if (idbAvailable) return iget(key);
|
||||||
return miLocalStorage.getItemAsJson(`${PREFIX}${key}`);
|
return miLocalStorage.getItemAsJson(`${PREFIX}${key}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value in indexedDB (or localStorage as a fallback).
|
||||||
|
*
|
||||||
|
* @param {string} key - The key of the item to set.
|
||||||
|
* @param {any} val - The value of the item to set.
|
||||||
|
* @returns {Promise<void>} - A promise that resolves when the value has been set.`
|
||||||
|
*/
|
||||||
export async function set(key: string, val: any) {
|
export async function set(key: string, val: any) {
|
||||||
if (idbAvailable) return iset(key, val);
|
if (idbAvailable) return iset(key, val);
|
||||||
return miLocalStorage.setItemAsJson(`${PREFIX}${key}`, val);
|
return miLocalStorage.setItemAsJson(`${PREFIX}${key}`, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a value from indexedDB (or localStorage as a fallback).
|
||||||
|
*
|
||||||
|
* @param {string} key - The key of the item to delete.
|
||||||
|
* @returns {Promise<void>} - A promise that resolves when the value has been deleted.
|
||||||
|
*/
|
||||||
export async function del(key: string) {
|
export async function del(key: string) {
|
||||||
if (idbAvailable) return idel(key);
|
if (idbAvailable) return idel(key);
|
||||||
return miLocalStorage.removeItem(`${PREFIX}${key}`);
|
return miLocalStorage.removeItem(`${PREFIX}${key}`);
|
||||||
|
|
1464
pnpm-lock.yaml
1464
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue