Merge branch 'develop' into enh-14786

This commit is contained in:
かっこかり 2024-10-21 17:32:38 +09:00 committed by GitHub
commit f5448bf2a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 221 additions and 46 deletions

View file

@ -117,8 +117,8 @@ async function requestRender() {
sitekey: props.sitekey,
theme: defaultStore.state.darkMode ? 'dark' : 'light',
callback: callback,
'expired-callback': callback,
'error-callback': callback,
'expired-callback': () => callback(undefined),
'error-callback': () => callback(undefined),
});
} else if (props.provider === 'mcaptcha' && props.instanceUrl && props.sitekey) {
const { default: Widget } = await import('@mcaptcha/vanilla-glue');

View file

@ -37,13 +37,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { host } from '@@/js/config.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { pleaseLogin } from '@/scripts/please-login.js';
import { host } from '@@/js/config.js';
import { $i } from '@/account.js';
import { defaultStore } from '@/store.js';
@ -80,7 +80,7 @@ function onFollowChange(user: Misskey.entities.UserDetailed) {
}
async function onClick() {
pleaseLogin(undefined, { type: 'web', path: `/@${props.user.username}@${props.user.host ?? host}` });
pleaseLogin({ openOnRemote: { type: 'web', path: `/@${props.user.username}@${props.user.host ?? host}` } });
wait.value = true;

View file

@ -227,6 +227,7 @@ const emit = defineEmits<{
}>();
const inTimeline = inject<boolean>('inTimeline', false);
const tl_withSensitive = inject<Ref<boolean>>('tl_withSensitive', ref(false));
const inChannel = inject('inChannel', null);
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
@ -299,7 +300,7 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string
if (checkOnly) return false;
if (inTimeline && !defaultStore.state.tl.filter.withSensitive && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute';
if (inTimeline && !tl_withSensitive.value && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute';
return false;
}
@ -419,7 +420,7 @@ if (!props.mock) {
}
function renote(viaKeyboard = false) {
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
showMovedDialog();
const { menu } = getRenoteMenu({ note: note.value, renoteButton, mock: props.mock });
@ -429,7 +430,7 @@ function renote(viaKeyboard = false) {
}
function reply(): void {
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
if (props.mock) {
return;
}
@ -442,7 +443,7 @@ function reply(): void {
}
function react(): void {
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
showMovedDialog();
if (appearNote.value.reactionAcceptance === 'likeOnly') {
sound.playMisskeySfx('reaction');
@ -563,7 +564,7 @@ function showRenoteMenu(): void {
}
if (isMyRenote) {
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
os.popupMenu([
getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
{ type: 'divider' },

View file

@ -207,6 +207,7 @@ import { computed, inject, onMounted, provide, ref, shallowRef } from 'vue';
import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js';
import { isLink } from '@@/js/is-link.js';
import { host } from '@@/js/config.js';
import MkNoteSub from '@/components/MkNoteSub.vue';
import MkNoteSimple from '@/components/MkNoteSimple.vue';
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
@ -230,7 +231,6 @@ import { reactionPicker } from '@/scripts/reaction-picker.js';
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { host } from '@@/js/config.js';
import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/scripts/get-note-menu.js';
import { useNoteCapture } from '@/scripts/use-note-capture.js';
import { deepClone } from '@/scripts/clone.js';
@ -404,7 +404,7 @@ if (appearNote.value.reactionAcceptance === 'likeOnly') {
}
function renote() {
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
showMovedDialog();
const { menu } = getRenoteMenu({ note: note.value, renoteButton });
@ -412,7 +412,7 @@ function renote() {
}
function reply(): void {
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
showMovedDialog();
os.post({
reply: appearNote.value,
@ -423,7 +423,7 @@ function reply(): void {
}
function react(): void {
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
showMovedDialog();
if (appearNote.value.reactionAcceptance === 'likeOnly') {
sound.playMisskeySfx('reaction');
@ -499,7 +499,7 @@ async function clip(): Promise<void> {
function showRenoteMenu(): void {
if (!isMyRenote) return;
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
os.popupMenu([{
text: i18n.ts.unrenote,
icon: 'ti ti-trash',

View file

@ -29,14 +29,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { host } from '@@/js/config.js';
import { useInterval } from '@@/js/use-interval.js';
import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
import { sum } from '@/scripts/array.js';
import { pleaseLogin } from '@/scripts/please-login.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { host } from '@@/js/config.js';
import { useInterval } from '@@/js/use-interval.js';
const props = defineProps<{
noteId: string;
@ -85,7 +85,7 @@ if (props.poll.expiresAt) {
const vote = async (id) => {
if (props.readOnly || closed.value || isVoted.value) return;
pleaseLogin(undefined, pleaseLoginContext.value);
pleaseLogin({ openOnRemote: pleaseLoginContext.value });
const { canceled } = await os.confirm({
type: 'question',

View file

@ -65,10 +65,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo>
<input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown">
<input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown" @keyup="onKeyup" @compositionend="onCompositionEnd">
<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
<div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div>
<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @keyup="onKeyup" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
</div>
<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
@ -201,6 +201,7 @@ const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'
const imeText = ref('');
const showingOptions = ref(false);
const textAreaReadOnly = ref(false);
const justEndedComposition = ref(false);
const draftKey = computed((): string => {
let key = props.channel ? `channel:${props.channel.id}` : '';
@ -573,7 +574,13 @@ function clear() {
function onKeydown(ev: KeyboardEvent) {
if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post();
if (ev.key === 'Escape') emit('esc');
// justEndedComposition.value is for Safari, which keyDown occurs after compositionend.
// ev.isComposing is for another browsers.
if (ev.key === 'Escape' && !justEndedComposition.value && !ev.isComposing) emit('esc');
}
function onKeyup(ev: KeyboardEvent) {
justEndedComposition.value = false;
}
function onCompositionUpdate(ev: CompositionEvent) {
@ -582,6 +589,7 @@ function onCompositionUpdate(ev: CompositionEvent) {
function onCompositionEnd(ev: CompositionEvent) {
imeText.value = '';
justEndedComposition.value = true;
}
async function onPaste(ev: ClipboardEvent) {

View file

@ -38,6 +38,7 @@ const props = withDefaults(defineProps<{
sound?: boolean;
withRenotes?: boolean;
withReplies?: boolean;
withSensitive?: boolean;
onlyFiles?: boolean;
}>(), {
withRenotes: true,
@ -51,6 +52,7 @@ const emit = defineEmits<{
}>();
provide('inTimeline', true);
provide('tl_withSensitive', computed(() => props.withSensitive));
provide('inChannel', computed(() => props.src === 'channel'));
type TimelineQueryType = {
@ -248,6 +250,9 @@ function refreshEndpointAndChannel() {
// IDTL
watch(() => [props.list, props.antenna, props.channel, props.role, props.withRenotes], refreshEndpointAndChannel);
// withSensitiveOK
watch(() => props.withSensitive, reloadTimeline);
//
refreshEndpointAndChannel();

View file

@ -688,14 +688,16 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
}
export function post(props: Record<string, any> = {}): Promise<void> {
pleaseLogin(undefined, (props.initialText || props.initialNote ? {
type: 'share',
params: {
text: props.initialText ?? props.initialNote.text,
visibility: props.initialVisibility ?? props.initialNote?.visibility,
localOnly: (props.initialLocalOnly || props.initialNote?.localOnly) ? '1' : '0',
},
} : undefined));
pleaseLogin({
openOnRemote: (props.initialText || props.initialNote ? {
type: 'share',
params: {
text: props.initialText ?? props.initialNote.text,
visibility: props.initialVisibility ?? props.initialNote?.visibility,
localOnly: (props.initialLocalOnly || props.initialNote?.localOnly) ? '1' : '0',
},
} : undefined),
});
showMovedDialog();
return new Promise(resolve => {

View file

@ -24,7 +24,7 @@ const props = defineProps<{
}>();
if (props.showLoginPopup) {
pleaseLogin('/');
pleaseLogin({ path: '/' });
}
const headerActions = computed(() => []);

View file

@ -61,6 +61,7 @@ import { i18n } from '@/i18n.js';
import { dateString } from '@/filters/date.js';
import MkClipPreview from '@/components/MkClipPreview.vue';
import { defaultStore } from '@/store.js';
import { pleaseLogin } from '@/scripts/please-login.js';
const props = defineProps<{
noteId: string;
@ -128,6 +129,11 @@ function fetchNote() {
});
}
}).catch(err => {
if (err.id === '8e75455b-738c-471d-9f80-62693f33372e') {
pleaseLogin({
message: i18n.ts.thisContentsAreMarkedAsSigninRequiredByAuthor,
});
}
error.value = err;
});
}

View file

@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>{{ i18n.ts.noCrawleDescription }}</template>
</MkSwitch>
<MkSwitch v-model="preventAiLearning" @update:modelValue="save()">
{{ i18n.ts.preventAiLearning }}<span class="_beta">{{ i18n.ts.beta }}</span>
{{ i18n.ts.preventAiLearning }}
<template #caption>{{ i18n.ts.preventAiLearningDescription }}</template>
</MkSwitch>
<MkSwitch v-model="isExplorable" @update:modelValue="save()">
@ -44,6 +44,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>{{ i18n.ts.makeExplorableDescription }}</template>
</MkSwitch>
<FormSection>
<template #label>{{ i18n.ts.lockdown }}</template>
<div class="_gaps_m">
<MkSwitch v-model="requireSigninToViewContents" @update:modelValue="save()">
{{ i18n.ts._accountSettings.requireSigninToViewContents }}<span class="_beta">{{ i18n.ts.beta }}</span>
<template #caption>
<div>{{ i18n.ts._accountSettings.requireSigninToViewContentsDescription1 }}</div>
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription2 }}</div>
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription3 }}</div>
</template>
</MkSwitch>
</div>
</FormSection>
<FormSection>
<div class="_gaps_m">
<MkSwitch v-model="rememberNoteVisibility" @update:modelValue="save()">{{ i18n.ts.rememberNoteVisibility }}</MkSwitch>
@ -90,6 +105,7 @@ const autoAcceptFollowed = ref($i.autoAcceptFollowed);
const noCrawle = ref($i.noCrawle);
const preventAiLearning = ref($i.preventAiLearning);
const isExplorable = ref($i.isExplorable);
const requireSigninToViewContents = ref($i.requireSigninToViewContents ?? false);
const hideOnlineStatus = ref($i.hideOnlineStatus);
const publicReactions = ref($i.publicReactions);
const followingVisibility = ref($i.followingVisibility);
@ -107,6 +123,7 @@ function save() {
noCrawle: !!noCrawle.value,
preventAiLearning: !!preventAiLearning.value,
isExplorable: !!isExplorable.value,
requireSigninToViewContents: !!requireSigninToViewContents.value,
hideOnlineStatus: !!hideOnlineStatus.value,
publicReactions: !!publicReactions.value,
followingVisibility: followingVisibility.value,

View file

@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:list="src.split(':')[1]"
:withRenotes="withRenotes"
:withReplies="withReplies"
:withSensitive="withSensitive"
:onlyFiles="onlyFiles"
:sound="true"
@queue="queueUpdated"
@ -121,11 +122,6 @@ watch(src, () => {
queue.value = 0;
});
watch(withSensitive, () => {
//
tlComponent.value?.reloadTimeline();
});
function queueUpdated(q: number): void {
queue.value = q;
}

View file

@ -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(),

View file

@ -49,6 +49,7 @@ export type Column = {
tl?: BasicTimelineType;
withRenotes?: boolean;
withReplies?: boolean;
withSensitive?: boolean;
onlyFiles?: boolean;
soundSetting: SoundStore;
};

View file

@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:src="column.tl"
:withRenotes="withRenotes"
:withReplies="withReplies"
:withSensitive="withSensitive"
:onlyFiles="onlyFiles"
@note="onNote"
/>
@ -54,6 +55,7 @@ const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
const withRenotes = ref(props.column.withRenotes ?? true);
const withReplies = ref(props.column.withReplies ?? false);
const withSensitive = ref(props.column.withSensitive ?? true);
const onlyFiles = ref(props.column.onlyFiles ?? false);
watch(withRenotes, v => {
@ -68,6 +70,12 @@ watch(withReplies, v => {
});
});
watch(withSensitive, v => {
updateColumn(props.column.id, {
withSensitive: v,
});
});
watch(onlyFiles, v => {
updateColumn(props.column.id, {
onlyFiles: v,
@ -144,6 +152,10 @@ const menu = computed<MenuItem[]>(() => {
text: i18n.ts.fileAttachedOnly,
ref: onlyFiles,
disabled: hasWithReplies(props.column.tl) ? withReplies : false,
}, {
type: 'switch',
text: i18n.ts.withSensitive,
ref: withSensitive,
});
return menuItems;