Merge branch 'develop' into report
This commit is contained in:
commit
8f73d4b567
40 changed files with 704 additions and 428 deletions
|
|
@ -131,6 +131,10 @@ function onMousedown(evt: MouseEvent): void {
|
|||
box-sizing: border-box;
|
||||
transition: background 0.1s ease;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background: var(--buttonHoverBg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div :class="$style.codeEditorScroller">
|
||||
<textarea
|
||||
ref="inputEl"
|
||||
v-model="vModel"
|
||||
v-model="v"
|
||||
:class="[$style.textarea]"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
|
|
@ -58,7 +58,6 @@ const emit = defineEmits<{
|
|||
}>();
|
||||
|
||||
const { modelValue } = toRefs(props);
|
||||
const vModel = ref<string>(modelValue.value ?? '');
|
||||
const v = ref<string>(modelValue.value ?? '');
|
||||
const focused = ref(false);
|
||||
const changed = ref(false);
|
||||
|
|
@ -79,15 +78,14 @@ const onKeydown = (ev: KeyboardEvent) => {
|
|||
|
||||
if (ev.code === 'Enter') {
|
||||
const pos = inputEl.value?.selectionStart ?? 0;
|
||||
const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length;
|
||||
const posEnd = inputEl.value?.selectionEnd ?? v.value.length;
|
||||
if (pos === posEnd) {
|
||||
const lines = vModel.value.slice(0, pos).split('\n');
|
||||
const lines = v.value.slice(0, pos).split('\n');
|
||||
const currentLine = lines[lines.length - 1];
|
||||
const currentLineSpaces = currentLine.match(/^\s+/);
|
||||
const posDelta = currentLineSpaces ? currentLineSpaces[0].length : 0;
|
||||
ev.preventDefault();
|
||||
vModel.value = vModel.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + vModel.value.slice(pos);
|
||||
v.value = vModel.value;
|
||||
v.value = v.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + v.value.slice(pos);
|
||||
nextTick(() => {
|
||||
inputEl.value?.setSelectionRange(pos + 1 + posDelta, pos + 1 + posDelta);
|
||||
});
|
||||
|
|
@ -97,9 +95,8 @@ const onKeydown = (ev: KeyboardEvent) => {
|
|||
|
||||
if (ev.key === 'Tab') {
|
||||
const pos = inputEl.value?.selectionStart ?? 0;
|
||||
const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length;
|
||||
vModel.value = vModel.value.slice(0, pos) + '\t' + vModel.value.slice(posEnd);
|
||||
v.value = vModel.value;
|
||||
const posEnd = inputEl.value?.selectionEnd ?? v.value.length;
|
||||
v.value = v.value.slice(0, pos) + '\t' + v.value.slice(posEnd);
|
||||
nextTick(() => {
|
||||
inputEl.value?.setSelectionRange(pos + 1, pos + 1);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, watch } from 'vue';
|
||||
import gsap from 'gsap';
|
||||
import number from '@/filters/number.js';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
@ -20,8 +19,24 @@ const tweened = reactive({
|
|||
number: 0,
|
||||
});
|
||||
|
||||
watch(() => props.value, (n) => {
|
||||
gsap.to(tweened, { duration: 1, number: Number(n) || 0 });
|
||||
watch(() => props.value, (to, from) => {
|
||||
// requestAnimationFrameを利用して、500msでfromからtoまでを1次関数的に変化させる
|
||||
let start: number | null = null;
|
||||
|
||||
function step(timestamp: number) {
|
||||
if (start === null) {
|
||||
start = timestamp;
|
||||
}
|
||||
const elapsed = timestamp - start;
|
||||
tweened.number = (from ?? 0) + (to - (from ?? 0)) * elapsed / 500;
|
||||
if (elapsed < 500) {
|
||||
window.requestAnimationFrame(step);
|
||||
} else {
|
||||
tweened.number = to;
|
||||
}
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(step);
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -752,7 +752,17 @@ async function post(ev?: MouseEvent) {
|
|||
|
||||
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
|
||||
const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
|
||||
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
|
||||
if (!postData.text) {
|
||||
postData.text = hashtags_;
|
||||
} else {
|
||||
const postTextLines = postData.text.split('\n');
|
||||
if (postTextLines[postTextLines.length - 1].trim() === '') {
|
||||
postTextLines[postTextLines.length - 1] += hashtags_;
|
||||
} else {
|
||||
postTextLines[postTextLines.length - 1] += ' ' + hashtags_;
|
||||
}
|
||||
postData.text = postTextLines.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
// plugin
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, onUnmounted, ref } from 'vue';
|
||||
import type { summaly } from 'summaly';
|
||||
import type { summaly } from '@misskey-dev/summaly';
|
||||
import { url as local } from '@/config.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
|
|
|
|||
|
|
@ -80,6 +80,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>Verifymail.io API Auth Key</template>
|
||||
</MkInput>
|
||||
<MkSwitch v-model="enableTruemailApi" @update:modelValue="save">
|
||||
<template #label>Use TrueMail API</template>
|
||||
</MkSwitch>
|
||||
<MkInput v-model="truemailInstance" @update:modelValue="save">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>TrueMail API Instance</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="truemailAuthKey" @update:modelValue="save">
|
||||
<template #prefix><i class="ti ti-key"></i></template>
|
||||
<template #label>TrueMail API Auth Key</template>
|
||||
</MkInput>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
|
|
@ -153,6 +164,9 @@ const enableIpLogging = ref<boolean>(false);
|
|||
const enableActiveEmailValidation = ref<boolean>(false);
|
||||
const enableVerifymailApi = ref<boolean>(false);
|
||||
const verifymailAuthKey = ref<string | null>(null);
|
||||
const enableTruemailApi = ref<boolean>(false);
|
||||
const truemailInstance = ref<string | null>(null);
|
||||
const truemailAuthKey = ref<string | null>(null);
|
||||
const bannedEmailDomains = ref<string>('');
|
||||
|
||||
async function init() {
|
||||
|
|
@ -194,6 +208,9 @@ function save() {
|
|||
enableActiveEmailValidation: enableActiveEmailValidation.value,
|
||||
enableVerifymailApi: enableVerifymailApi.value,
|
||||
verifymailAuthKey: verifymailAuthKey.value,
|
||||
enableTruemailApi: enableTruemailApi.value,
|
||||
truemailInstance: truemailInstance.value,
|
||||
truemailAuthKey: truemailAuthKey.value,
|
||||
bannedEmailDomains: bannedEmailDomains.value.split('\n'),
|
||||
}).then(() => {
|
||||
fetchInstance();
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div v-else-if="tab === 'search'">
|
||||
<div class="_gaps">
|
||||
<div>
|
||||
<MkInput v-model="searchQuery">
|
||||
<MkInput v-model="searchQuery" @enter="search()">
|
||||
<template #prefix><i class="ti ti-search"></i></template>
|
||||
</MkInput>
|
||||
<MkButton primary rounded style="margin-top: 8px;" @click="search()">{{ i18n.ts.search }}</MkButton>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkSpacer :contentMax="700">
|
||||
<div v-if="tab === 'search'">
|
||||
<div class="_gaps">
|
||||
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search">
|
||||
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
|
||||
<template #prefix><i class="ti ti-search"></i></template>
|
||||
</MkInput>
|
||||
<MkRadios v-model="searchType" @update:modelValue="search()">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<div class="_gaps">
|
||||
<div class="_gaps">
|
||||
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search">
|
||||
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
|
||||
<template #prefix><i class="ti ti-search"></i></template>
|
||||
</MkInput>
|
||||
<MkFolder>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<div class="_gaps">
|
||||
<div class="_gaps">
|
||||
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search">
|
||||
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
|
||||
<template #prefix><i class="ti ti-search"></i></template>
|
||||
</MkInput>
|
||||
<MkRadios v-model="searchOrigin" @update:modelValue="search()">
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ export class Storage<T extends StateDef> {
|
|||
this.reactiveState[key].value = this.state[key] = rawValue;
|
||||
|
||||
return this.addIdbSetJob(async () => {
|
||||
if (_DEV_) console.log(`set ${key} start`);
|
||||
if (_DEV_) console.log(`set ${String(key)} start`);
|
||||
switch (this.def[key].where) {
|
||||
case 'device': {
|
||||
this.pizzaxChannel.postMessage({
|
||||
|
|
@ -207,7 +207,7 @@ export class Storage<T extends StateDef> {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (_DEV_) console.log(`set ${key} complete`);
|
||||
if (_DEV_) console.log(`set ${String(key)} complete`);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { reactive, ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { readAndCompressImage } from 'browser-image-resizer';
|
||||
import { readAndCompressImage } from '@misskey-dev/browser-image-resizer';
|
||||
import { getCompressionConfig } from './upload/compress-config.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { apiUrl } from '@/config.js';
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import isAnimated from 'is-file-animated';
|
||||
import { isWebpSupported } from './isWebpSupported.js';
|
||||
import type { BrowserImageResizerConfig } from 'browser-image-resizer';
|
||||
import type { BrowserImageResizerConfigWithConvertedOutput } from '@misskey-dev/browser-image-resizer';
|
||||
|
||||
const compressTypeMap = {
|
||||
'image/jpeg': { quality: 0.90, mimeType: 'image/webp' },
|
||||
|
|
@ -21,7 +21,7 @@ const compressTypeMapFallback = {
|
|||
'image/svg+xml': { quality: 1, mimeType: 'image/png' },
|
||||
} as const;
|
||||
|
||||
export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfig | undefined> {
|
||||
export async function getCompressionConfig(file: File): Promise<BrowserImageResizerConfigWithConvertedOutput | undefined> {
|
||||
const imgConfig = (isWebpSupported() ? compressTypeMap : compressTypeMapFallback)[file.type];
|
||||
if (!imgConfig || await isAnimated(file)) {
|
||||
return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue