summaryrefslogtreecommitdiff
path: root/packages/frontend/src/scripts
diff options
context:
space:
mode:
authorJulia <julia@insertdomain.name>2024-12-31 02:30:13 +0000
committerJulia <julia@insertdomain.name>2024-12-31 02:30:13 +0000
commit4c0bbddd0fba7e0d76fb484312e691ee29fe5858 (patch)
tree4bb1a3a2a79c679ac021a2199bd526be469524d4 /packages/frontend/src/scripts
parentmerge: fixes for 2024.9.4 (if we want to) (!770) (diff)
parentBump version (diff)
downloadsharkey-4c0bbddd0fba7e0d76fb484312e691ee29fe5858.tar.gz
sharkey-4c0bbddd0fba7e0d76fb484312e691ee29fe5858.tar.bz2
sharkey-4c0bbddd0fba7e0d76fb484312e691ee29fe5858.zip
merge: Bump stable version (!842)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/842
Diffstat (limited to 'packages/frontend/src/scripts')
-rw-r--r--packages/frontend/src/scripts/check-word-mute.ts5
-rw-r--r--packages/frontend/src/scripts/device-kind.ts24
-rw-r--r--packages/frontend/src/scripts/favicon-dot.ts6
-rw-r--r--packages/frontend/src/scripts/following-feed-utils.ts189
-rw-r--r--packages/frontend/src/scripts/form.ts16
-rw-r--r--packages/frontend/src/scripts/fullscreen.ts46
-rw-r--r--packages/frontend/src/scripts/get-bg-color.ts18
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts11
-rw-r--r--packages/frontend/src/scripts/get-user-menu.ts3
-rw-r--r--packages/frontend/src/scripts/hotkey.ts2
-rw-r--r--packages/frontend/src/scripts/init-chart.ts2
-rw-r--r--packages/frontend/src/scripts/merge.ts2
-rw-r--r--packages/frontend/src/scripts/misskey-api.ts6
-rw-r--r--packages/frontend/src/scripts/please-login.ts14
-rw-r--r--packages/frontend/src/scripts/popup-position.ts14
-rw-r--r--packages/frontend/src/scripts/select-file.ts6
-rw-r--r--packages/frontend/src/scripts/shuffle.ts5
-rw-r--r--packages/frontend/src/scripts/theme.ts2
-rw-r--r--packages/frontend/src/scripts/upload.ts8
19 files changed, 258 insertions, 121 deletions
diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts
index e65c327ffe..6525c207f7 100644
--- a/packages/frontend/src/scripts/check-word-mute.ts
+++ b/packages/frontend/src/scripts/check-word-mute.ts
@@ -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;
diff --git a/packages/frontend/src/scripts/device-kind.ts b/packages/frontend/src/scripts/device-kind.ts
index 7c33f8ccee..7aadb617ca 100644
--- a/packages/frontend/src/scripts/device-kind.ts
+++ b/packages/frontend/src/scripts/device-kind.ts
@@ -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;
+}
diff --git a/packages/frontend/src/scripts/favicon-dot.ts b/packages/frontend/src/scripts/favicon-dot.ts
index a5972276e2..1f62c9ca70 100644
--- a/packages/frontend/src/scripts/favicon-dot.ts
+++ b/packages/frontend/src/scripts/favicon-dot.ts
@@ -78,7 +78,7 @@ class FavIconDot {
this.ctx.beginPath();
this.ctx.arc(this.faviconImage.width - 10, 10, 10, 0, 2 * Math.PI);
const computedStyle = getComputedStyle(document.documentElement);
- this.ctx.fillStyle = tinycolor(computedStyle.getPropertyValue('--navIndicator')).toHexString();
+ this.ctx.fillStyle = tinycolor(computedStyle.getPropertyValue('--MI_THEME-navIndicator')).toHexString();
this.ctx.strokeStyle = 'white';
this.ctx.fill();
this.ctx.stroke();
@@ -104,7 +104,7 @@ class FavIconDot {
this.drawDot();
this.canvas.toDataURL('image/png');
} catch (error) {
- return false;
+ return false;
}
return true;
}
@@ -140,6 +140,6 @@ export async function worksOnInstance() {
icon = new FavIconDot();
await icon.setup();
}
-
+
return await icon.worksOnInstance();
}
diff --git a/packages/frontend/src/scripts/following-feed-utils.ts b/packages/frontend/src/scripts/following-feed-utils.ts
index 064d6b72e3..39f17949d6 100644
--- a/packages/frontend/src/scripts/following-feed-utils.ts
+++ b/packages/frontend/src/scripts/following-feed-utils.ts
@@ -3,19 +3,75 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { computed } from 'vue';
+import { computed, Ref, WritableComputedRef } from 'vue';
import { defaultStore } from '@/store.js';
import { deepMerge } from '@/scripts/merge.js';
import { PageHeaderItem } from '@/types/page-header.js';
import { i18n } from '@/i18n.js';
import { popupMenu } from '@/os.js';
+import { MenuItem } from '@/types/menu.js';
export const followingTab = 'following' as const;
export const mutualsTab = 'mutuals' as const;
export const followersTab = 'followers' as const;
-export type FollowingFeedTab = typeof followingTab | typeof mutualsTab | typeof followersTab;
+export const followingFeedTabs = [followingTab, mutualsTab, followersTab] as const;
+export type FollowingFeedTab = typeof followingFeedTabs[number];
-export function createOptions(): PageHeaderItem {
+export function followingTabName(tab: FollowingFeedTab): string;
+export function followingTabName(tab: FollowingFeedTab | null | undefined): null;
+export function followingTabName(tab: FollowingFeedTab | null | undefined): string | null {
+ if (tab === followingTab) return i18n.ts.following;
+ if (tab === followersTab) return i18n.ts.followers;
+ if (tab === mutualsTab) return i18n.ts.mutuals;
+ return null;
+}
+
+export function followingTabIcon(tab: FollowingFeedTab | null | undefined): string {
+ if (tab === followersTab) return 'ph-user ph-bold ph-lg';
+ if (tab === mutualsTab) return 'ph-user-switch ph-bold ph-lg';
+ return 'ph-user-check ph-bold ph-lg';
+}
+
+export type FollowingFeedModel = {
+ [Key in keyof FollowingFeedState]: WritableComputedRef<FollowingFeedState[Key]>;
+}
+
+export interface FollowingFeedState {
+ withNonPublic: boolean,
+ withQuotes: boolean,
+ withBots: boolean,
+ withReplies: boolean,
+ onlyFiles: boolean,
+ userList: FollowingFeedTab,
+ remoteWarningDismissed: boolean,
+}
+
+export const defaultFollowingFeedState: FollowingFeedState = {
+ withNonPublic: false,
+ withQuotes: false,
+ withBots: true,
+ withReplies: false,
+ onlyFiles: false,
+ userList: followingTab,
+ remoteWarningDismissed: false,
+};
+
+interface StorageInterface<T extends Partial<FollowingFeedState> = Partial<FollowingFeedState>> {
+ readonly state: Partial<T>;
+ readonly reactiveState: Ref<Partial<T>>;
+ save(updated: T): void;
+}
+
+export function createHeaderItem(storage?: Ref<StorageInterface>): PageHeaderItem {
+ const menu = createOptionsMenu(storage);
+ return {
+ icon: 'ti ti-dots',
+ text: i18n.ts.options,
+ handler: ev => popupMenu(menu, ev.currentTarget ?? ev.target),
+ };
+}
+
+export function createOptionsMenu(storage?: Ref<StorageInterface>): MenuItem[] {
const {
userList,
withNonPublic,
@@ -23,80 +79,83 @@ export function createOptions(): PageHeaderItem {
withBots,
withReplies,
onlyFiles,
- } = createModel();
+ } = createModel(storage);
- return {
- icon: 'ti ti-dots',
- text: i18n.ts.options,
- handler: ev =>
- popupMenu([
- {
- type: 'switch',
- text: i18n.ts.showNonPublicNotes,
- ref: withNonPublic,
- disabled: userList.value === 'followers',
- },
- {
- type: 'switch',
- text: i18n.ts.showQuotes,
- ref: withQuotes,
- },
- {
- type: 'switch',
- text: i18n.ts.showBots,
- ref: withBots,
- },
- {
- type: 'switch',
- text: i18n.ts.showReplies,
- ref: withReplies,
- disabled: onlyFiles,
- },
- {
- type: 'divider',
- },
- {
- type: 'switch',
- text: i18n.ts.fileAttachedOnly,
- ref: onlyFiles,
- disabled: withReplies,
- },
- ], ev.currentTarget ?? ev.target),
- };
+ return [
+ {
+ type: 'switch',
+ text: i18n.ts.showNonPublicNotes,
+ ref: withNonPublic,
+ disabled: computed(() => userList.value === followersTab),
+ },
+ {
+ type: 'switch',
+ text: i18n.ts.showQuotes,
+ ref: withQuotes,
+ },
+ {
+ type: 'switch',
+ text: i18n.ts.showBots,
+ ref: withBots,
+ },
+ {
+ type: 'switch',
+ text: i18n.ts.showReplies,
+ ref: withReplies,
+ disabled: onlyFiles,
+ },
+ {
+ type: 'divider',
+ },
+ {
+ type: 'switch',
+ text: i18n.ts.fileAttachedOnly,
+ ref: onlyFiles,
+ disabled: withReplies,
+ },
+ ];
}
-export function createModel() {
- const userList = computed({
- get: () => defaultStore.reactiveState.followingFeed.value.userList,
+export function createModel(storage?: Ref<StorageInterface>): FollowingFeedModel {
+ // eslint-disable-next-line no-param-reassign
+ storage ??= createDefaultStorage();
+
+ // Based on timeline.saveTlFilter()
+ const saveFollowingFilter = <K extends keyof FollowingFeedState>(key: K, value: FollowingFeedState[K]) => {
+ const state = deepMerge(storage.value.state, defaultFollowingFeedState);
+ const out = deepMerge({ [key]: value }, state);
+ storage.value.save(out);
+ };
+
+ const userList: WritableComputedRef<FollowingFeedTab> = computed({
+ get: () => storage.value.reactiveState.value.userList ?? defaultFollowingFeedState.userList,
set: value => saveFollowingFilter('userList', value),
});
-
- const withNonPublic = computed({
+ const withNonPublic: WritableComputedRef<boolean> = computed({
get: () => {
if (userList.value === 'followers') return false;
- return defaultStore.reactiveState.followingFeed.value.withNonPublic;
+ return storage.value.reactiveState.value.withNonPublic ?? defaultFollowingFeedState.withNonPublic;
},
set: value => saveFollowingFilter('withNonPublic', value),
});
- const withQuotes = computed({
- get: () => defaultStore.reactiveState.followingFeed.value.withQuotes,
+ const withQuotes: WritableComputedRef<boolean> = computed({
+ get: () => storage.value.reactiveState.value.withQuotes ?? defaultFollowingFeedState.withQuotes,
set: value => saveFollowingFilter('withQuotes', value),
});
- const withBots = computed({
- get: () => defaultStore.reactiveState.followingFeed.value.withBots,
+ const withBots: WritableComputedRef<boolean> = computed({
+ get: () => storage.value.reactiveState.value.withBots ?? defaultFollowingFeedState.withBots,
set: value => saveFollowingFilter('withBots', value),
});
- const withReplies = computed({
- get: () => defaultStore.reactiveState.followingFeed.value.withReplies,
+ const withReplies: WritableComputedRef<boolean> = computed({
+ get: () => storage.value.reactiveState.value.withReplies ?? defaultFollowingFeedState.withReplies,
set: value => saveFollowingFilter('withReplies', value),
});
- const onlyFiles = computed({
- get: () => defaultStore.reactiveState.followingFeed.value.onlyFiles,
+ const onlyFiles: WritableComputedRef<boolean> = computed({
+ get: () => storage.value.reactiveState.value.onlyFiles ?? defaultFollowingFeedState.onlyFiles,
set: value => saveFollowingFilter('onlyFiles', value),
});
-
- const remoteWarningDismissed = computed({
- get: () => defaultStore.reactiveState.followingFeed.value.remoteWarningDismissed,
+ const remoteWarningDismissed: WritableComputedRef<boolean> = computed({
+ get: () => storage.value.reactiveState.value.remoteWarningDismissed ?? defaultFollowingFeedState.remoteWarningDismissed,
set: value => saveFollowingFilter('remoteWarningDismissed', value),
});
@@ -111,8 +170,12 @@ export function createModel() {
};
}
-// Based on timeline.saveTlFilter()
-function saveFollowingFilter<Key extends keyof typeof defaultStore.state.followingFeed>(key: Key, value: (typeof defaultStore.state.followingFeed)[Key]) {
- const out = deepMerge({ [key]: value }, defaultStore.state.followingFeed);
- return defaultStore.set('followingFeed', out);
+function createDefaultStorage() {
+ return computed(() => ({
+ state: defaultStore.state.followingFeed,
+ reactiveState: defaultStore.reactiveState.followingFeed,
+ save(updated: typeof defaultStore.state.followingFeed) {
+ return defaultStore.set('followingFeed', updated);
+ },
+ }));
}
diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts
index 242a504c3b..1032e97ac9 100644
--- a/packages/frontend/src/scripts/form.ts
+++ b/packages/frontend/src/scripts/form.ts
@@ -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';
diff --git a/packages/frontend/src/scripts/fullscreen.ts b/packages/frontend/src/scripts/fullscreen.ts
new file mode 100644
index 0000000000..7a0a018ef3
--- /dev/null
+++ b/packages/frontend/src/scripts/fullscreen.ts
@@ -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;
+ }
+};
diff --git a/packages/frontend/src/scripts/get-bg-color.ts b/packages/frontend/src/scripts/get-bg-color.ts
new file mode 100644
index 0000000000..ccf60b454f
--- /dev/null
+++ b/packages/frontend/src/scripts/get-bg-color.ts
@@ -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);
+};
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 4f3fb65665..c56fd185b6 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -267,13 +267,10 @@ export function getNoteMenu(props: {
function togglePin(pin: boolean): void {
os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', {
noteId: appearNote.id,
- }, undefined, null, res => {
- if (res.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
- os.alert({
- type: 'error',
- text: i18n.ts.pinLimitExceeded,
- });
- }
+ }, undefined, {
+ '72dab508-c64d-498f-8740-a8eec1ba385a': {
+ text: i18n.ts.pinLimitExceeded,
+ },
});
}
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index d15279d633..090cffe203 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -103,8 +103,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
async function getConfirmed(text: string): Promise<boolean> {
const confirm = await os.confirm({
- type: 'warning',
- title: 'confirm',
+ type: 'question',
text,
});
diff --git a/packages/frontend/src/scripts/hotkey.ts b/packages/frontend/src/scripts/hotkey.ts
index 04fb235694..d5304ee210 100644
--- a/packages/frontend/src/scripts/hotkey.ts
+++ b/packages/frontend/src/scripts/hotkey.ts
@@ -42,7 +42,7 @@ const KEY_ALIASES = {
const MODIFIER_KEYS = ['ctrl', 'alt', 'shift'];
-const IGNORE_ELEMENTS = ['input', 'textarea'];
+const IGNORE_ELEMENTS = ['input', 'textarea', 'ruffle-player'];
//#endregion
//#region store
diff --git a/packages/frontend/src/scripts/init-chart.ts b/packages/frontend/src/scripts/init-chart.ts
index 2465a14703..41e1636aa7 100644
--- a/packages/frontend/src/scripts/init-chart.ts
+++ b/packages/frontend/src/scripts/init-chart.ts
@@ -50,7 +50,7 @@ export function initChart() {
);
// フォントカラー
- Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
+ Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-fg');
Chart.defaults.borderColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
diff --git a/packages/frontend/src/scripts/merge.ts b/packages/frontend/src/scripts/merge.ts
index 9794a300da..89fdda0cbb 100644
--- a/packages/frontend/src/scripts/merge.ts
+++ b/packages/frontend/src/scripts/merge.ts
@@ -18,7 +18,7 @@ function isPureObject(value: unknown): value is Record<string | number | symbol,
* valueにないキーをdefからもらう(再帰的)\
* nullはそのまま、undefinedはdefの値
**/
-export function deepMerge<X extends Record<string | number | symbol, unknown>>(value: DeepPartial<X>, def: X): X {
+export function deepMerge<X extends object>(value: DeepPartial<X>, def: X): X {
if (isPureObject(value) && isPureObject(def)) {
const result = deepClone(value as Cloneable) as X;
for (const [k, v] of Object.entries(def) as [keyof X, X[keyof X]][]) {
diff --git a/packages/frontend/src/scripts/misskey-api.ts b/packages/frontend/src/scripts/misskey-api.ts
index 1b1159fd01..e7a92e2d5c 100644
--- a/packages/frontend/src/scripts/misskey-api.ts
+++ b/packages/frontend/src/scripts/misskey-api.ts
@@ -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}`, {
diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts
index 18f05bc7f4..43dcf11936 100644
--- a/packages/frontend/src/scripts/please-login.ts
+++ b/packages/frontend/src/scripts/please-login.ts
@@ -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(),
diff --git a/packages/frontend/src/scripts/popup-position.ts b/packages/frontend/src/scripts/popup-position.ts
index 3dad41a8b3..be49532cf8 100644
--- a/packages/frontend/src/scripts/popup-position.ts
+++ b/packages/frontend/src/scripts/popup-position.ts
@@ -15,6 +15,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
const contentWidth = el.offsetWidth;
const contentHeight = el.offsetHeight;
+ const HORIZONTAL_MARGIN = 1;
+
let rect: DOMRect;
if (props.anchorElement) {
@@ -36,9 +38,11 @@ export function calcPopupPosition(el: HTMLElement, props: {
left -= (el.offsetWidth / 2);
if (left + contentWidth - window.scrollX > window.innerWidth) {
- left = window.innerWidth - contentWidth + window.scrollX - 1;
+ left = window.innerWidth - contentWidth + window.scrollX - HORIZONTAL_MARGIN;
}
+ left = Math.max(HORIZONTAL_MARGIN, left);
+
return [left, top];
};
@@ -57,9 +61,11 @@ export function calcPopupPosition(el: HTMLElement, props: {
left -= (el.offsetWidth / 2);
if (left + contentWidth - window.scrollX > window.innerWidth) {
- left = window.innerWidth - contentWidth + window.scrollX - 1;
+ left = window.innerWidth - contentWidth + window.scrollX - HORIZONTAL_MARGIN;
}
+ left = Math.max(HORIZONTAL_MARGIN, left);
+
return [left, top];
};
@@ -75,6 +81,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
top = props.y;
}
+ left = Math.max(HORIZONTAL_MARGIN, left);
+
top -= (el.offsetHeight / 2);
if (top + contentHeight - window.scrollY > window.innerHeight) {
@@ -106,6 +114,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
top -= (el.offsetHeight / 2);
}
+ left = Math.max(HORIZONTAL_MARGIN, left);
+
if (top + contentHeight - window.scrollY > window.innerHeight) {
top = window.innerHeight - contentHeight + window.scrollY - 1;
}
diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts
index 9aa38178b2..b037aa8acc 100644
--- a/packages/frontend/src/scripts/select-file.ts
+++ b/packages/frontend/src/scripts/select-file.ts
@@ -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);
}
diff --git a/packages/frontend/src/scripts/shuffle.ts b/packages/frontend/src/scripts/shuffle.ts
index fed16bc71c..1f6ef1928c 100644
--- a/packages/frontend/src/scripts/shuffle.ts
+++ b/packages/frontend/src/scripts/shuffle.ts
@@ -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) {
diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index bd3cddde67..8242e7d2e4 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -123,7 +123,7 @@ export function applyTheme(theme: Theme, persist = true) {
for (const [k, v] of Object.entries(props)) {
if (k.startsWith('font')) continue;
- document.documentElement.style.setProperty(`--${k}`, v.toString());
+ document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
}
document.documentElement.style.setProperty('color-scheme', colorScheme);
diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts
index 22dce609c6..713573a377 100644
--- a/packages/frontend/src/scripts/upload.ts
+++ b/packages/frontend/src/scripts/upload.ts
@@ -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);