Merge branch 'main' into feature/search-drive-by-alt-text
Some checks failed
Dockle / dockle (pull_request) Has been cancelled
Lint / pnpm_install (pull_request) Has been cancelled
Lint / lint (backend) (pull_request) Has been cancelled
Lint / lint (frontend) (pull_request) Has been cancelled
Lint / lint (misskey-js) (pull_request) Has been cancelled
Lint / lint (sw) (pull_request) Has been cancelled
Lint / typecheck (backend) (pull_request) Has been cancelled
Lint / typecheck (misskey-js) (pull_request) Has been cancelled
Test (backend) / unit (22.x) (pull_request) Has been cancelled
Test (backend) / e2e (22.x) (pull_request) Has been cancelled
Test (frontend) / vitest (22.x) (pull_request) Has been cancelled
Test (production install and build) / production (22.x) (pull_request) Has been cancelled
Test (backend) / validate-api-json (22.x) (pull_request) Has been cancelled
Pull Request Labeler / triage (pull_request_target) Has been cancelled
Some checks failed
Dockle / dockle (pull_request) Has been cancelled
Lint / pnpm_install (pull_request) Has been cancelled
Lint / lint (backend) (pull_request) Has been cancelled
Lint / lint (frontend) (pull_request) Has been cancelled
Lint / lint (misskey-js) (pull_request) Has been cancelled
Lint / lint (sw) (pull_request) Has been cancelled
Lint / typecheck (backend) (pull_request) Has been cancelled
Lint / typecheck (misskey-js) (pull_request) Has been cancelled
Test (backend) / unit (22.x) (pull_request) Has been cancelled
Test (backend) / e2e (22.x) (pull_request) Has been cancelled
Test (frontend) / vitest (22.x) (pull_request) Has been cancelled
Test (production install and build) / production (22.x) (pull_request) Has been cancelled
Test (backend) / validate-api-json (22.x) (pull_request) Has been cancelled
Pull Request Labeler / triage (pull_request_target) Has been cancelled
This commit is contained in:
commit
c2a9d3252a
10 changed files with 99 additions and 5 deletions
|
@ -529,6 +529,7 @@ mediaListWithOneImageAppearance: "Height of media lists with one image only"
|
||||||
limitTo: "Limit to {x}"
|
limitTo: "Limit to {x}"
|
||||||
noFollowRequests: "You don't have any pending follow requests"
|
noFollowRequests: "You don't have any pending follow requests"
|
||||||
openImageInNewTab: "Open images in new tab"
|
openImageInNewTab: "Open images in new tab"
|
||||||
|
warnForMissingAltText: "Warn you when you forget to put alt text"
|
||||||
dashboard: "Dashboard"
|
dashboard: "Dashboard"
|
||||||
local: "Local"
|
local: "Local"
|
||||||
remote: "Remote"
|
remote: "Remote"
|
||||||
|
@ -1046,6 +1047,9 @@ thisPostMayBeAnnoying: "This note may annoy others."
|
||||||
thisPostMayBeAnnoyingHome: "Post to home timeline"
|
thisPostMayBeAnnoyingHome: "Post to home timeline"
|
||||||
thisPostMayBeAnnoyingCancel: "Cancel"
|
thisPostMayBeAnnoyingCancel: "Cancel"
|
||||||
thisPostMayBeAnnoyingIgnore: "Post anyway"
|
thisPostMayBeAnnoyingIgnore: "Post anyway"
|
||||||
|
thisPostIsMissingAltTextCancel: "Cancel"
|
||||||
|
thisPostIsMissingAltTextIgnore: "Post anyway"
|
||||||
|
thisPostIsMissingAltText: "One of the files attached to this post is missing alt text. Please ensure all the attachments have alt text."
|
||||||
collapseRenotes: "Collapse renotes you've already seen"
|
collapseRenotes: "Collapse renotes you've already seen"
|
||||||
internalServerError: "Internal Server Error"
|
internalServerError: "Internal Server Error"
|
||||||
internalServerErrorDescription: "The server has run into an unexpected error."
|
internalServerErrorDescription: "The server has run into an unexpected error."
|
||||||
|
|
12
locales/index.d.ts
vendored
12
locales/index.d.ts
vendored
|
@ -4211,6 +4211,18 @@ export interface Locale extends ILocale {
|
||||||
* このまま投稿
|
* このまま投稿
|
||||||
*/
|
*/
|
||||||
"thisPostMayBeAnnoyingIgnore": string;
|
"thisPostMayBeAnnoyingIgnore": string;
|
||||||
|
/**
|
||||||
|
* やめる
|
||||||
|
*/
|
||||||
|
"thisPostIsMissingAltTextCancel": string;
|
||||||
|
/**
|
||||||
|
* このまま投稿
|
||||||
|
*/
|
||||||
|
"thisPostIsMissingAltTextIgnore": string;
|
||||||
|
/**
|
||||||
|
* この投稿に添付されたファイルの 1 つに代替テキストがありません。すべての添付ファイルに代替テキストが含まれていることを確認してください。
|
||||||
|
*/
|
||||||
|
"thisPostIsMissingAltText": string;
|
||||||
/**
|
/**
|
||||||
* 見たことのあるリノートを省略して表示
|
* 見たことのあるリノートを省略して表示
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1048,6 +1048,9 @@ thisPostMayBeAnnoying: "この投稿は迷惑になる可能性があります
|
||||||
thisPostMayBeAnnoyingHome: "ホームに投稿"
|
thisPostMayBeAnnoyingHome: "ホームに投稿"
|
||||||
thisPostMayBeAnnoyingCancel: "やめる"
|
thisPostMayBeAnnoyingCancel: "やめる"
|
||||||
thisPostMayBeAnnoyingIgnore: "このまま投稿"
|
thisPostMayBeAnnoyingIgnore: "このまま投稿"
|
||||||
|
thisPostIsMissingAltTextCancel: "やめる"
|
||||||
|
thisPostIsMissingAltTextIgnore: "このまま投稿"
|
||||||
|
thisPostIsMissingAltText: "この投稿に添付されたファイルの 1 つに代替テキストがありません。すべての添付ファイルに代替テキストが含まれていることを確認してください。"
|
||||||
collapseRenotes: "見たことのあるリノートを省略して表示"
|
collapseRenotes: "見たことのあるリノートを省略して表示"
|
||||||
internalServerError: "サーバー内部エラー"
|
internalServerError: "サーバー内部エラー"
|
||||||
internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。"
|
internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。"
|
||||||
|
|
|
@ -101,7 +101,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, watch, nextTick, onMounted, onUnmounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue';
|
import { inject, watch, nextTick, onMounted, onUnmounted, defineAsyncComponent, provide, shallowRef, ref, computed, toRaw } from 'vue';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||||
|
@ -760,6 +760,31 @@ async function post(ev?: MouseEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (defaultStore.state.warnMissingAltText) {
|
||||||
|
const filesData = toRaw(files.value);
|
||||||
|
|
||||||
|
const isMissingAltText = filesData.filter(
|
||||||
|
file => file.type.startsWith('image/') || file.type.startsWith('video/') || file.type.startsWith('audio/'),
|
||||||
|
).some(file => !file.comment);
|
||||||
|
|
||||||
|
if (isMissingAltText) {
|
||||||
|
const { canceled, result } = await os.actions({
|
||||||
|
type: 'warning',
|
||||||
|
text: i18n.ts.thisPostIsMissingAltText,
|
||||||
|
actions: [{
|
||||||
|
value: 'cancel',
|
||||||
|
text: i18n.ts.thisPostIsMissingAltTextCancel,
|
||||||
|
}, {
|
||||||
|
value: 'ignore',
|
||||||
|
text: i18n.ts.thisPostIsMissingAltTextIgnore,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (canceled) return;
|
||||||
|
if (result === 'cancel') return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let postData = {
|
let postData = {
|
||||||
text: text.value === '' ? null : text.value,
|
text: text.value === '' ? null : text.value,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
@ -874,9 +899,9 @@ async function post(ev?: MouseEvent) {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
text: err.message + '\n' + (err as any).id,
|
text: err.message + '\n' + (err as any).id,
|
||||||
});
|
});
|
||||||
emit("postError");
|
emit('postError');
|
||||||
});
|
});
|
||||||
emit("posting");
|
emit('posting');
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
|
@ -893,7 +918,7 @@ async function insertEmoji(ev: MouseEvent) {
|
||||||
os.openEmojiPicker(
|
os.openEmojiPicker(
|
||||||
(ev.currentTarget ?? ev.target) as HTMLElement,
|
(ev.currentTarget ?? ev.target) as HTMLElement,
|
||||||
{ asReactionPicker: false },
|
{ asReactionPicker: false },
|
||||||
textareaEl.value
|
textareaEl.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -227,6 +227,22 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
||||||
}
|
}
|
||||||
return h(MkSparkle, {}, genEl(token.children, scale));
|
return h(MkSparkle, {}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
case 'fade': {
|
||||||
|
// Dont run with reduced motion on
|
||||||
|
if (!defaultStore.state.animation) {
|
||||||
|
style = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const direction = token.props.args.out
|
||||||
|
? 'alternate-reverse'
|
||||||
|
: 'alternate';
|
||||||
|
const speed = validTime(token.props.args.speed) ?? '1.5s';
|
||||||
|
const delay = validTime(token.props.args.delay) ?? '0s';
|
||||||
|
const loop = safeParseFloat(token.props.args.loop) ?? 'infinite';
|
||||||
|
style = `animation: mfm-fade ${speed} ${delay} linear ${loop}; animation-direction: ${direction};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'rotate': {
|
case 'rotate': {
|
||||||
const degrees = safeParseFloat(token.props.args.deg) ?? 90;
|
const degrees = safeParseFloat(token.props.args.deg) ?? 90;
|
||||||
style = `transform: rotate(${degrees}deg); transform-origin: center center;`;
|
style = `transform: rotate(${degrees}deg); transform-origin: center center;`;
|
||||||
|
@ -239,6 +255,22 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
|
||||||
style = `transform: translateX(${x}em) translateY(${y}em);`;
|
style = `transform: translateX(${x}em) translateY(${y}em);`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'crop': {
|
||||||
|
const top = Number.parseFloat(
|
||||||
|
(token.props.args.top ?? '0').toString(),
|
||||||
|
);
|
||||||
|
const right = Number.parseFloat(
|
||||||
|
(token.props.args.right ?? '0').toString(),
|
||||||
|
);
|
||||||
|
const bottom = Number.parseFloat(
|
||||||
|
(token.props.args.bottom ?? '0').toString(),
|
||||||
|
);
|
||||||
|
const left = Number.parseFloat(
|
||||||
|
(token.props.args.left ?? '0').toString(),
|
||||||
|
);
|
||||||
|
style = `clip-path: inset(${top}% ${right}% ${bottom}% ${left}%);`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'scale': {
|
case 'scale': {
|
||||||
if (!defaultStore.state.advancedMfm) {
|
if (!defaultStore.state.advancedMfm) {
|
||||||
style = '';
|
style = '';
|
||||||
|
|
|
@ -122,7 +122,7 @@ export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error
|
||||||
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg';
|
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg';
|
||||||
export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg';
|
export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg';
|
||||||
|
|
||||||
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime'];
|
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'crop', 'fade'];
|
||||||
export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = {
|
export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = {
|
||||||
tada: ['speed=', 'delay='],
|
tada: ['speed=', 'delay='],
|
||||||
jelly: ['speed=', 'delay='],
|
jelly: ['speed=', 'delay='],
|
||||||
|
@ -146,4 +146,6 @@ export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = {
|
||||||
rotate: ['deg='],
|
rotate: ['deg='],
|
||||||
ruby: [],
|
ruby: [],
|
||||||
unixtime: [],
|
unixtime: [],
|
||||||
|
fade: ['speed=', 'delay=', 'loop=', 'out'],
|
||||||
|
crop: ['top=', 'bottom=', 'left=', 'right='],
|
||||||
};
|
};
|
||||||
|
|
|
@ -162,6 +162,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
|
<MkSwitch v-model="warnMissingAltText">{{ i18n.ts.warnForMissingAltText }}</MkSwitch>
|
||||||
<MkSwitch v-model="imageNewTab">{{ i18n.ts.openImageInNewTab }}</MkSwitch>
|
<MkSwitch v-model="imageNewTab">{{ i18n.ts.openImageInNewTab }}</MkSwitch>
|
||||||
<MkSwitch v-model="useReactionPickerForContextMenu">{{ i18n.ts.useReactionPickerForContextMenu }}</MkSwitch>
|
<MkSwitch v-model="useReactionPickerForContextMenu">{{ i18n.ts.useReactionPickerForContextMenu }}</MkSwitch>
|
||||||
<MkSwitch v-model="enableInfiniteScroll">{{ i18n.ts.enableInfiniteScroll }}</MkSwitch>
|
<MkSwitch v-model="enableInfiniteScroll">{{ i18n.ts.enableInfiniteScroll }}</MkSwitch>
|
||||||
|
@ -299,6 +300,7 @@ const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds'));
|
||||||
const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
|
const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
|
||||||
const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia'));
|
const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia'));
|
||||||
const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
|
const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
|
||||||
|
const warnMissingAltText = computed(defaultStore.makeGetterSetter('warnMissingAltText'));
|
||||||
const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
|
const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
|
||||||
const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
|
const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
|
||||||
const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel'));
|
const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel'));
|
||||||
|
|
|
@ -75,6 +75,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
|
||||||
'showRenotesCount',
|
'showRenotesCount',
|
||||||
'showReactionsCount',
|
'showReactionsCount',
|
||||||
'loadRawImages',
|
'loadRawImages',
|
||||||
|
'warnMissingAltText',
|
||||||
'imageNewTab',
|
'imageNewTab',
|
||||||
'dataSaver',
|
'dataSaver',
|
||||||
'disableShowingAnimatedImages',
|
'disableShowingAnimatedImages',
|
||||||
|
|
|
@ -247,6 +247,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
warnMissingAltText: {
|
||||||
|
where: 'device',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
imageNewTab: {
|
imageNewTab: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -672,3 +672,12 @@ html[data-color-mode=dark] ._woodenFrame {
|
||||||
0% { filter: hue-rotate(0deg) contrast(150%) saturate(150%); }
|
0% { filter: hue-rotate(0deg) contrast(150%) saturate(150%); }
|
||||||
100% { filter: hue-rotate(360deg) contrast(150%) saturate(150%); }
|
100% { filter: hue-rotate(360deg) contrast(150%) saturate(150%); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes mfm-fade {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue