This commit is contained in:
syuilo 2023-02-22 15:28:17 +09:00
parent 3bb7afe544
commit 0fb9c372dd
22 changed files with 69 additions and 71 deletions

View file

@ -55,6 +55,7 @@ module.exports = {
'vue/multi-word-component-names': 'warn',
'vue/require-v-for-key': 'warn',
'vue/no-unused-components': 'warn',
'vue/no-unused-vars': 'warn',
'vue/valid-v-for': 'warn',
'vue/return-in-computed-property': 'warn',
'vue/no-setup-props-destructure': 'warn',

View file

@ -43,7 +43,7 @@ const emit = defineEmits<{
}>();
const uiWindow = shallowRef<InstanceType<typeof MkWindow>>();
const comment = ref(props.initialComment || '');
const comment = ref(props.initialComment ?? '');
function send() {
os.apiWithDialog('users/report-abuse', {

View file

@ -209,7 +209,7 @@ function exec() {
}
} else if (props.type === 'hashtag') {
if (!props.q || props.q === '') {
hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') || '[]');
hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]');
fetching.value = false;
} else {
const cacheKey = `autocomplete:hashtag:${props.q}`;

View file

@ -69,7 +69,7 @@ const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown
if (loaded) {
available.value = true;
} else {
(document.getElementById(scriptId.value) || document.head.appendChild(Object.assign(document.createElement('script'), {
(document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), {
async: true,
id: scriptId.value,
src: src.value,

View file

@ -45,8 +45,8 @@ onMounted(() => {
src: media.url,
w: media.properties.width,
h: media.properties.height,
alt: media.comment || media.name,
comment: media.comment || media.name,
alt: media.comment ?? media.name,
comment: media.comment ?? media.name,
};
if (media.properties.orientation != null && media.properties.orientation >= 5) {
[item.w, item.h] = [item.h, item.w];
@ -90,8 +90,8 @@ onMounted(() => {
[itemData.w, itemData.h] = [itemData.h, itemData.w];
}
itemData.msrc = file.thumbnailUrl;
itemData.alt = file.comment || file.name;
itemData.comment = file.comment || file.name;
itemData.alt = file.comment ?? file.name;
itemData.comment = file.comment ?? file.name;
itemData.thumbCropped = true;
});

View file

@ -54,7 +54,7 @@ const props = withDefaults(defineProps<{
showGlobalToggle: true,
});
let includingTypes = $computed(() => props.includingTypes || []);
let includingTypes = $computed(() => props.includingTypes ?? []);
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();

View file

@ -104,7 +104,7 @@ const {
enableInfiniteScroll,
} = defaultStore.reactiveState;
const contentEl = $computed(() => props.pagination.pageEl || rootEl);
const contentEl = $computed(() => props.pagination.pageEl ?? rootEl);
const scrollableElement = $computed(() => getScrollContainer(contentEl));
//

View file

@ -154,7 +154,7 @@ let autocomplete = $ref(null);
let draghover = $ref(false);
let quoteId = $ref(null);
let hasNotSpecifiedMentions = $ref(false);
let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') || '[]'));
let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
let imeText = $ref('');
const draftKey = $computed((): string => {
@ -533,7 +533,7 @@ function onDrop(ev): void {
}
function saveDraft() {
const draftData = JSON.parse(miLocalStorage.getItem('drafts') || '{}');
const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
draftData[draftKey] = {
updatedAt: new Date(),
@ -642,7 +642,7 @@ async function post(ev?: MouseEvent) {
emit('posted');
if (postData.text && postData.text !== '') {
const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag);
const history = JSON.parse(miLocalStorage.getItem('hashtags') || '[]') as string[];
const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[];
miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
}
posting = false;
@ -746,7 +746,7 @@ onMounted(() => {
nextTick(() => {
// 稿
if (!props.instant && !props.mention && !props.specified) {
const draft = JSON.parse(miLocalStorage.getItem('drafts') || '{}')[draftKey];
const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey];
if (draft) {
text = draft.data.text;
useCw = draft.data.useCw;

View file

@ -16,7 +16,7 @@
<template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template>
</MkInput>
<MkInput v-model="host" @update:model-value="search" :datalist="[hostname]">
<MkInput v-model="host" :datalist="[hostname]" @update:model-value="search">
<template #label>{{ i18n.ts.host }}</template>
<template #prefix>@</template>
</MkInput>

View file

@ -24,7 +24,7 @@ const rawUrl = computed(() => {
return props.url;
}
if (props.host == null && !customEmojiName.value.includes('@')) {
return customEmojis.value.find(x => x.name === customEmojiName.value)?.url || null;
return customEmojis.value.find(x => x.name === customEmojiName.value)?.url ?? null;
}
return props.host ? `/emoji/${customEmojiName.value}@${props.host}.webp` : `/emoji/${customEmojiName.value}.webp`;
});
@ -32,7 +32,7 @@ const rawUrl = computed(() => {
const url = computed(() =>
defaultStore.reactiveState.disableShowingAnimatedImages.value && rawUrl.value
? getStaticImageUrl(rawUrl.value)
: rawUrl.value
: rawUrl.value,
);
const alt = computed(() => `:${customEmojiName.value}:`);

View file

@ -2,9 +2,9 @@
<div v-if="show" ref="el" :class="[$style.root]" :style="{ background: bg }">
<div :class="[$style.upper, { [$style.slim]: narrow, [$style.thin]: thin_ }]">
<div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu">
<MkAvatar :class="$style.avatar" :user="$i" />
<MkAvatar :class="$style.avatar" :user="$i"/>
</div>
<div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft" />
<div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/>
<template v-if="metadata">
<div v-if="!hideTitle" :class="$style.titleContainer" @click="top">
@ -36,11 +36,11 @@
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, inject } from 'vue';
import tinycolor from 'tinycolor2';
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
import { scrollToTop } from '@/scripts/scroll';
import { globalEvents } from '@/events';
import { injectPageMetadata } from '@/scripts/page-metadata';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
import XTabs, { Tab } from './MkPageHeader.tabs.vue';
const props = withDefaults(defineProps<{
tabs?: Tab[];
@ -96,7 +96,7 @@ function onTabClick(): void {
}
const calcBg = () => {
const rawBg = metadata?.bg || 'var(--bg)';
const rawBg = metadata?.bg ?? 'var(--bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString();

View file

@ -113,7 +113,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void {
}
const calcBg = () => {
const rawBg = metadata?.bg || 'var(--bg)';
const rawBg = metadata?.bg ?? 'var(--bg)';
const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
tinyBg.setAlpha(0.85);
bg.value = tinyBg.toRgbString();

View file

@ -1,12 +1,12 @@
<template>
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="500">
<div v-if="state == 'fetch-session-error'">
<p>{{ i18n.ts.somethingHappened }}</p>
</div>
<div v-else-if="$i && !session">
<MkLoading />
<MkLoading/>
</div>
<div v-else-if="$i && session">
<XForm
@ -21,15 +21,16 @@
</div>
<div v-if="state == 'accepted' && session">
<h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1>
<p v-if="session.app.callbackUrl">{{ i18n.ts._auth.callback }}
<MkEllipsis />
<p v-if="session.app.callbackUrl">
{{ i18n.ts._auth.callback }}
<MkEllipsis/>
</p>
<p v-if="!session.app.callbackUrl">{{ i18n.ts._auth.pleaseGoBack }}</p>
</div>
</div>
<div v-else>
<p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p>
<MkSignin @login="onLogin" />
<MkSignin @login="onLogin"/>
</div>
</MkSpacer>
</MkStickyContainer>
@ -37,12 +38,12 @@
<script lang="ts" setup>
import { onMounted } from 'vue';
import { AuthSession } from 'misskey-js/built/entities';
import XForm from './auth.form.vue';
import MkSignin from '@/components/MkSignin.vue';
import * as os from '@/os';
import { $i, login } from '@/account';
import { definePageMetadata } from '@/scripts/page-metadata';
import { AuthSession } from 'misskey-js/built/entities';
import { i18n } from '@/i18n';
const props = defineProps<{
@ -82,7 +83,7 @@ onMounted(async () => {
} else {
state = 'waiting';
}
} catch (e) {
} catch (err) {
state = 'fetch-session-error';
}
});

View file

@ -124,11 +124,11 @@ function saveFields() {
function save() {
os.apiWithDialog('i/update', {
name: profile.name || null,
description: profile.description || null,
location: profile.location || null,
birthday: profile.birthday || null,
lang: profile.lang || null,
name: profile.name ?? null,
description: profile.description ?? null,
location: profile.location ?? null,
birthday: profile.birthday ?? null,
lang: profile.lang ?? null,
isBot: !!profile.isBot,
isCat: !!profile.isCat,
showTimelineReplies: !!profile.showTimelineReplies,

View file

@ -48,8 +48,8 @@ export class Storage<T extends StateDef> {
// 簡易的にキューイングして占有ロックとする
private currentIdbJob: Promise<any> = Promise.resolve();
private addIdbSetJob<T>(job: () => Promise<T>) {
const promise = this.currentIdbJob.then(job, e => {
console.error('Pizzax failed to save data to idb!', e);
const promise = this.currentIdbJob.then(job, err => {
console.error('Pizzax failed to save data to idb!', err);
return job();
});
this.currentIdbJob = promise;
@ -130,22 +130,22 @@ export class Storage<T extends StateDef> {
await defaultStore.ready;
api('i/registry/get-all', { scope: ['client', this.key] })
.then(kvs => {
const cache: Partial<T> = {};
for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) {
if (v.where === 'account') {
if (Object.prototype.hasOwnProperty.call(kvs, k)) {
this.reactiveState[k].value = this.state[k] = (kvs as Partial<T>)[k];
cache[k] = (kvs as Partial<T>)[k];
} else {
this.reactiveState[k].value = this.state[k] = v.default;
.then(kvs => {
const cache: Partial<T> = {};
for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) {
if (v.where === 'account') {
if (Object.prototype.hasOwnProperty.call(kvs, k)) {
this.reactiveState[k].value = this.state[k] = (kvs as Partial<T>)[k];
cache[k] = (kvs as Partial<T>)[k];
} else {
this.reactiveState[k].value = this.state[k] = v.default;
}
}
}
}
return set(this.registryCacheKeyName, cache);
})
.then(() => resolve());
return set(this.registryCacheKeyName, cache);
})
.then(() => resolve());
}, 1);
} else {
resolve();

View file

@ -240,7 +240,7 @@ export function getNoteMenu(props: {
icon: 'ti ti-external-link',
text: i18n.ts.showOnRemote,
action: () => {
window.open(appearNote.url || appearNote.uri, '_blank');
window.open(appearNote.url ?? appearNote.uri, '_blank');
},
} : undefined,
{
@ -302,7 +302,7 @@ export function getNoteMenu(props: {
icon: 'ti ti-exclamation-circle',
text: i18n.ts.reportAbuse,
action: () => {
const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`;
const u = appearNote.url ?? appearNote.uri ?? `${url}/notes/${appearNote.id}`;
os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
user: appearNote.user,
initialComment: `Note: ${u}\n-----\n`,
@ -344,7 +344,7 @@ export function getNoteMenu(props: {
icon: 'ti ti-external-link',
text: i18n.ts.showOnRemote,
action: () => {
window.open(appearNote.url || appearNote.uri, '_blank');
window.open(appearNote.url ?? appearNote.uri, '_blank');
},
} : undefined]
.filter(x => x !== undefined);

View file

@ -58,7 +58,7 @@ export class HpmlScope {
constructor(layerdStates: HpmlScope['layerdStates'], name?: HpmlScope['name']) {
this.layerdStates = layerdStates;
this.name = name || 'anonymous';
this.name = name ?? 'anonymous';
}
@autobind

View file

@ -63,7 +63,7 @@ export class HpmlTypeChecker {
@autobind
public getExpectedType(v: Expr, slot: number): Type {
const def = funcDefs[v.type || ''];
const def = funcDefs[v.type ?? ''];
if (def == null) {
throw new Error('Unknown type: ' + v.type);
}
@ -107,7 +107,7 @@ export class HpmlTypeChecker {
return pageVar.type;
}
const envVar = envVarsDef[v.value || ''];
const envVar = envVarsDef[v.value ?? ''];
if (envVar !== undefined) {
return envVar;
}

View file

@ -10,7 +10,7 @@ export function getScrollContainer(el: HTMLElement | null): HTMLElement | null {
}
}
export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top: number = 0) {
export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top = 0) {
if (!el.parentElement) return top;
const data = el.dataset.stickyContainerHeaderHeight;
const newTop = data ? Number(data) + top : top;
@ -23,14 +23,14 @@ export function getScrollPosition(el: HTMLElement | null): number {
return container == null ? window.scrollY : container.scrollTop;
}
export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance: number = 1, once: boolean = false) {
export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) {
// とりあえず評価してみる
if (isTopVisible(el)) {
cb();
if (once) return null;
}
const container = getScrollContainer(el) || window;
const container = getScrollContainer(el) ?? window;
const onScroll = ev => {
if (!document.body.contains(el)) return;
@ -45,7 +45,7 @@ export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance: numbe
return removeListener;
}
export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance: number = 1, once: boolean = false) {
export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) {
const container = getScrollContainer(el);
// とりあえず評価してみる
@ -54,7 +54,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance: nu
if (once) return null;
}
const containerOrWindow = container || window;
const containerOrWindow = container ?? window;
const onScroll = ev => {
if (!document.body.contains(el)) return;
if (isBottomVisible(el, 1, container)) {
@ -104,12 +104,12 @@ export function scrollToBottom(
} else {
window.scroll({
top: (el.scrollHeight - window.innerHeight + getStickyTop(el, container) + (window.innerWidth <= 500 ? 96 : 0)) || 0,
...options
...options,
});
}
}
export function isTopVisible(el: HTMLElement, tolerance: number = 1): boolean {
export function isTopVisible(el: HTMLElement, tolerance = 1): boolean {
const scrollTop = getScrollPosition(el);
return scrollTop <= tolerance;
}
@ -124,6 +124,6 @@ export function getBodyScrollHeight() {
return Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
document.body.clientHeight, document.documentElement.clientHeight,
);
}

View file

@ -1,13 +1,13 @@
import { api } from '@/os';
import { $i } from '@/account';
import { Theme } from './scripts/theme';
import { miLocalStorage } from './local-storage';
import { api } from '@/os';
import { $i } from '@/account';
const lsCacheKey = $i ? `themes:${$i.id}` as const : null;
export function getThemes(): Theme[] {
if ($i == null) return [];
return JSON.parse(miLocalStorage.getItem(lsCacheKey!) || '[]');
return JSON.parse(miLocalStorage.getItem(lsCacheKey!) ?? '[]');
}
export async function fetchThemes(): Promise<void> {

View file

@ -125,7 +125,7 @@ function onAiClick(ev) {
if (window.innerWidth < 1024) {
const currentUI = miLocalStorage.getItem('ui');
miLocalStorage.setItem('ui_temp', currentUI || 'default');
miLocalStorage.setItem('ui_temp', currentUI ?? 'default');
miLocalStorage.setItem('ui', 'default');
location.reload();
}

View file

@ -1,7 +1,3 @@
export default function(user: { name?: string | null, username: string }): string {
// Show username if name is empty.
// XXX: typescript-eslint has no configuration to allow using `||` against string.
// https://github.com/typescript-eslint/typescript-eslint/issues/4906
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return user.name || user.username;
return user.name === '' ? user.username : user.name ?? user.username;
}