Merge tag '2024.11.0' into feature/2024.10

This commit is contained in:
dakkar 2024-11-22 12:29:04 +00:00
commit bc816cb166
234 changed files with 6612 additions and 4634 deletions

View file

@ -2,10 +2,9 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Misskey from 'misskey-js';
import type { Note, MeDetailed } from "misskey-js/entities.js";
export function checkWordMute(note: Note, me: MeDetailed | null | undefined, mutedWords: Array<string | string[]>): boolean {
export function checkWordMute(note: Misskey.entities.Note, me: Misskey.entities.UserLite | null | undefined, mutedWords: Array<string | string[]>): boolean {
// 自分自身
if (me && (note.userId === me.id)) return false;

View file

@ -3,22 +3,22 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { defaultStore } from '@/store.js';
await defaultStore.ready;
export type DeviceKind = 'smartphone' | 'tablet' | 'desktop';
const ua = navigator.userAgent.toLowerCase();
const isTablet = /ipad/.test(ua) || (/mobile|iphone|android/.test(ua) && window.innerWidth > 700);
const isSmartphone = !isTablet && /mobile|iphone|android/.test(ua);
const isIPhone = /iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1;
// navigator.platform may be deprecated but this check is still required
const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;
const isIos = /ipad|iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1;
export const DEFAULT_DEVICE_KIND: DeviceKind = (
isSmartphone
? 'smartphone'
: isTablet
? 'tablet'
: 'desktop'
);
export const isFullscreenNotSupported = isIPhone || isIos;
export let deviceKind: DeviceKind = DEFAULT_DEVICE_KIND;
export const deviceKind: 'smartphone' | 'tablet' | 'desktop' = defaultStore.state.overridedDeviceKind ? defaultStore.state.overridedDeviceKind
: isSmartphone ? 'smartphone'
: isTablet ? 'tablet'
: 'desktop';
export function updateDeviceKind(kind: DeviceKind | null) {
deviceKind = kind ?? DEFAULT_DEVICE_KIND;
}

View file

@ -15,7 +15,7 @@ type Hidden = boolean | ((v: any) => boolean);
export type FormItem = {
label?: string;
type: 'string';
default: string | null;
default?: string | null;
description?: string;
required?: boolean;
hidden?: Hidden;
@ -24,7 +24,7 @@ export type FormItem = {
} | {
label?: string;
type: 'number';
default: number | null;
default?: number | null;
description?: string;
required?: boolean;
hidden?: Hidden;
@ -32,20 +32,20 @@ export type FormItem = {
} | {
label?: string;
type: 'boolean';
default: boolean | null;
default?: boolean | null;
description?: string;
hidden?: Hidden;
} | {
label?: string;
type: 'enum';
default: string | null;
default?: string | null;
required?: boolean;
hidden?: Hidden;
enum: EnumItem[];
} | {
label?: string;
type: 'radio';
default: unknown | null;
default?: unknown | null;
required?: boolean;
hidden?: Hidden;
options: {
@ -55,7 +55,7 @@ export type FormItem = {
} | {
label?: string;
type: 'range';
default: number | null;
default?: number | null;
description?: string;
required?: boolean;
step?: number;
@ -66,12 +66,12 @@ export type FormItem = {
} | {
label?: string;
type: 'object';
default: Record<string, unknown> | null;
default?: Record<string, unknown> | null;
hidden: Hidden;
} | {
label?: string;
type: 'array';
default: unknown[] | null;
default?: unknown[] | null;
hidden: Hidden;
} | {
type: 'button';

View file

@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
type PartiallyPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type VideoEl = PartiallyPartial<HTMLVideoElement, 'requestFullscreen'> & {
webkitEnterFullscreen?(): void;
webkitExitFullscreen?(): void;
};
type PlayerEl = PartiallyPartial<HTMLElement, 'requestFullscreen'>;
type RequestFullscreenProps = {
readonly videoEl: VideoEl;
readonly playerEl: PlayerEl;
readonly options?: FullscreenOptions | null;
};
type ExitFullscreenProps = {
readonly videoEl: VideoEl;
};
export const requestFullscreen = ({ videoEl, playerEl, options }: RequestFullscreenProps) => {
if (playerEl.requestFullscreen != null) {
playerEl.requestFullscreen(options ?? undefined);
return;
}
if (videoEl.webkitEnterFullscreen != null) {
videoEl.webkitEnterFullscreen();
return;
}
};
export const exitFullscreen = ({ videoEl }: ExitFullscreenProps) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (document.exitFullscreen != null) {
document.exitFullscreen();
return;
}
if (videoEl.webkitExitFullscreen != null) {
videoEl.webkitExitFullscreen();
return;
}
};

View file

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import tinycolor from 'tinycolor2';
export const getBgColor = (elem?: Element | null | undefined): string | null => {
if (elem == null) return null;
const { backgroundColor: bg } = window.getComputedStyle(elem);
if (bg && tinycolor(bg).getAlpha() !== 0) {
return bg;
}
return getBgColor(elem.parentElement);
};

View file

@ -17,7 +17,7 @@ export function misskeyApi<
_ResT = ResT extends void ? Misskey.api.SwitchCaseResponseType<E, P> : ResT,
>(
endpoint: E,
data: P = {} as any,
data: P & { i?: string | null; } = {} as any,
token?: string | null | undefined,
signal?: AbortSignal,
): Promise<_ResT> {
@ -30,8 +30,8 @@ export function misskeyApi<
const promise = new Promise<_ResT>((resolve, reject) => {
// Append a credential
if ($i) (data as any).i = $i.token;
if (token !== undefined) (data as any).i = token;
if ($i) data.i = $i.token;
if (token !== undefined) data.i = token;
// Send request
window.fetch(`${apiUrl}/${endpoint}`, {

View file

@ -44,17 +44,21 @@ export type OpenOnRemoteOptions = {
params: Record<string, string>;
};
export function pleaseLogin(path?: string, openOnRemote?: OpenOnRemoteOptions) {
export function pleaseLogin(opts: {
path?: string;
message?: string;
openOnRemote?: OpenOnRemoteOptions;
} = {}) {
if ($i) return;
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {
autoSet: true,
message: openOnRemote ? i18n.ts.signinOrContinueOnRemote : i18n.ts.signinRequired,
openOnRemote,
message: opts.message ?? (opts.openOnRemote ? i18n.ts.signinOrContinueOnRemote : i18n.ts.signinRequired),
openOnRemote: opts.openOnRemote,
}, {
cancelled: () => {
if (path) {
window.location.href = path;
if (opts.path) {
window.location.href = opts.path;
}
},
closed: () => dispose(),

View file

@ -80,7 +80,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> {
});
}
function select(src: any, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
function select(src: HTMLElement | EventTarget | null, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
return new Promise((res, rej) => {
const keepOriginal = ref(defaultStore.state.keepOriginalUploading);
@ -107,10 +107,10 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Miss
});
}
export function selectFile(src: any, label: string | null = null): Promise<Misskey.entities.DriveFile> {
export function selectFile(src: HTMLElement | EventTarget | null, label: string | null = null): Promise<Misskey.entities.DriveFile> {
return select(src, label, false).then(files => files[0]);
}
export function selectFiles(src: any, label: string | null = null): Promise<Misskey.entities.DriveFile[]> {
export function selectFiles(src: HTMLElement | EventTarget | null, label: string | null = null): Promise<Misskey.entities.DriveFile[]> {
return select(src, label, true);
}

View file

@ -6,8 +6,9 @@
/**
* ()
*/
export function shuffle<T extends any[]>(array: T): T {
let currentIndex = array.length, randomIndex;
export function shuffle<T extends unknown[]>(array: T): T {
let currentIndex = array.length;
let randomIndex: number;
// While there remain elements to shuffle.
while (currentIndex !== 0) {

View file

@ -32,13 +32,13 @@ const mimeTypeMap = {
export function uploadFile(
file: File,
folder?: any,
folder?: string | Misskey.entities.DriveFolder,
name?: string,
keepOriginal: boolean = defaultStore.state.keepOriginalUploading,
): Promise<Misskey.entities.DriveFile> {
if ($i == null) throw new Error('Not logged in');
if (folder && typeof folder === 'object') folder = folder.id;
const _folder = typeof folder === 'string' ? folder : folder?.id;
if (file.size > instance.maxFileSize) {
alert({
@ -89,11 +89,11 @@ export function uploadFile(
}
const formData = new FormData();
formData.append('i', $i.token);
formData.append('i', $i!.token);
formData.append('force', 'true');
formData.append('file', resizedImage ?? file);
formData.append('name', ctx.name);
if (folder) formData.append('folderId', folder);
if (_folder) formData.append('folderId', _folder);
const xhr = new XMLHttpRequest();
xhr.open('POST', apiUrl + '/drive/files/create', true);