Compare commits

...

10 Commits

Author SHA1 Message Date
osamu 6d5058c3d7 コメント対応(MkButtonのimportとonBeforeUpdate->watch) 2023-11-05 11:12:42 +09:00
おさむのひと 81df6a6f8d Merge branch 'develop' into fix/deck_column_target_change 2023-11-05 10:41:19 +09:00
syuilo 66cecfaefd Update CHANGELOG.md 2023-11-05 10:23:24 +09:00
Marie 2cce28533f fix(backend): isBot not being set on Application type (#12248)
* fix: bot not being set on all relays

* updatePerson missing the change

* chore: replace wrong word with correct word
2023-11-05 10:22:10 +09:00
syuilo c2ddb649f8 enhance: 非通知なお知らせを作成できるように 2023-11-05 09:04:38 +09:00
syuilo bdbb3266ae fix(backend): アーカイブしたお知らせがコントロールパネルに表示される問題を修正 2023-11-05 09:04:03 +09:00
syuilo a005606392 Update MkTimeline.vue 2023-11-05 08:31:02 +09:00
syuilo f2953e6205 fix code style 2023-11-05 08:29:50 +09:00
syuilo 1876f7d5ff Merge branch 'develop' into pr/12237 2023-11-05 08:25:57 +09:00
syuilo 56401ed91c 🎨 2023-11-05 08:25:08 +09:00
16 changed files with 73 additions and 21 deletions
+3
View File
@@ -29,6 +29,7 @@
- ユーザーが誤ったメールアドレスを入力した場合に招待コードが失効してしまう問題が解消されます。
- Enhance: すでにフォローしたすべての人の返信をTLに追加できるように
- Enhance: 未読の通知数を表示できるように
- Enhance: 通知されず、確認の必要もないお知らせ(silence)を作成可能になりました
- Enhance: ローカリゼーションの更新
- Enhance: 依存関係の更新
- Change: CWを使用する場合、注釈を空にすることは許可されなくなりました
@@ -63,6 +64,7 @@
- Fix: 11以上されているリアクションにおいてツールチップで示されるリアクション数が本来よりも1多い問題を修正 #12174
- Fix: サイレンス状態で公開範囲のパブリックを選択できてしまう問題を修正 #12224
- Fix: In deck layout, replies option is not saved after refresh
- Fix: アーカイブしたお知らせがコントロールパネルに表示される問題を修正
- Fix: デッキに表示されたチャンネルの表示先チャンネルを切り替えた際、即座に反映されない問題を修正 #12236
- Note: アップデート後、サウンドに関する設定が初期化されます
@@ -73,6 +75,7 @@
- Enhance: プロフィールの自己紹介欄のMFMが連合するようになりました
- 相手がMisskey v2023.11.0以降である必要があります
- Enhance: チャンネル取得時のパフォーマンスを向上
- Enhance: AP: ApplicationタイプのアカウントをisBotとして扱うように
- Fix: リストTLに自分のフォロワー限定投稿が含まれない問題を修正
- Fix: ローカルタイムラインに投稿者自身の投稿への返信が含まれない問題を修正
- Fix: 自分のフォローしているユーザーの自分のフォローしていないユーザーの visibility: followers な投稿への返信がストリーミングで流れてくる問題を修正
+2
View File
@@ -1172,6 +1172,8 @@ export interface Locale {
"readConfirmText": string;
"shouldNotBeUsedToPresentPermanentInfo": string;
"dialogAnnouncementUxWarn": string;
"silence": string;
"silenceDescription": string;
};
"_initialAccountSetting": {
"accountCreated": string;
+2
View File
@@ -1170,6 +1170,8 @@ _announcement:
readConfirmText: "「{title}」の内容を読み、既読にします。"
shouldNotBeUsedToPresentPermanentInfo: "特に新規ユーザーのUXを損ねる可能性が高いため、ストック情報ではなくフロー情報の掲示にお知らせを使用することを推奨します。"
dialogAnnouncementUxWarn: "ダイアログ形式のお知らせが同時に2つ以上ある場合、UXに悪影響を及ぼす可能性が非常に高いため、使用は慎重に行うことを推奨します。"
silence: "非通知"
silenceDescription: "オンにすると、このお知らせは通知されず、既読にする必要もなくなります。"
_initialAccountSetting:
accountCreated: "アカウントの作成が完了しました!"
@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class AnnouncementSilence1699141698112 {
name = 'AnnouncementSilence1699141698112'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "announcement" ADD "silence" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`CREATE INDEX "IDX_7b8d9225168e962f94ea517e00" ON "announcement" ("silence") `);
}
async down(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_7b8d9225168e962f94ea517e00"`);
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "silence"`);
}
}
@@ -47,6 +47,7 @@ export class AnnouncementService {
const q = this.announcementsRepository.createQueryBuilder('announcement')
.where('announcement.isActive = true')
.andWhere('announcement.silence = false')
.andWhere(new Brackets(qb => {
qb.orWhere('announcement.userId = :userId', { userId: user.id });
qb.orWhere('announcement.userId IS NULL');
@@ -73,6 +74,7 @@ export class AnnouncementService {
icon: values.icon,
display: values.display,
forExistingUsers: values.forExistingUsers,
silence: values.silence,
needConfirmationToRead: values.needConfirmationToRead,
userId: values.userId,
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
@@ -124,6 +126,7 @@ export class AnnouncementService {
display: values.display,
icon: values.icon,
forExistingUsers: values.forExistingUsers,
silence: values.silence,
needConfirmationToRead: values.needConfirmationToRead,
isActive: values.isActive,
});
@@ -210,6 +213,7 @@ export class AnnouncementService {
icon: announcement.icon,
display: announcement.display,
needConfirmationToRead: announcement.needConfirmationToRead,
silence: announcement.silence,
forYou: announcement.userId === me?.id,
isRead: reads.some(read => read.announcementId === announcement.id),
}));
@@ -269,7 +269,7 @@ export class ApPersonService implements OnModuleInit {
const tags = extractApHashtags(person.tag).map(normalizeForSearch).splice(0, 32);
const isBot = getApType(object) === 'Service';
const isBot = getApType(object) === 'Service' || getApType(object) === 'Application';
const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/);
@@ -456,7 +456,7 @@ export class ApPersonService implements OnModuleInit {
emojis: emojiNames,
name: truncate(person.name, nameLength),
tags,
isBot: getApType(object) === 'Service',
isBot: getApType(object) === 'Service' || getApType(object) === 'Application',
isCat: (person as any).isCat === true,
isLocked: person.manuallyApprovesFollowers,
movedToUri: person.movedTo ?? null,
@@ -66,6 +66,12 @@ export class MiAnnouncement {
})
public forExistingUsers: boolean;
@Index()
@Column('boolean', {
default: false,
})
public silence: boolean;
@Index()
@Column({
...id(),
@@ -58,6 +58,7 @@ export const paramDef = {
icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'], default: 'info' },
display: { type: 'string', enum: ['normal', 'banner', 'dialog'], default: 'normal' },
forExistingUsers: { type: 'boolean', default: false },
silence: { type: 'boolean', default: false },
needConfirmationToRead: { type: 'boolean', default: false },
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
},
@@ -78,6 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
icon: ps.icon,
display: ps.display,
forExistingUsers: ps.forExistingUsers,
silence: ps.silence,
needConfirmationToRead: ps.needConfirmationToRead,
userId: ps.userId,
}, me);
@@ -86,6 +86,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
) {
super(meta, paramDef, async (ps, me) => {
const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId);
query.andWhere('announcement.isActive = true');
if (ps.userId) {
query.andWhere('announcement.userId = :userId', { userId: ps.userId });
} else {
@@ -113,6 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
display: announcement.display,
isActive: announcement.isActive,
forExistingUsers: announcement.forExistingUsers,
silence: announcement.silence,
needConfirmationToRead: announcement.needConfirmationToRead,
userId: announcement.userId,
reads: reads.get(announcement)!,
@@ -35,6 +35,7 @@ export const paramDef = {
icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'] },
display: { type: 'string', enum: ['normal', 'banner', 'dialog'] },
forExistingUsers: { type: 'boolean' },
silence: { type: 'boolean' },
needConfirmationToRead: { type: 'boolean' },
isActive: { type: 'boolean' },
},
@@ -63,6 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
display: ps.display,
icon: ps.icon,
forExistingUsers: ps.forExistingUsers,
silence: ps.silence,
needConfirmationToRead: ps.needConfirmationToRead,
isActive: ps.isActive,
}, me);
@@ -90,6 +90,9 @@ function concatMapWithArray(map: MisskeyEntityMap, entities: MisskeyEntity[]): M
</script>
<script lang="ts" setup>
import { infoImageUrl } from '@/instance.js';
import MkButton from '@/components/MkButton.vue';
import MkLoading from '@/components/global/MkLoading.vue';
import MkError from '@/components/global/MkError.vue';
const props = withDefaults(defineProps<{
pagination: Paging;
+16 -12
View File
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { computed, onBeforeMount, onUnmounted, onBeforeUpdate, provide } from 'vue';
import { computed, watch, onUnmounted, provide } from 'vue';
import { Connection } from 'misskey-js/built/streaming.js';
import MkNotes from '@/components/MkNotes.vue';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
@@ -88,7 +88,8 @@ let connection2: Connection;
let paginationQuery: Paging | null = null;
const stream = useStream();
const connectChannel = () => {
function connectChannel() {
if (props.src === 'antenna') {
connection = stream.useChannel('antenna', {
antennaId: props.antenna,
@@ -142,14 +143,14 @@ const connectChannel = () => {
});
}
if (props.src !== 'directs' || props.src !== 'mentions') connection.on('note', prepend);
};
}
const disconnectChannel = () => {
function disconnectChannel() {
if (connection) connection.dispose();
if (connection2) connection2.dispose();
};
}
const updatePaginationQuery = () => {
function updatePaginationQuery() {
let endpoint: string | null;
let query: TimelineQueryType | null;
@@ -222,24 +223,27 @@ const updatePaginationQuery = () => {
} else {
paginationQuery = null;
}
};
}
const refreshEndpointAndChannel = () => {
function refreshEndpointAndChannel() {
if (!defaultStore.state.disableStreamingTimeline) {
disconnectChannel();
connectChannel();
}
updatePaginationQuery();
};
}
// IDTL
watch(() => [props.list, props.antenna, props.channel, props.role], refreshEndpointAndChannel);
//
refreshEndpointAndChannel();
onUnmounted(() => {
disconnectChannel();
});
onBeforeMount(refreshEndpointAndChannel);
onBeforeUpdate(refreshEndpointAndChannel);
function reloadTimeline() {
return new Promise<void>((res) => {
tlNotesCount = 0;
@@ -18,10 +18,10 @@ interface Props {
const contentSymbol = Symbol();
const observer = new ResizeObserver((entries) => {
const results: {
container: HTMLSpanElement;
transform: string;
}[] = [];
const results: {
container: HTMLSpanElement;
transform: string;
}[] = [];
for (const entry of entries) {
const content = (entry.target[contentSymbol] ? entry.target : entry.target.firstElementChild) as HTMLSpanElement;
const props: Required<Props> = content[contentSymbol];
@@ -48,6 +48,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="announcement.forExistingUsers" :helpText="i18n.ts._announcement.forExistingUsersDescription">
{{ i18n.ts._announcement.forExistingUsers }}
</MkSwitch>
<MkSwitch v-model="announcement.silence" :helpText="i18n.ts._announcement.silenceDescription">
{{ i18n.ts._announcement.silence }}
</MkSwitch>
<MkSwitch v-model="announcement.needConfirmationToRead" :helpText="i18n.ts._announcement.needConfirmationToReadDescription">
{{ i18n.ts._announcement.needConfirmationToRead }}
</MkSwitch>
@@ -97,6 +100,7 @@ function add() {
icon: 'info',
display: 'normal',
forExistingUsers: false,
silence: false,
needConfirmationToRead: false,
});
}
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<section v-for="announcement in items" :key="announcement.id" class="_panel" :class="$style.announcement">
<div v-if="announcement.forYou" :class="$style.forYou"><i class="ti ti-pin"></i> {{ i18n.ts.forYou }}</div>
<div :class="$style.header">
<span v-if="$i && !announcement.isRead" style="margin-right: 0.5em;">🆕</span>
<span v-if="$i && !announcement.silence && !announcement.isRead" style="margin-right: 0.5em;">🆕</span>
<span style="margin-right: 0.5em;">
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
<i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTime :time="announcement.updatedAt ?? announcement.createdAt" mode="detail"/>
</div>
</div>
<div v-if="tab !== 'past' && $i && !announcement.isRead" :class="$style.footer">
<div v-if="tab !== 'past' && $i && !announcement.silence && !announcement.isRead" :class="$style.footer">
<MkButton primary @click="read(announcement)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton>
</div>
</section>
@@ -94,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="[$style.avatarDecoration, { [$style.avatarDecorationActive]: $i.avatarDecorations.some(x => x.id === avatarDecoration.id) }]"
@click="openDecoration(avatarDecoration)"
>
<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="2 / 3">{{ avatarDecoration.name }}</MkCondensedLine></div>
<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decoration="{ url: avatarDecoration.url }" forceShowDecoration/>
<i v-if="avatarDecoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => avatarDecoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.avatarDecorationLock" class="ti ti-lock"></i>
</div>