summaryrefslogtreecommitdiff
path: root/packages/client/src/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/client/src/scripts')
-rw-r--r--packages/client/src/scripts/2fa.ts8
-rw-r--r--packages/client/src/scripts/autocomplete.ts28
-rw-r--r--packages/client/src/scripts/contains.ts2
-rw-r--r--packages/client/src/scripts/emojilist.ts2
-rw-r--r--packages/client/src/scripts/extract-avg-color-from-blurhash.ts2
-rw-r--r--packages/client/src/scripts/format-time-string.ts6
-rw-r--r--packages/client/src/scripts/get-account-from-id.ts2
-rw-r--r--packages/client/src/scripts/get-md5.ts10
-rw-r--r--packages/client/src/scripts/get-note-menu.ts20
-rw-r--r--packages/client/src/scripts/get-note-summary.ts2
-rw-r--r--packages/client/src/scripts/get-user-menu.ts11
-rw-r--r--packages/client/src/scripts/get-user-name.ts3
-rw-r--r--packages/client/src/scripts/hotkey.ts24
-rw-r--r--packages/client/src/scripts/hpml/evaluator.ts10
-rw-r--r--packages/client/src/scripts/hpml/lib.ts4
-rw-r--r--packages/client/src/scripts/idb-proxy.ts4
-rw-r--r--packages/client/src/scripts/initialize-sw.ts30
-rw-r--r--packages/client/src/scripts/lookup-user.ts6
-rw-r--r--packages/client/src/scripts/navigate.ts34
-rw-r--r--packages/client/src/scripts/physics.ts6
-rw-r--r--packages/client/src/scripts/please-login.ts19
-rw-r--r--packages/client/src/scripts/popout.ts5
-rw-r--r--packages/client/src/scripts/reaction-picker.ts4
-rw-r--r--packages/client/src/scripts/select-file.ts13
-rw-r--r--packages/client/src/scripts/theme-editor.ts2
-rw-r--r--packages/client/src/scripts/theme.ts51
-rw-r--r--packages/client/src/scripts/upload.ts114
-rw-r--r--packages/client/src/scripts/url.ts2
-rw-r--r--packages/client/src/scripts/use-note-capture.ts4
29 files changed, 294 insertions, 134 deletions
diff --git a/packages/client/src/scripts/2fa.ts b/packages/client/src/scripts/2fa.ts
index 00363cffa6..d1b9581e72 100644
--- a/packages/client/src/scripts/2fa.ts
+++ b/packages/client/src/scripts/2fa.ts
@@ -1,11 +1,11 @@
-export function byteify(data: string, encoding: 'ascii' | 'base64' | 'hex') {
+export function byteify(string: string, encoding: 'ascii' | 'base64' | 'hex') {
switch (encoding) {
case 'ascii':
- return Uint8Array.from(data, c => c.charCodeAt(0));
+ return Uint8Array.from(string, c => c.charCodeAt(0));
case 'base64':
return Uint8Array.from(
atob(
- data
+ string
.replace(/-/g, '+')
.replace(/_/g, '/')
),
@@ -13,7 +13,7 @@ export function byteify(data: string, encoding: 'ascii' | 'base64' | 'hex') {
);
case 'hex':
return new Uint8Array(
- data
+ string
.match(/.{1,2}/g)
.map(byte => parseInt(byte, 16))
);
diff --git a/packages/client/src/scripts/autocomplete.ts b/packages/client/src/scripts/autocomplete.ts
index f4a3a4c0fc..8d9bdee8f5 100644
--- a/packages/client/src/scripts/autocomplete.ts
+++ b/packages/client/src/scripts/autocomplete.ts
@@ -1,5 +1,5 @@
-import { nextTick, Ref, ref } from 'vue';
-import * as getCaretCoordinates from 'textarea-caret';
+import { nextTick, Ref, ref, defineAsyncComponent } from 'vue';
+import getCaretCoordinates from 'textarea-caret';
import { toASCII } from 'punycode/';
import { popup } from '@/os';
@@ -74,21 +74,21 @@ export class Autocomplete {
emojiIndex,
mfmTagIndex);
- if (max == -1) {
+ if (max === -1) {
this.close();
return;
}
- const isMention = mentionIndex != -1;
- const isHashtag = hashtagIndex != -1;
- const isMfmTag = mfmTagIndex != -1;
- const isEmoji = emojiIndex != -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
+ const isMention = mentionIndex !== -1;
+ const isHashtag = hashtagIndex !== -1;
+ const isMfmTag = mfmTagIndex !== -1;
+ const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
let opened = false;
if (isMention) {
const username = text.substr(mentionIndex + 1);
- if (username != '' && username.match(/^[a-zA-Z0-9_]+$/)) {
+ if (username !== '' && username.match(/^[a-zA-Z0-9_]+$/)) {
this.open('user', username);
opened = true;
} else if (username === '') {
@@ -130,7 +130,7 @@ export class Autocomplete {
* サジェストを提示します。
*/
private async open(type: string, q: string | null) {
- if (type != this.currentType) {
+ if (type !== this.currentType) {
this.close();
}
if (this.opening) return;
@@ -157,7 +157,7 @@ export class Autocomplete {
const _y = ref(y);
const _q = ref(q);
- const { dispose } = await popup(import('@/components/autocomplete.vue'), {
+ const { dispose } = await popup(defineAsyncComponent(() => import('@/components/autocomplete.vue')), {
textarea: this.textarea,
close: this.close,
type: type,
@@ -201,7 +201,7 @@ export class Autocomplete {
const caret = this.textarea.selectionStart;
- if (type == 'user') {
+ if (type === 'user') {
const source = this.text;
const before = source.substr(0, caret);
@@ -219,7 +219,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + (acct.length + 2);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'hashtag') {
+ } else if (type === 'hashtag') {
const source = this.text;
const before = source.substr(0, caret);
@@ -235,7 +235,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + (value.length + 2);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'emoji') {
+ } else if (type === 'emoji') {
const source = this.text;
const before = source.substr(0, caret);
@@ -251,7 +251,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + value.length;
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type == 'mfmTag') {
+ } else if (type === 'mfmTag') {
const source = this.text;
const before = source.substr(0, caret);
diff --git a/packages/client/src/scripts/contains.ts b/packages/client/src/scripts/contains.ts
index 770bda63bb..256e09d293 100644
--- a/packages/client/src/scripts/contains.ts
+++ b/packages/client/src/scripts/contains.ts
@@ -2,7 +2,7 @@ export default (parent, child, checkSame = true) => {
if (checkSame && parent === child) return true;
let node = child.parentNode;
while (node) {
- if (node == parent) return true;
+ if (node === parent) return true;
node = node.parentNode;
}
return false;
diff --git a/packages/client/src/scripts/emojilist.ts b/packages/client/src/scripts/emojilist.ts
index bd8689e4f8..4196170d24 100644
--- a/packages/client/src/scripts/emojilist.ts
+++ b/packages/client/src/scripts/emojilist.ts
@@ -8,4 +8,4 @@ export type UnicodeEmojiDef = {
}
// initial converted from https://github.com/muan/emojilib/commit/242fe68be86ed6536843b83f7e32f376468b38fb
-export const emojilist = require('../emojilist.json') as UnicodeEmojiDef[];
+export const emojilist = (await import('../emojilist.json')).default as UnicodeEmojiDef[];
diff --git a/packages/client/src/scripts/extract-avg-color-from-blurhash.ts b/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
index 123ab7a06d..af517f2672 100644
--- a/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
+++ b/packages/client/src/scripts/extract-avg-color-from-blurhash.ts
@@ -1,5 +1,5 @@
export function extractAvgColorFromBlurhash(hash: string) {
- return typeof hash == 'string'
+ return typeof hash === 'string'
? '#' + [...hash.slice(2, 6)]
.map(x => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~'.indexOf(x))
.reduce((a, c) => a * 83 + c, 0)
diff --git a/packages/client/src/scripts/format-time-string.ts b/packages/client/src/scripts/format-time-string.ts
index bfb2c397ae..fb4718c007 100644
--- a/packages/client/src/scripts/format-time-string.ts
+++ b/packages/client/src/scripts/format-time-string.ts
@@ -13,7 +13,7 @@ const defaultLocaleStringFormats: {[index: string]: string} = {
function formatLocaleString(date: Date, format: string): string {
return format.replace(/\{\{(\w+)(:(\w+))?\}\}/g, (match: string, kind: string, unused?, option?: string) => {
if (['weekday', 'era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName'].includes(kind)) {
- return date.toLocaleString(window.navigator.language, {[kind]: option ? option : defaultLocaleStringFormats[kind]});
+ return date.toLocaleString(window.navigator.language, { [kind]: option ? option : defaultLocaleStringFormats[kind] });
} else {
return match;
}
@@ -24,8 +24,8 @@ export function formatDateTimeString(date: Date, format: string): string {
return format
.replace(/yyyy/g, date.getFullYear().toString())
.replace(/yy/g, date.getFullYear().toString().slice(-2))
- .replace(/MMMM/g, date.toLocaleString(window.navigator.language, { month: 'long'}))
- .replace(/MMM/g, date.toLocaleString(window.navigator.language, { month: 'short'}))
+ .replace(/MMMM/g, date.toLocaleString(window.navigator.language, { month: 'long' }))
+ .replace(/MMM/g, date.toLocaleString(window.navigator.language, { month: 'short' }))
.replace(/MM/g, (`0${date.getMonth() + 1}`).slice(-2))
.replace(/M/g, (date.getMonth() + 1).toString())
.replace(/dd/g, (`0${date.getDate()}`).slice(-2))
diff --git a/packages/client/src/scripts/get-account-from-id.ts b/packages/client/src/scripts/get-account-from-id.ts
index ba3adceecc..1da897f176 100644
--- a/packages/client/src/scripts/get-account-from-id.ts
+++ b/packages/client/src/scripts/get-account-from-id.ts
@@ -3,5 +3,5 @@ import { get } from '@/scripts/idb-proxy';
export async function getAccountFromId(id: string) {
const accounts = await get('accounts') as { token: string; id: string; }[];
if (!accounts) console.log('Accounts are not recorded');
- return accounts.find(e => e.id === id);
+ return accounts.find(account => account.id === id);
}
diff --git a/packages/client/src/scripts/get-md5.ts b/packages/client/src/scripts/get-md5.ts
deleted file mode 100644
index b002d762b1..0000000000
--- a/packages/client/src/scripts/get-md5.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// スクリプトサイズがデカい
-//import * as crypto from 'crypto';
-
-export default (data: ArrayBuffer) => {
- //const buf = new Buffer(data);
- //const hash = crypto.createHash('md5');
- //hash.update(buf);
- //return hash.digest('hex');
- return '';
-};
diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts
index b19656d3cc..78749ad6bb 100644
--- a/packages/client/src/scripts/get-note-menu.ts
+++ b/packages/client/src/scripts/get-note-menu.ts
@@ -1,4 +1,4 @@
-import { Ref } from 'vue';
+import { defineAsyncComponent, Ref } from 'vue';
import * as misskey from 'misskey-js';
import { $i } from '@/account';
import { i18n } from '@/i18n';
@@ -22,7 +22,7 @@ export function getNoteMenu(props: {
props.note.poll == null
);
- let appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note;
+ const appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note;
function del(): void {
os.confirm({
@@ -83,8 +83,8 @@ export function getNoteMenu(props: {
function togglePin(pin: boolean): void {
os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', {
noteId: appearNote.id
- }, undefined, null, e => {
- if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
+ }, undefined, null, res => {
+ if (res.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
os.alert({
type: 'error',
text: i18n.ts.pinLimitExceeded
@@ -209,7 +209,7 @@ export function getNoteMenu(props: {
text: i18n.ts.clip,
action: () => clip()
},
- (appearNote.userId != $i.id) ? statePromise.then(state => state.isWatching ? {
+ (appearNote.userId !== $i.id) ? statePromise.then(state => state.isWatching ? {
icon: 'fas fa-eye-slash',
text: i18n.ts.unwatch,
action: () => toggleWatch(false)
@@ -227,7 +227,7 @@ export function getNoteMenu(props: {
text: i18n.ts.muteThread,
action: () => toggleThreadMute(true)
}),
- appearNote.userId == $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? {
+ appearNote.userId === $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? {
icon: 'fas fa-thumbtack',
text: i18n.ts.unpin,
action: () => togglePin(false)
@@ -246,14 +246,14 @@ export function getNoteMenu(props: {
}]
: []
),*/
- ...(appearNote.userId != $i.id ? [
+ ...(appearNote.userId !== $i.id ? [
null,
{
icon: 'fas fa-exclamation-circle',
text: i18n.ts.reportAbuse,
action: () => {
const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`;
- os.popup(import('@/components/abuse-report-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
user: appearNote.user,
initialComment: `Note: ${u}\n-----\n`
}, {}, 'closed');
@@ -261,9 +261,9 @@ export function getNoteMenu(props: {
}]
: []
),
- ...(appearNote.userId == $i.id || $i.isModerator || $i.isAdmin ? [
+ ...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
null,
- appearNote.userId == $i.id ? {
+ appearNote.userId === $i.id ? {
icon: 'fas fa-edit',
text: i18n.ts.deleteAndEdit,
action: delEdit
diff --git a/packages/client/src/scripts/get-note-summary.ts b/packages/client/src/scripts/get-note-summary.ts
index 54b8d109d6..d57e1c3029 100644
--- a/packages/client/src/scripts/get-note-summary.ts
+++ b/packages/client/src/scripts/get-note-summary.ts
@@ -24,7 +24,7 @@ export const getNoteSummary = (note: misskey.entities.Note): string => {
}
// ファイルが添付されているとき
- if ((note.files || []).length != 0) {
+ if ((note.files || []).length !== 0) {
summary += ` (${i18n.t('withNFiles', { n: note.files.length })})`;
}
diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts
index 192d14b83e..091338efd6 100644
--- a/packages/client/src/scripts/get-user-menu.ts
+++ b/packages/client/src/scripts/get-user-menu.ts
@@ -6,6 +6,7 @@ import * as os from '@/os';
import { userActions } from '@/store';
import { router } from '@/router';
import { $i, iAmModerator } from '@/account';
+import { defineAsyncComponent } from 'vue';
export function getUserMenu(user) {
const meId = $i ? $i.id : null;
@@ -127,7 +128,7 @@ export function getUserMenu(user) {
}
function reportAbuse() {
- os.popup(import('@/components/abuse-report-window.vue'), {
+ os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), {
user: user,
}, {}, 'closed');
}
@@ -147,7 +148,7 @@ export function getUserMenu(user) {
userId: user.id
}).then(() => {
user.isFollowed = !user.isFollowed;
- })
+ });
}
let menu = [{
@@ -168,7 +169,7 @@ export function getUserMenu(user) {
action: () => {
os.post({ specified: user });
}
- }, meId != user.id ? {
+ }, meId !== user.id ? {
type: 'link',
icon: 'fas fa-comments',
text: i18n.ts.startMessaging,
@@ -177,13 +178,13 @@ export function getUserMenu(user) {
icon: 'fas fa-list-ul',
text: i18n.ts.addToList,
action: pushList
- }, meId != user.id ? {
+ }, meId !== user.id ? {
icon: 'fas fa-users',
text: i18n.ts.inviteToGroup,
action: inviteGroup
} : undefined] as any;
- if ($i && meId != user.id) {
+ if ($i && meId !== user.id) {
menu = menu.concat([null, {
icon: user.isMuted ? 'fas fa-eye' : 'fas fa-eye-slash',
text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
diff --git a/packages/client/src/scripts/get-user-name.ts b/packages/client/src/scripts/get-user-name.ts
new file mode 100644
index 0000000000..d499ea0203
--- /dev/null
+++ b/packages/client/src/scripts/get-user-name.ts
@@ -0,0 +1,3 @@
+export default function(user: { name?: string | null, username: string }): string {
+ return user.name || user.username;
+}
diff --git a/packages/client/src/scripts/hotkey.ts b/packages/client/src/scripts/hotkey.ts
index 2b3f491fd8..fd9c74f6c8 100644
--- a/packages/client/src/scripts/hotkey.ts
+++ b/packages/client/src/scripts/hotkey.ts
@@ -53,34 +53,34 @@ const parseKeymap = (keymap: Keymap) => Object.entries(keymap).map(([patterns, c
const ignoreElemens = ['input', 'textarea'];
-function match(e: KeyboardEvent, patterns: Action['patterns']): boolean {
- const key = e.code.toLowerCase();
+function match(ev: KeyboardEvent, patterns: Action['patterns']): boolean {
+ const key = ev.code.toLowerCase();
return patterns.some(pattern => pattern.which.includes(key) &&
- pattern.ctrl === e.ctrlKey &&
- pattern.shift === e.shiftKey &&
- pattern.alt === e.altKey &&
- !e.metaKey
+ pattern.ctrl === ev.ctrlKey &&
+ pattern.shift === ev.shiftKey &&
+ pattern.alt === ev.altKey &&
+ !ev.metaKey
);
}
export const makeHotkey = (keymap: Keymap) => {
const actions = parseKeymap(keymap);
- return (e: KeyboardEvent) => {
+ return (ev: KeyboardEvent) => {
if (document.activeElement) {
if (ignoreElemens.some(el => document.activeElement!.matches(el))) return;
if (document.activeElement.attributes['contenteditable']) return;
}
for (const action of actions) {
- const matched = match(e, action.patterns);
+ const matched = match(ev, action.patterns);
if (matched) {
- if (!action.allowRepeat && e.repeat) return;
+ if (!action.allowRepeat && ev.repeat) return;
- e.preventDefault();
- e.stopPropagation();
- action.callback(e);
+ ev.preventDefault();
+ ev.stopPropagation();
+ action.callback(ev);
break;
}
}
diff --git a/packages/client/src/scripts/hpml/evaluator.ts b/packages/client/src/scripts/hpml/evaluator.ts
index 6329c0860e..8106687b61 100644
--- a/packages/client/src/scripts/hpml/evaluator.ts
+++ b/packages/client/src/scripts/hpml/evaluator.ts
@@ -36,7 +36,7 @@ export class Hpml {
if (this.opts.enableAiScript) {
this.aiscript = markRaw(new AiScript({ ...createAiScriptEnv({
storageKey: 'pages:' + this.page.id
- }), ...initAiLib(this)}, {
+ }), ...initAiLib(this) }, {
in: (q) => {
return new Promise(ok => {
os.inputText({
@@ -85,7 +85,7 @@ export class Hpml {
public eval() {
try {
this.vars.value = this.evaluateVars();
- } catch (e) {
+ } catch (err) {
//this.onError(e);
}
}
@@ -103,7 +103,7 @@ export class Hpml {
public callAiScript(fn: string) {
try {
if (this.aiscript) this.aiscript.execFn(this.aiscript.scope.get(fn), []);
- } catch (e) {}
+ } catch (err) {}
}
@autobind
@@ -185,7 +185,7 @@ export class Hpml {
if (this.aiscript) {
try {
return utils.valToJs(this.aiscript.scope.get(expr.value));
- } catch (e) {
+ } catch (err) {
return null;
}
} else {
@@ -194,7 +194,7 @@ export class Hpml {
}
// Define user function
- if (expr.type == 'fn') {
+ if (expr.type === 'fn') {
return {
slots: expr.value.slots.map(x => x.name),
exec: (slotArg: Record<string, any>) => {
diff --git a/packages/client/src/scripts/hpml/lib.ts b/packages/client/src/scripts/hpml/lib.ts
index 2a1ac73a40..01a44ffcdf 100644
--- a/packages/client/src/scripts/hpml/lib.ts
+++ b/packages/client/src/scripts/hpml/lib.ts
@@ -1,9 +1,9 @@
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
import { Hpml } from './evaluator';
import { values, utils } from '@syuilo/aiscript';
import { Fn, HpmlScope } from '.';
import { Expr } from './expr';
-import * as seedrandom from 'seedrandom';
+import seedrandom from 'seedrandom';
/* TODO: https://www.chartjs.org/docs/latest/configuration/canvas-background.html#color
// https://stackoverflow.com/questions/38493564/chart-area-background-color-chartjs
diff --git a/packages/client/src/scripts/idb-proxy.ts b/packages/client/src/scripts/idb-proxy.ts
index 5f76ae30bb..d462a0d7ce 100644
--- a/packages/client/src/scripts/idb-proxy.ts
+++ b/packages/client/src/scripts/idb-proxy.ts
@@ -13,8 +13,8 @@ let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true;
if (idbAvailable) {
try {
await iset('idb-test', 'test');
- } catch (e) {
- console.error('idb error', e);
+ } catch (err) {
+ console.error('idb error', err);
idbAvailable = false;
}
}
diff --git a/packages/client/src/scripts/initialize-sw.ts b/packages/client/src/scripts/initialize-sw.ts
index d6dbd5dbd4..7bacfbdf00 100644
--- a/packages/client/src/scripts/initialize-sw.ts
+++ b/packages/client/src/scripts/initialize-sw.ts
@@ -4,26 +4,26 @@ import { api } from '@/os';
import { lang } from '@/config';
export async function initializeSw() {
- if (instance.swPublickey &&
- ('serviceWorker' in navigator) &&
- ('PushManager' in window) &&
- $i && $i.token) {
- navigator.serviceWorker.register(`/sw.js`);
+ if (!('serviceWorker' in navigator)) return;
- navigator.serviceWorker.ready.then(registration => {
- registration.active?.postMessage({
- msg: 'initialize',
- lang,
- });
+ navigator.serviceWorker.register(`/sw.js`, { scope: '/', type: 'classic' });
+ navigator.serviceWorker.ready.then(registration => {
+ registration.active?.postMessage({
+ msg: 'initialize',
+ lang,
+ });
+
+ if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) {
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(instance.swPublickey)
- }).then(subscription => {
+ })
+ .then(subscription => {
function encode(buffer: ArrayBuffer | null) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));
}
-
+
// Register
api('sw/register', {
endpoint: subscription.endpoint,
@@ -37,15 +37,15 @@ export async function initializeSw() {
if (err.name === 'NotAllowedError') {
return;
}
-
+
// 違うapplicationServerKey (または gcm_sender_id)のサブスクリプションが
// 既に存在していることが原因でエラーになった可能性があるので、
// そのサブスクリプションを解除しておく
const subscription = await registration.pushManager.getSubscription();
if (subscription) subscription.unsubscribe();
});
- });
- }
+ }
+ });
}
/**
diff --git a/packages/client/src/scripts/lookup-user.ts b/packages/client/src/scripts/lookup-user.ts
index 8de5c84ce8..2d00e51621 100644
--- a/packages/client/src/scripts/lookup-user.ts
+++ b/packages/client/src/scripts/lookup-user.ts
@@ -25,12 +25,12 @@ export async function lookupUser() {
_notFound = true;
}
};
- usernamePromise.then(show).catch(e => {
- if (e.code === 'NO_SUCH_USER') {
+ usernamePromise.then(show).catch(err => {
+ if (err.code === 'NO_SUCH_USER') {
notFound();
}
});
- idPromise.then(show).catch(e => {
+ idPromise.then(show).catch(err => {
notFound();
});
}
diff --git a/packages/client/src/scripts/navigate.ts b/packages/client/src/scripts/navigate.ts
new file mode 100644
index 0000000000..08b891ec5b
--- /dev/null
+++ b/packages/client/src/scripts/navigate.ts
@@ -0,0 +1,34 @@
+import { inject } from 'vue';
+import { router } from '@/router';
+import { defaultStore } from '@/store';
+
+export type Navigate = (path: string, record?: boolean) => void;
+
+export class MisskeyNavigator {
+ public readonly navHook: Navigate | null = null;
+ public readonly sideViewHook: Navigate | null = null;
+
+ // It should be constructed during vue creating in order for inject function to work
+ constructor() {
+ this.navHook = inject<Navigate | null>('navHook', null);
+ this.sideViewHook = inject<Navigate | null>('sideViewHook', null);
+ }
+
+ // Use this method instead of router.push()
+ public push(path: string, record = true) {
+ if (this.navHook) {
+ this.navHook(path, record);
+ } else {
+ if (defaultStore.state.defaultSideView && this.sideViewHook && path !== '/') {
+ return this.sideViewHook(path, record);
+ }
+
+ if (router.currentRoute.value.path === path) {
+ window.scroll({ top: 0, behavior: 'smooth' });
+ } else {
+ if (record) router.push(path);
+ else router.replace(path);
+ }
+ }
+ }
+}
diff --git a/packages/client/src/scripts/physics.ts b/packages/client/src/scripts/physics.ts
index 36e476b6f9..9e657906c2 100644
--- a/packages/client/src/scripts/physics.ts
+++ b/packages/client/src/scripts/physics.ts
@@ -41,9 +41,9 @@ export function physics(container: HTMLElement) {
const groundThickness = 1024;
const ground = Matter.Bodies.rectangle(containerCenterX, containerHeight + (groundThickness / 2), containerWidth, groundThickness, {
- isStatic: true,
- restitution: 0.1,
- friction: 2
+ isStatic: true,
+ restitution: 0.1,
+ friction: 2
});
//const wallRight = Matter.Bodies.rectangle(window.innerWidth+50, window.innerHeight/2, 100, window.innerHeight, wallopts);
diff --git a/packages/client/src/scripts/please-login.ts b/packages/client/src/scripts/please-login.ts
index aeaafa124b..e21a6d2ed3 100644
--- a/packages/client/src/scripts/please-login.ts
+++ b/packages/client/src/scripts/please-login.ts
@@ -1,14 +1,21 @@
+import { defineAsyncComponent } from 'vue';
import { $i } from '@/account';
import { i18n } from '@/i18n';
-import { alert } from '@/os';
+import { popup } from '@/os';
-export function pleaseLogin() {
+export function pleaseLogin(path?: string) {
if ($i) return;
- alert({
- title: i18n.ts.signinRequired,
- text: null
- });
+ popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {
+ autoSet: true,
+ message: i18n.ts.signinRequired
+ }, {
+ cancelled: () => {
+ if (path) {
+ window.location.href = path;
+ }
+ },
+ }, 'closed');
throw new Error('signin required');
}
diff --git a/packages/client/src/scripts/popout.ts b/packages/client/src/scripts/popout.ts
index b8286a2a76..580031d0a3 100644
--- a/packages/client/src/scripts/popout.ts
+++ b/packages/client/src/scripts/popout.ts
@@ -1,8 +1,9 @@
import * as config from '@/config';
+import { appendQuery } from './url';
export function popout(path: string, w?: HTMLElement) {
- let url = path.startsWith('http://') || path.startsWith('https://') ? path : config.url + "/" + path;
- url += '?zen';
+ let url = path.startsWith('http://') || path.startsWith('https://') ? path : config.url + path;
+ url = appendQuery(url, 'zen');
if (w) {
const position = w.getBoundingClientRect();
const width = parseInt(getComputedStyle(w, '').width, 10);
diff --git a/packages/client/src/scripts/reaction-picker.ts b/packages/client/src/scripts/reaction-picker.ts
index 3ac1f63430..b7699cae4a 100644
--- a/packages/client/src/scripts/reaction-picker.ts
+++ b/packages/client/src/scripts/reaction-picker.ts
@@ -1,4 +1,4 @@
-import { Ref, ref } from 'vue';
+import { defineAsyncComponent, Ref, ref } from 'vue';
import { popup } from '@/os';
class ReactionPicker {
@@ -12,7 +12,7 @@ class ReactionPicker {
}
public async init() {
- await popup(import('@/components/emoji-picker-dialog.vue'), {
+ await popup(defineAsyncComponent(() => import('@/components/emoji-picker-dialog.vue')), {
src: this.src,
asReactionPicker: true,
manualShowing: this.manualShowing
diff --git a/packages/client/src/scripts/select-file.ts b/packages/client/src/scripts/select-file.ts
index 23df4edf54..461d613b42 100644
--- a/packages/client/src/scripts/select-file.ts
+++ b/packages/client/src/scripts/select-file.ts
@@ -4,6 +4,7 @@ import { stream } from '@/stream';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { DriveFile } from 'misskey-js/built/entities';
+import { uploadFile } from '@/scripts/upload';
function select(src: any, label: string | null, multiple: boolean): Promise<DriveFile | DriveFile[]> {
return new Promise((res, rej) => {
@@ -14,14 +15,14 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
input.type = 'file';
input.multiple = multiple;
input.onchange = () => {
- const promises = Array.from(input.files).map(file => os.upload(file, defaultStore.state.uploadFolder, undefined, keepOriginal.value));
+ const promises = Array.from(input.files).map(file => uploadFile(file, defaultStore.state.uploadFolder, undefined, keepOriginal.value));
Promise.all(promises).then(driveFiles => {
res(multiple ? driveFiles : driveFiles[0]);
- }).catch(e => {
+ }).catch(err => {
os.alert({
type: 'error',
- text: e
+ text: err
});
});
@@ -53,9 +54,9 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
const marker = Math.random().toString(); // TODO: UUIDとか使う
const connection = stream.useChannel('main');
- connection.on('urlUploadFinished', data => {
- if (data.marker === marker) {
- res(multiple ? [data.file] : data.file);
+ connection.on('urlUploadFinished', urlResponse => {
+ if (urlResponse.marker === marker) {
+ res(multiple ? [urlResponse.file] : urlResponse.file);
connection.dispose();
}
});
diff --git a/packages/client/src/scripts/theme-editor.ts b/packages/client/src/scripts/theme-editor.ts
index 3d69d2836a..2c917e280d 100644
--- a/packages/client/src/scripts/theme-editor.ts
+++ b/packages/client/src/scripts/theme-editor.ts
@@ -1,4 +1,4 @@
-import { v4 as uuid} from 'uuid';
+import { v4 as uuid } from 'uuid';
import { themeProps, Theme } from './theme';
diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts
index 2cb78fae5c..dec9fb355c 100644
--- a/packages/client/src/scripts/theme.ts
+++ b/packages/client/src/scripts/theme.ts
@@ -1,5 +1,6 @@
+import { ref } from 'vue';
import { globalEvents } from '@/events';
-import * as tinycolor from 'tinycolor2';
+import tinycolor from 'tinycolor2';
export type Theme = {
id: string;
@@ -10,30 +11,38 @@ export type Theme = {
props: Record<string, string>;
};
-export const lightTheme: Theme = require('@/themes/_light.json5');
-export const darkTheme: Theme = require('@/themes/_dark.json5');
+import lightTheme from '@/themes/_light.json5';
+import darkTheme from '@/themes/_dark.json5';
export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
-export const builtinThemes = [
- require('@/themes/l-light.json5'),
- require('@/themes/l-coffee.json5'),
- require('@/themes/l-apricot.json5'),
- require('@/themes/l-rainy.json5'),
- require('@/themes/l-vivid.json5'),
- require('@/themes/l-cherry.json5'),
- require('@/themes/l-sushi.json5'),
+export const getBuiltinThemes = () => Promise.all(
+ [
+ 'l-light',
+ 'l-coffee',
+ 'l-apricot',
+ 'l-rainy',
+ 'l-vivid',
+ 'l-cherry',
+ 'l-sushi',
- require('@/themes/d-dark.json5'),
- require('@/themes/d-persimmon.json5'),
- require('@/themes/d-astro.json5'),
- require('@/themes/d-future.json5'),
- require('@/themes/d-botanical.json5'),
- require('@/themes/d-cherry.json5'),
- require('@/themes/d-ice.json5'),
- require('@/themes/d-pumpkin.json5'),
- require('@/themes/d-black.json5'),
-] as Theme[];
+ 'd-dark',
+ 'd-persimmon',
+ 'd-astro',
+ 'd-future',
+ 'd-botanical',
+ 'd-cherry',
+ 'd-ice',
+ 'd-pumpkin',
+ 'd-black',
+ ].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default))
+);
+
+export const getBuiltinThemesRef = () => {
+ const builtinThemes = ref<Theme[]>([]);
+ getBuiltinThemes().then(themes => builtinThemes.value = themes);
+ return builtinThemes;
+};
let timeout = null;
diff --git a/packages/client/src/scripts/upload.ts b/packages/client/src/scripts/upload.ts
new file mode 100644
index 0000000000..2f7b30b58d
--- /dev/null
+++ b/packages/client/src/scripts/upload.ts
@@ -0,0 +1,114 @@
+import { reactive, ref } from 'vue';
+import { defaultStore } from '@/store';
+import { apiUrl } from '@/config';
+import * as Misskey from 'misskey-js';
+import { $i } from '@/account';
+import { readAndCompressImage } from 'browser-image-resizer';
+import { alert } from '@/os';
+
+type Uploading = {
+ id: string;
+ name: string;
+ progressMax: number | undefined;
+ progressValue: number | undefined;
+ img: string;
+};
+export const uploads = ref<Uploading[]>([]);
+
+const compressTypeMap = {
+ 'image/jpeg': { quality: 0.85, mimeType: 'image/jpeg' },
+ 'image/webp': { quality: 0.85, mimeType: 'image/jpeg' },
+ 'image/svg+xml': { quality: 1, mimeType: 'image/png' },
+} as const;
+
+const mimeTypeMap = {
+ 'image/webp': 'webp',
+ 'image/jpeg': 'jpg',
+ 'image/png': 'png',
+} as const;
+
+export function uploadFile(
+ file: File,
+ folder?: any,
+ name?: string,
+ keepOriginal: boolean = defaultStore.state.keepOriginalUploading
+): Promise<Misskey.entities.DriveFile> {
+ if (folder && typeof folder === 'object') folder = folder.id;
+
+ return new Promise((resolve, reject) => {
+ const id = Math.random().toString();
+
+ const reader = new FileReader();
+ reader.onload = async (ev) => {
+ const ctx = reactive<Uploading>({
+ id: id,
+ name: name || file.name || 'untitled',
+ progressMax: undefined,
+ progressValue: undefined,
+ img: window.URL.createObjectURL(file)
+ });
+
+ uploads.value.push(ctx);
+
+ let resizedImage: any;
+ if (!keepOriginal && file.type in compressTypeMap) {
+ const imgConfig = compressTypeMap[file.type];
+
+ const config = {
+ maxWidth: 2048,
+ maxHeight: 2048,
+ debug: true,
+ ...imgConfig,
+ };
+
+ try {
+ resizedImage = await readAndCompressImage(file, config);
+ ctx.name = file.type !== imgConfig.mimeType ? `${ctx.name}.${mimeTypeMap[compressTypeMap[file.type].mimeType]}` : ctx.name;
+ } catch (err) {
+ console.error('Failed to resize image', err);
+ }
+ }
+
+ const formData = new FormData();
+ 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);
+
+ const xhr = new XMLHttpRequest();
+ xhr.open('POST', apiUrl + '/drive/files/create', true);
+ xhr.onload = (ev) => {
+ if (xhr.status !== 200 || ev.target == null || ev.target.response == null) {
+ // TODO: 消すのではなくて再送できるようにしたい
+ uploads.value = uploads.value.filter(x => x.id !== id);
+
+ alert({
+ type: 'error',
+ title: 'Failed to upload',
+ text: `${JSON.stringify(ev.target?.response)}, ${JSON.stringify(xhr.response)}`
+ });
+
+ reject();
+ return;
+ }
+
+ const driveFile = JSON.parse(ev.target.response);
+
+ resolve(driveFile);
+
+ uploads.value = uploads.value.filter(x => x.id !== id);
+ };
+
+ xhr.upload.onprogress = ev => {
+ if (ev.lengthComputable) {
+ ctx.progressMax = ev.total;
+ ctx.progressValue = ev.loaded;
+ }
+ };
+
+ xhr.send(formData);
+ };
+ reader.readAsArrayBuffer(file);
+ });
+}
diff --git a/packages/client/src/scripts/url.ts b/packages/client/src/scripts/url.ts
index c7f2b7c1e7..542b00e0f0 100644
--- a/packages/client/src/scripts/url.ts
+++ b/packages/client/src/scripts/url.ts
@@ -4,7 +4,7 @@ export function query(obj: {}): string {
.reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>);
return Object.entries(params)
- .map((e) => `${e[0]}=${encodeURIComponent(e[1])}`)
+ .map((p) => `${p[0]}=${encodeURIComponent(p[1])}`)
.join('&');
}
diff --git a/packages/client/src/scripts/use-note-capture.ts b/packages/client/src/scripts/use-note-capture.ts
index b2a96062c7..f1f976693e 100644
--- a/packages/client/src/scripts/use-note-capture.ts
+++ b/packages/client/src/scripts/use-note-capture.ts
@@ -11,8 +11,8 @@ export function useNoteCapture(props: {
const note = props.note;
const connection = $i ? stream : null;
- function onStreamNoteUpdated(data): void {
- const { type, id, body } = data;
+ function onStreamNoteUpdated(noteData): void {
+ const { type, id, body } = noteData;
if (id !== note.value.id) return;