Merge branch 'develop' into enh-14794

This commit is contained in:
かっこかり 2024-10-21 21:01:43 +09:00 committed by GitHub
commit 4f5d58cea1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 110 additions and 71 deletions

View file

@ -14,6 +14,7 @@
- Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正
- Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/768)
- Fix: デッキのタイムラインカラムで「センシティブなファイルを含むノートを表示」設定が使用できなかった問題を修正
### Server
-

View file

@ -64,6 +64,22 @@ Thank you for your PR! Before creating a PR, please check the following:
Thanks for your cooperation 🤗
### Additional things for ActivityPub payload changes
*This section is specific to misskey-dev implementation. Other fork or implementation may take different way. A significant difference is that non-"misskey-dev" extension is not described in the misskey-hub's document.*
If PR includes changes to ActivityPub payload, please reflect it in [misskey-hub's document](https://github.com/misskey-dev/misskey-hub-next/blob/master/content/ns.md) by sending PR.
The name of purporsed extension property (referred as "extended property" in later) to ActivityPub shall be prefixed by `_misskey_`. (i.e. `_misskey_quote`)
The extended property in `packages/backend/src/core/activitypub/type.ts` **must** be declared as optional because ActivityPub payloads that comes from older Misskey or other implementation may not contain it.
The extended property must be included in the context definition. Context is defined in `packages/backend/src/core/activitypub/misc/contexts.ts`.
The key shall be same as the name of extended property, and the value shall be same as "short IRI".
"Short IRI" is defined in misskey-hub's document, but usually takes form of `misskey:<name of extended property>`. (i.e. `misskey:_misskey_quote`)
One should not add property that has defined before by other implementation, or add custom variant value to "well-known" property.
## Reviewers guide
Be willing to comment on the good points and not just the things you want fixed 💯

View file

@ -5,12 +5,12 @@
import { defineAsyncComponent, reactive, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { apiUrl } from '@@/js/config.js';
import type { MenuItem, MenuButton } from '@/types/menu.js';
import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js';
import type { MenuItem, MenuButton } from '@/types/menu.js';
import { del, get, set } from '@/scripts/idb-proxy.js';
import { apiUrl } from '@@/js/config.js';
import { waiting, popup, popupMenu, success, alert } from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
@ -165,7 +165,18 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr
});
}
export function updateAccount(accountData: Partial<Account>) {
export function updateAccount(accountData: Account) {
if (!$i) return;
for (const key of Object.keys($i)) {
delete $i[key];
}
for (const [key, value] of Object.entries(accountData)) {
$i[key] = value;
}
miLocalStorage.setItem('account', JSON.stringify($i));
}
export function updateAccountPartial(accountData: Partial<Account>) {
if (!$i) return;
for (const [key, value] of Object.entries(accountData)) {
$i[key] = value;

View file

@ -4,14 +4,14 @@
*/
import { createApp, defineAsyncComponent, markRaw } from 'vue';
import { ui } from '@@/js/config.js';
import { common } from './common.js';
import type * as Misskey from 'misskey-js';
import { ui } from '@@/js/config.js';
import { i18n } from '@/i18n.js';
import { alert, confirm, popup, post, toast } from '@/os.js';
import { useStream } from '@/stream.js';
import * as sound from '@/scripts/sound.js';
import { $i, signout, updateAccount } from '@/account.js';
import { $i, signout, updateAccountPartial } from '@/account.js';
import { instance } from '@/instance.js';
import { ColdDeviceStorage, defaultStore } from '@/store.js';
import { reactionPicker } from '@/scripts/reaction-picker.js';
@ -291,11 +291,11 @@ export async function mainBoot() {
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
updateAccount(i);
updateAccountPartial(i);
});
main.on('readAllNotifications', () => {
updateAccount({
updateAccountPartial({
hasUnreadNotification: false,
unreadNotificationsCount: 0,
});
@ -303,39 +303,39 @@ export async function mainBoot() {
main.on('unreadNotification', () => {
const unreadNotificationsCount = ($i?.unreadNotificationsCount ?? 0) + 1;
updateAccount({
updateAccountPartial({
hasUnreadNotification: true,
unreadNotificationsCount,
});
});
main.on('unreadMention', () => {
updateAccount({ hasUnreadMentions: true });
updateAccountPartial({ hasUnreadMentions: true });
});
main.on('readAllUnreadMentions', () => {
updateAccount({ hasUnreadMentions: false });
updateAccountPartial({ hasUnreadMentions: false });
});
main.on('unreadSpecifiedNote', () => {
updateAccount({ hasUnreadSpecifiedNotes: true });
updateAccountPartial({ hasUnreadSpecifiedNotes: true });
});
main.on('readAllUnreadSpecifiedNotes', () => {
updateAccount({ hasUnreadSpecifiedNotes: false });
updateAccountPartial({ hasUnreadSpecifiedNotes: false });
});
main.on('readAllAntennas', () => {
updateAccount({ hasUnreadAntenna: false });
updateAccountPartial({ hasUnreadAntenna: false });
});
main.on('unreadAntenna', () => {
updateAccount({ hasUnreadAntenna: true });
updateAccountPartial({ hasUnreadAntenna: true });
sound.playMisskeySfx('antenna');
});
main.on('readAllAnnouncements', () => {
updateAccount({ hasUnreadAnnouncement: false });
updateAccountPartial({ hasUnreadAnnouncement: false });
});
// 個人宛てお知らせが発行されたとき

View file

@ -29,7 +29,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
import MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { $i, updateAccount } from '@/account.js';
import { $i, updateAccountPartial } from '@/account.js';
const props = withDefaults(defineProps<{
announcement: Misskey.entities.Announcement;
@ -51,7 +51,7 @@ async function ok() {
modal.value?.close();
misskeyApi('i/read-announcement', { announcementId: props.announcement.id });
updateAccount({
updateAccountPartial({
unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id),
});
}

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;
}

View file

@ -16,9 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only
@keydown.space.enter="show"
>
<div ref="prefixEl" :class="$style.prefix"><slot name="prefix"></slot></div>
<select
<div
ref="inputEl"
v-model="v"
v-adaptive-border
tabindex="-1"
:class="$style.inputCore"
@ -26,27 +25,25 @@ SPDX-License-Identifier: AGPL-3.0-only
:required="required"
:readonly="readonly"
:placeholder="placeholder"
@input="onInput"
@mousedown.prevent="() => {}"
@keydown.prevent="() => {}"
>
<slot></slot>
</select>
<div style="pointer-events: none;">{{ currentValueText ?? '' }}</div>
<div style="display: none;">
<slot></slot>
</div>
</div>
<div ref="suffixEl" :class="$style.suffix"><i class="ti ti-chevron-down" :class="[$style.chevron, { [$style.chevronOpening]: opening }]"></i></div>
</div>
<div :class="$style.caption"><slot name="caption"></slot></div>
<MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
</div>
</template>
<script lang="ts" setup>
import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots, VNodeChild } from 'vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { useInterval } from '@@/js/use-interval.js';
import { i18n } from '@/i18n.js';
import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
const props = defineProps<{
modelValue: string | null;
@ -56,25 +53,20 @@ const props = defineProps<{
placeholder?: string;
autofocus?: boolean;
inline?: boolean;
manualSave?: boolean;
small?: boolean;
large?: boolean;
}>();
const emit = defineEmits<{
(ev: 'changeByUser', value: string | null): void;
(ev: 'update:modelValue', value: string | null): void;
(ev: 'update:modelValue', value: string | number | null): void;
}>();
const slots = useSlots();
const { modelValue, autofocus } = toRefs(props);
const v = ref(modelValue.value);
const focused = ref(false);
const opening = ref(false);
const changed = ref(false);
const invalid = ref(false);
const filled = computed(() => v.value !== '' && v.value != null);
const currentValueText = ref<string | null>(null);
const inputEl = ref<HTMLObjectElement | null>(null);
const prefixEl = ref<HTMLElement | null>(null);
const suffixEl = ref<HTMLElement | null>(null);
@ -85,26 +77,6 @@ const height =
36;
const focus = () => container.value?.focus();
const onInput = (ev) => {
changed.value = true;
};
const updated = () => {
changed.value = false;
emit('update:modelValue', v.value);
};
watch(modelValue, newValue => {
v.value = newValue;
});
watch(v, () => {
if (!props.manualSave) {
updated();
}
invalid.value = inputEl.value?.validity.badInput ?? true;
});
//
// 0
@ -134,6 +106,31 @@ onMounted(() => {
});
});
watch(modelValue, () => {
const scanOptions = (options: VNodeChild[]) => {
for (const vnode of options) {
if (typeof vnode !== 'object' || vnode === null || Array.isArray(vnode)) continue;
if (vnode.type === 'optgroup') {
const optgroup = vnode;
if (Array.isArray(optgroup.children)) scanOptions(optgroup.children);
} else if (Array.isArray(vnode.children)) { //
const fragment = vnode;
if (Array.isArray(fragment.children)) scanOptions(fragment.children);
} else if (vnode.props == null) { // v-if false
// nop?
} else {
const option = vnode;
if (option.props?.value === modelValue.value) {
currentValueText.value = option.children as string;
break;
}
}
}
};
scanOptions(slots.default!());
}, { immediate: true });
function show() {
if (opening.value) return;
focus();
@ -146,11 +143,9 @@ function show() {
const pushOption = (option: VNode) => {
menu.push({
text: option.children as string,
active: computed(() => v.value === option.props?.value),
active: computed(() => modelValue.value === option.props?.value),
action: () => {
v.value = option.props?.value;
changed.value = true;
emit('changeByUser', v.value);
emit('update:modelValue', option.props?.value);
},
});
};
@ -248,7 +243,8 @@ function show() {
.inputCore {
appearance: none;
-webkit-appearance: none;
display: block;
display: flex;
align-items: center;
height: v-bind("height + 'px'");
width: 100%;
margin: 0;

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

@ -55,7 +55,7 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { $i, updateAccount } from '@/account.js';
import { $i, updateAccountPartial } from '@/account.js';
import { defaultStore } from '@/store.js';
const props = defineProps<{
@ -90,7 +90,7 @@ async function read(target: Misskey.entities.Announcement): Promise<void> {
target.isRead = true;
await misskeyApi('i/read-announcement', { announcementId: target.id });
if ($i) {
updateAccount({
updateAccountPartial({
unreadAnnouncements: $i.unreadAnnouncements.filter((a: { id: string; }) => a.id !== target.id),
});
}

View file

@ -56,7 +56,7 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { $i, updateAccount } from '@/account.js';
import { $i, updateAccountPartial } from '@/account.js';
const paginationCurrent = {
endpoint: 'announcements' as const,
@ -94,7 +94,7 @@ async function read(target) {
return a;
});
misskeyApi('i/read-announcement', { announcementId: target.id });
updateAccount({
updateAccountPartial({
unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== target.id),
});
}

View file

@ -84,7 +84,7 @@ import FormSection from '@/components/form/section.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkLink from '@/components/MkLink.vue';
import * as os from '@/os.js';
import { signinRequired, updateAccount } from '@/account.js';
import { signinRequired, updateAccountPartial } from '@/account.js';
import { i18n } from '@/i18n.js';
const $i = signinRequired();
@ -123,7 +123,7 @@ async function unregisterTOTP(): Promise<void> {
password: auth.result.password,
token: auth.result.token,
}).then(res => {
updateAccount({
updateAccountPartial({
twoFactorEnabled: false,
});
}).catch(error => {

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

@ -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;