diff --git a/CHANGELOG_CHERRYPICK.md b/CHANGELOG_CHERRYPICK.md index 35bedd405d..eefc503f88 100644 --- a/CHANGELOG_CHERRYPICK.md +++ b/CHANGELOG_CHERRYPICK.md @@ -22,6 +22,17 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE # 릴리즈 노트 이 문서는 CherryPick의 변경 사항만 포함합니다. +## 4.x.x +출시일: unreleased
+기반 Misskey 버전: 2023.x.x
+Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGELOG.md#2023xx) 문서를 참고하십시오. + +### General +- 미디어, 고양이 타임라인 개선 + - [misskey-dev/misskey@eb740e2](https://github.com/misskey-dev/misskey/commit/eb740e2c72ae6854b244ad099c927c069008720e) 이 추가됨에 따라, 해당 기능에 병합하고 기존 미디어 및 고양이 타임라인을 제거함 + +--- + ## 4.3.0 출시일: 2023/09/29
기반 Misskey 버전: 2023.9.1
diff --git a/locales/en-US.yml b/locales/en-US.yml index 0a3be0fddc..54fdfcc695 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1200,6 +1200,7 @@ authenticationRequiredToContinue: "Please authenticate to continue" dateAndTime: "Timestamp" showRenotes: "Show renotes" edited: "Edited" +showCatOnly: "Show only cats" additionalPermissionsForFlash: "Allow to add permission to Play" thisFlashRequiresTheFollowingPermissions: "This Play requires the following permissions" doYouWantToAllowThisPlayToAccessYourAccount: "Do you want to allow this Play to access your account?" @@ -1210,8 +1211,7 @@ _tlTutorial: step1_1: 'The {icon} Home timeline is where you can see posts from the accounts you follow.' step1_2: 'The {icon} Local timeline is where you can see posts from everyone else on this server.' step1_3: 'The {icon} Social timeline is a combination of the Home and Local timelines.' - step1_4: 'The {icon} Cat timeline shows posts from users who have enabled cat on their social timeline.' - step1_5: 'The {icon} Global timeline is where you can see posts from every other connected server.' + step1_4: 'The {icon} Global timeline is where you can see posts from every other connected server.' _announcement: forExistingUsers: "Existing users only" forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it." @@ -1612,7 +1612,6 @@ _role: _options: gtlAvailable: "Can view the global timeline" ltlAvailable: "Can view the local timeline" - ctlAvailable: "Can view the cat timeline" canPublicNote: "Can send public notes" canEditNote: "Note editing" canInvite: "Can create instance invite codes" @@ -2198,9 +2197,7 @@ _instanceCharts: _timelines: home: "Home" local: "Local" - media: "Media" social: "Social" - cat: "Cat" global: "Global" _play: new: "Create Play" diff --git a/locales/index.d.ts b/locales/index.d.ts index ca42ed2d2f..3c046b3cb3 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1206,6 +1206,7 @@ export interface Locale { "notificationRecieveConfig": string; "mutualFollow": string; "fileAttachedOnly": string; + "showCatOnly": string; "additionalPermissionsForFlash": string; "thisFlashRequiresTheFollowingPermissions": string; "doYouWantToAllowThisPlayToAccessYourAccount": string; @@ -1218,7 +1219,6 @@ export interface Locale { "step1_2": string; "step1_3": string; "step1_4": string; - "step1_5": string; }; "_announcement": { "forExistingUsers": string; @@ -1709,7 +1709,6 @@ export interface Locale { "_options": { "gtlAvailable": string; "ltlAvailable": string; - "ctlAvailable": string; "canPublicNote": string; "canEditNote": string; "canInvite": string; @@ -2346,9 +2345,7 @@ export interface Locale { "_timelines": { "home": string; "local": string; - "media": string; "social": string; - "cat": string; "global": string; }; "_play": { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1192badd4d..f099ca745d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1203,6 +1203,7 @@ edited: "編集済み" notificationRecieveConfig: "通知の受信設定" mutualFollow: "相互フォロー" fileAttachedOnly: "ファイル付きのみ" +showCatOnly: "キャット付きのみ" additionalPermissionsForFlash: "Playへの追加許可" thisFlashRequiresTheFollowingPermissions: "このPlayは以下の権限を要求しています" doYouWantToAllowThisPlayToAccessYourAccount: "このPlayによるアカウントへのアクセスを許可しますか?" @@ -1215,8 +1216,7 @@ _tlTutorial: step1_1: '{icon} ホームタイムラインは、あなたがフォローしているアカウントの投稿を見られます。' step1_2: '{icon} ローカルタイムラインでは、このサーバーにいるみんなの投稿を見られます。' step1_3: '{icon} ソーシャルタイムラインでは、ホームタイムラインとローカルタイムラインの投稿が両方表示されます。' - step1_4: '{icon} キャットタイムラインでは、ソーシャルタイムラインでキャットを有効にしたユーザーの投稿を見ることができます。' - step1_5: '{icon} グローバルタイムラインでは、このサーバーに接続されているすべてのサーバーからの投稿を見られます。' + step1_4: '{icon} グローバルタイムラインでは、このサーバーに接続されているすべてのサーバーからの投稿を見られます。' _announcement: forExistingUsers: "既存ユーザーのみ" @@ -1629,7 +1629,6 @@ _role: _options: gtlAvailable: "グローバルタイムラインの閲覧" ltlAvailable: "ローカルタイムラインの閲覧" - ctlAvailable: "キャットタイムラインの閲覧" canPublicNote: "パブリック投稿の許可" canEditNote: "ノートの編集" canInvite: "サーバー招待コードの発行" @@ -2258,9 +2257,7 @@ _instanceCharts: _timelines: home: "ホーム" local: "ローカル" - media: "メディア" social: "ソーシャル" - cat: "キャット" global: "グローバル" _play: diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index bcb28c6820..511c129ca0 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1186,6 +1186,7 @@ youHaveUnreadAnnouncements: "읽지 않은 공지사항이 있어요." useSecurityKey: "브라우저 또는 기기의 안내에 따라 보안 키 또는 패스키를 사용해 주세요." replies: "답글" renotes: "리노트" +showCatOnly: "고양이만 보기" additionalPermissionsForFlash: "Play에 대한 추가 권한" thisFlashRequiresTheFollowingPermissions: "이 Play는 다음 권한을 요구해요" doYouWantToAllowThisPlayToAccessYourAccount: "이 Play가 계정에 접근하도록 허용할까요?" @@ -1196,8 +1197,7 @@ _tlTutorial: step1_1: '{icon} 홈 타임라인은 내가 팔로우하고 있는 계정의 게시물을 볼 수 있어요.' step1_2: '{icon} 로컬 타임라인은 이 서버의 모든 유저가 올린 게시물을 볼 수 있어요.' step1_3: '{icon} 소셜 타임라인은 홈 타임라인과 로컬 타임라인을 합친 것과 같아요.' - step1_4: '{icon} 고양이 타임라인에서는 소셜 타임라인에서 고양이를 활성화한 유저의 게시물을 볼 수 있어요.' - step1_5: '{icon} 글로벌 타임라인에서는 이 서버와 연결된 모든 서버의 게시물을 볼 수 있어요.' + step1_4: '{icon} 글로벌 타임라인에서는 이 서버와 연결된 모든 서버의 게시물을 볼 수 있어요.' _announcement: forExistingUsers: "기존 유저에게만 알리기" forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시해요. 비활성화하면 게시 후에 가입한 유저에게도 표시해요." @@ -1588,7 +1588,6 @@ _role: _options: gtlAvailable: "글로벌 타임라인 보이기" ltlAvailable: "로컬 타임라인 보이기" - ctlAvailable: "고양이 타임라인 보이기" canPublicNote: "공개 노트 허용" canInvite: "서버 초대 코드 발행" inviteLimit: "초대 한도" @@ -2171,9 +2170,7 @@ _instanceCharts: _timelines: home: "홈" local: "로컬" - media: "미디어" social: "소셜" - cat: "고양이" global: "글로벌" _play: new: "Play 만들기" diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index ddd97db653..e5e69606b7 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -111,7 +111,6 @@ export class NodeinfoServerService { feedbackUrl: meta.feedbackUrl, disableRegistration: meta.disableRegistration, disableLocalTimeline: !basePolicies.ltlAvailable, - disableCatTimeline: !basePolicies.ctlAvailable, disableGlobalTimeline: !basePolicies.gtlAvailable, emailRequiredForSignup: meta.emailRequiredForSignup, enableHcaptcha: meta.enableHcaptcha, diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index 15032b333b..4f045fb35e 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -35,7 +35,6 @@ import { HashtagChannelService } from './api/stream/channels/hashtag.js'; import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js'; import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js'; import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js'; -import { CatTimelineChannelService } from './api/stream/channels/cat-timeline.js'; import { MessagingIndexChannelService } from './api/stream/channels/messaging-index.js'; import { MessagingChannelService } from './api/stream/channels/messaging.js'; import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js'; @@ -83,7 +82,6 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; HomeTimelineChannelService, HybridTimelineChannelService, LocalTimelineChannelService, - CatTimelineChannelService, MessagingIndexChannelService, MessagingChannelService, QueueStatsChannelService, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 6821233db3..1c7dae0336 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -279,7 +279,6 @@ import * as ep___notes_featured from './endpoints/notes/featured.js'; import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; -import * as ep___notes_catTimeline from './endpoints/notes/cat-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; @@ -657,7 +656,6 @@ const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep__ const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }; const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }; const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }; -const $notes_catTimeline: Provider = { provide: 'ep:notes/cat-timeline', useClass: ep___notes_catTimeline.default }; const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }; const $notes_polls_recommendation: Provider = { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }; const $notes_polls_vote: Provider = { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }; @@ -1040,7 +1038,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_globalTimeline, $notes_hybridTimeline, $notes_localTimeline, - $notes_catTimeline, $notes_mentions, $notes_polls_recommendation, $notes_polls_vote, @@ -1415,7 +1412,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_globalTimeline, $notes_hybridTimeline, $notes_localTimeline, - $notes_catTimeline, $notes_mentions, $notes_polls_recommendation, $notes_polls_vote, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 4eb6d159f9..9800150efa 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -278,7 +278,6 @@ import * as ep___notes_featured from './endpoints/notes/featured.js'; import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; -import * as ep___notes_catTimeline from './endpoints/notes/cat-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; @@ -654,7 +653,6 @@ const eps = [ ['notes/global-timeline', ep___notes_globalTimeline], ['notes/hybrid-timeline', ep___notes_hybridTimeline], ['notes/local-timeline', ep___notes_localTimeline], - ['notes/cat-timeline', ep___notes_catTimeline], ['notes/mentions', ep___notes_mentions], ['notes/polls/recommendation', ep___notes_polls_recommendation], ['notes/polls/vote', ep___notes_polls_vote], diff --git a/packages/backend/src/server/api/endpoints/notes/cat-timeline.ts b/packages/backend/src/server/api/endpoints/notes/cat-timeline.ts deleted file mode 100644 index 9a773b545a..0000000000 --- a/packages/backend/src/server/api/endpoints/notes/cat-timeline.ts +++ /dev/null @@ -1,163 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and noridev and other misskey, cherrypick contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Brackets } from 'typeorm'; -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, FollowingsRepository } from '@/models/_.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueryService } from '@/core/QueryService.js'; -import ActiveUsersChart from '@/core/chart/charts/active-users.js'; -import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { DI } from '@/di-symbols.js'; -import { RoleService } from '@/core/RoleService.js'; -import { IdService } from '@/core/IdService.js'; -import { ApiError } from '../../error.js'; - -export const meta = { - tags: ['notes'], - - requireCredential: true, - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'Note', - }, - }, - - errors: { - ctlDisabled: { - message: 'Cat timeline has been disabled.', - code: 'CTL_DISABLED', - id: '620763f4-f621-4533-ab33-0577a1a3c342', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - sinceId: { type: 'string', format: 'misskey:id' }, - untilId: { type: 'string', format: 'misskey:id' }, - sinceDate: { type: 'integer' }, - untilDate: { type: 'integer' }, - includeMyRenotes: { type: 'boolean', default: true }, - includeRenotedMyNotes: { type: 'boolean', default: true }, - includeLocalRenotes: { type: 'boolean', default: true }, - withFiles: { type: 'boolean', default: false }, - withReplies: { type: 'boolean', default: false }, - withRenotes: { type: 'boolean', default: true }, - }, - required: [], -} as const; - -@Injectable() -export default class extends Endpoint { // eslint-disable-line import/no-default-export - constructor( - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.followingsRepository) - private followingsRepository: FollowingsRepository, - - private noteEntityService: NoteEntityService, - private queryService: QueryService, - private roleService: RoleService, - private activeUsersChart: ActiveUsersChart, - private idService: IdService, - ) { - super(meta, paramDef, async (ps, me) => { - const policies = await this.roleService.getUserPolicies(me.id); - if (!policies.ctlAvailable) { - throw new ApiError(meta.errors.ctlDisabled); - } - - //#region Construct query - const followingQuery = this.followingsRepository.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); - - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで - .andWhere(new Brackets(qb => { - qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) - .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); - })) - .andWhere('(select "isCat" from "user" where id = note."userId")') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .setParameters(followingQuery.getParameters()); - - this.queryService.generateChannelQuery(query, me); - this.queryService.generateRepliesQuery(query, ps.withReplies, me); - this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateMutedNoteQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - - if (ps.withRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere(new Brackets(qb => { - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - })); - })); - } - //#endregion - - const timeline = await query.limit(ps.limit).getMany(); - - process.nextTick(() => { - this.activeUsersChart.read(me); - }); - - return await this.noteEntityService.packMany(timeline, me); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 2baac534cc..e2751925de 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -42,6 +42,7 @@ export const paramDef = { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withCats: { type: 'boolean', default: false }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -100,6 +101,10 @@ export default class extends Endpoint { // eslint- })); })); } + + if (ps.withCats) { + query.andWhere('(select "isCat" from "user" where id = note."userId")'); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index f83b4dea78..eb698d4826 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -53,6 +53,7 @@ export const paramDef = { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withCats: { type: 'boolean', default: false }, }, required: [], } as const; @@ -148,6 +149,10 @@ export default class extends Endpoint { // eslint- })); })); } + + if (ps.withCats) { + query.andWhere('(select "isCat" from "user" where id = note."userId")'); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 381d20e3eb..c8e8a14e2e 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -43,6 +43,7 @@ export const paramDef = { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withCats: { type: 'boolean', default: false }, fileType: { type: 'array', items: { type: 'string', } }, @@ -121,6 +122,10 @@ export default class extends Endpoint { // eslint- })); })); } + + if (ps.withCats) { + query.andWhere('(select "isCat" from "user" where id = note."userId")'); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 1ff0d4ed49..2d2838b7df 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -43,6 +43,7 @@ export const paramDef = { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withCats: { type: 'boolean', default: false }, }, required: [], } as const; @@ -137,6 +138,10 @@ export default class extends Endpoint { // eslint- })); })); } + + if (ps.withCats) { + query.andWhere('(select "isCat" from "user" where id = note."userId")'); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 424d8027fc..bc2b56fd7d 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -56,6 +56,7 @@ export const paramDef = { default: false, description: 'Only show notes that have attached files.', }, + withCats: { type: 'boolean', default: false }, }, required: ['listId'], } as const; @@ -149,6 +150,10 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withCats) { + query.andWhere('(select "isCat" from "user" where id = note."userId")'); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index c618de0df7..663cc94637 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -43,6 +43,7 @@ export const paramDef = { userId: { type: 'string', format: 'misskey:id' }, withReplies: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withCats: { type: 'boolean', default: false }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -139,6 +140,10 @@ export default class extends Endpoint { // eslint- })); } + if (ps.withCats) { + query.andWhere('(select "isCat" from "user" where id = note."userId")'); + } + //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index fbf0387f05..0a75f8b09a 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -7,7 +7,6 @@ import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import { HybridTimelineChannelService } from './channels/hybrid-timeline.js'; import { LocalTimelineChannelService } from './channels/local-timeline.js'; -import { CatTimelineChannelService } from './channels/cat-timeline.js'; import { HomeTimelineChannelService } from './channels/home-timeline.js'; import { GlobalTimelineChannelService } from './channels/global-timeline.js'; import { MainChannelService } from './channels/main.js'; @@ -29,7 +28,6 @@ export class ChannelsService { private mainChannelService: MainChannelService, private homeTimelineChannelService: HomeTimelineChannelService, private localTimelineChannelService: LocalTimelineChannelService, - private catTimelineChannelService: CatTimelineChannelService, private hybridTimelineChannelService: HybridTimelineChannelService, private globalTimelineChannelService: GlobalTimelineChannelService, private userListChannelService: UserListChannelService, @@ -53,7 +51,6 @@ export class ChannelsService { case 'homeTimeline': return this.homeTimelineChannelService; case 'localTimeline': return this.localTimelineChannelService; case 'hybridTimeline': return this.hybridTimelineChannelService; - case 'catTimeline': return this.catTimelineChannelService; case 'globalTimeline': return this.globalTimelineChannelService; case 'userList': return this.userListChannelService; case 'hashtag': return this.hashtagChannelService; diff --git a/packages/backend/src/server/api/stream/channels/cat-timeline.ts b/packages/backend/src/server/api/stream/channels/cat-timeline.ts deleted file mode 100644 index d8dbf4c9e8..0000000000 --- a/packages/backend/src/server/api/stream/channels/cat-timeline.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and noridev and other misskey, cherrypick contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Injectable } from '@nestjs/common'; -import { checkWordMute } from '@/misc/check-word-mute.js'; -import { isUserRelated } from '@/misc/is-user-related.js'; -import { isInstanceMuted } from '@/misc/is-instance-muted.js'; -import type { Packed } from '@/misc/json-schema.js'; -import { MetaService } from '@/core/MetaService.js'; -import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { bindThis } from '@/decorators.js'; -import { RoleService } from '@/core/RoleService.js'; -import Channel from '../channel.js'; - -class CatTimelineChannel extends Channel { - public readonly chName = 'catTimeline'; - public static shouldShare = true; - public static requireCredential = true; - private withReplies: boolean; - private withRenotes: boolean; - - constructor( - private metaService: MetaService, - private roleService: RoleService, - private noteEntityService: NoteEntityService, - - id: string, - connection: Channel['connection'], - ) { - super(id, connection); - //this.onNote = this.onNote.bind(this); - } - - @bindThis - public async init(params: any): Promise { - const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); - if (!policies.ctlAvailable) return; - - this.withReplies = params.withReplies ?? false; - this.withRenotes = params.withRenotes ?? true; - - // Subscribe events - this.subscriber.on('notesStream', this.onNote); - } - - @bindThis - private async onNote(note: Packed<'Note'>) { - // チャンネルの投稿ではなく、自分自身の投稿 または - // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または - // チャンネルの投稿ではなく、全体公開のローカルの投稿 または - // フォローしているチャンネルの投稿 の場合だけ - if (!( - (note.channelId == null && this.user!.id === note.userId) || - (note.channelId == null && this.following.has(note.userId)) || - (note.channelId == null && (note.user.host == null && note.visibility === 'public')) || - (note.channelId != null && this.followingChannels.has(note.channelId)) - )) return; - - if (['followers', 'specified'].includes(note.visibility)) { - note = await this.noteEntityService.pack(note.id, this.user!, { - detail: true, - }); - - if (note.isHidden) { - return; - } - } else { - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user!, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, { - detail: true, - }); - } - } - - // Ignore notes from instances the user has muted - if (isInstanceMuted(note, new Set(this.userProfile!.mutedInstances ?? []))) return; - - // 関係ない返信は除外 - if (note.reply && !this.withReplies) { - const reply = note.reply; - // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 - if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; - } - - if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; - - // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する - if (isUserRelated(note, this.userIdsWhoMeMuting)) return; - // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する - if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; - - if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; - - // 流れてきたNoteがミュートすべきNoteだったら無視する - // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) - // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 - // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 - // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる - if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; - - this.connection.cacheNote(note); - - this.send('note', note); - } - - @bindThis - public dispose(): void { - // Unsubscribe events - this.subscriber.off('notesStream', this.onNote); - } -} - -@Injectable() -export class CatTimelineChannelService { - public readonly shouldShare = CatTimelineChannel.shouldShare; - public readonly requireCredential = CatTimelineChannel.requireCredential; - - constructor( - private metaService: MetaService, - private roleService: RoleService, - private noteEntityService: NoteEntityService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): CatTimelineChannel { - return new CatTimelineChannel( - this.metaService, - this.roleService, - this.noteEntityService, - id, - connection, - ); - } -} diff --git a/packages/cherrypick-js/etc/cherrypick-js.api.md b/packages/cherrypick-js/etc/cherrypick-js.api.md index 171d35477a..3fab0b3ae6 100644 --- a/packages/cherrypick-js/etc/cherrypick-js.api.md +++ b/packages/cherrypick-js/etc/cherrypick-js.api.md @@ -222,13 +222,6 @@ export type Channels = { }; receives: null; }; - catTimeline: { - params: null; - events: { - note: (payload: Note) => void; - }; - receives: null; - }; globalTimeline: { params: null; events: { @@ -2478,7 +2471,6 @@ type LiteInstanceMetadata = { feedbackUrl: string; disableRegistration: boolean; disableLocalTimeline: boolean; - disableCatTimeline: boolean; disableGlobalTimeline: boolean; driveCapacityPerLocalUserMb: number; driveCapacityPerRemoteUserMb: number; diff --git a/packages/cherrypick-js/src/entities.ts b/packages/cherrypick-js/src/entities.ts index 1313897f06..e1be77c5dc 100644 --- a/packages/cherrypick-js/src/entities.ts +++ b/packages/cherrypick-js/src/entities.ts @@ -332,7 +332,6 @@ export type LiteInstanceMetadata = { feedbackUrl: string; disableRegistration: boolean; disableLocalTimeline: boolean; - disableCatTimeline: boolean; disableGlobalTimeline: boolean; driveCapacityPerLocalUserMb: number; driveCapacityPerRemoteUserMb: number; diff --git a/packages/cherrypick-js/src/streaming.types.ts b/packages/cherrypick-js/src/streaming.types.ts index 9acec7aadf..ce29a00032 100644 --- a/packages/cherrypick-js/src/streaming.types.ts +++ b/packages/cherrypick-js/src/streaming.types.ts @@ -63,13 +63,6 @@ export type Channels = { }; receives: null; }; - catTimeline: { - params: null; - events: { - note: (payload: Note) => void; - }; - receives: null; - }; globalTimeline: { params: null; events: { diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 51c156d20e..9c405e4101 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -30,10 +30,12 @@ const props = withDefaults(defineProps<{ withRenotes?: boolean; withReplies?: boolean; onlyFiles?: boolean; + onlyCats?: boolean; }>(), { withRenotes: true, withReplies: false, onlyFiles: false, + onlyCats: false, }); const emit = defineEmits<{ @@ -93,11 +95,13 @@ if (props.src === 'antenna') { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, }; connection = stream.useChannel('homeTimeline', { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, }); connection.on('note', prepend); @@ -112,11 +116,13 @@ if (props.src === 'antenna') { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, }; connection = stream.useChannel('localTimeline', { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, }); connection.on('note', prepend); @@ -129,46 +135,37 @@ if (props.src === 'antenna') { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, }; connection = stream.useChannel('hybridTimeline', { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, }); connection.on('note', prepend); tlIcon = 'ti ti-universe'; tlHint = i18n.ts._tlTutorial.step1_3; tlHintClosed = defaultStore.state.tlSocialHintClosed; -} else if (props.src === 'cat') { - endpoint = 'notes/cat-timeline'; - query = { - withReplies: defaultStore.state.showTimelineReplies, - }; - connection = stream.useChannel('catTimeline', { - withReplies: defaultStore.state.showTimelineReplies, - }); - connection.on('note', prepend); - - tlIcon = 'ti ti-cat'; - tlHint = i18n.ts._tlTutorial.step1_4; - tlHintClosed = defaultStore.state.tlCatHintClosed; } else if (props.src === 'global') { endpoint = 'notes/global-timeline'; query = { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, }; connection = stream.useChannel('globalTimeline', { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, }); connection.on('note', prepend); tlIcon = 'ti ti-world'; - tlHint = i18n.ts._tlTutorial.step1_5; + tlHint = i18n.ts._tlTutorial.step1_4; tlHintClosed = defaultStore.state.tlGlobalHintClosed; } else if (props.src === 'mentions') { endpoint = 'notes/mentions'; @@ -192,12 +189,14 @@ if (props.src === 'antenna') { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, listId: props.list, }; connection = stream.useChannel('userList', { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, + withCats: props.onlyCats, listId: props.list, }); connection.on('note', prepend); @@ -229,15 +228,9 @@ function closeHint() { case 'local': defaultStore.set('tlLocalHintClosed', true); break; - case 'media': - defaultStore.set('tlMediaHintClosed', true); - break; case 'social': defaultStore.set('tlSocialHintClosed', true); break; - case 'cat': - defaultStore.set('tlCatHintClosed', true); - break; case 'global': defaultStore.set('tlGlobalHintClosed', true); break; diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index ff157bbbe6..46e210f5cb 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -105,7 +105,6 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'enableHomeTimeline', 'enableLocalTimeline', 'enableSocialTimeline', - 'enableCatTimeline', 'enableGlobalTimeline', 'enableListTimeline', 'enableAntennaTimeline', diff --git a/packages/frontend/src/pages/settings/timeline.vue b/packages/frontend/src/pages/settings/timeline.vue index 0b72d21070..f0bab1c3ec 100644 --- a/packages/frontend/src/pages/settings/timeline.vue +++ b/packages/frontend/src/pages/settings/timeline.vue @@ -11,7 +11,6 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._timelines.home }} {{ i18n.ts._timelines.local }} {{ i18n.ts._timelines.social }} - {{ i18n.ts._timelines.cat }} {{ i18n.ts._timelines.global }} @@ -56,7 +55,6 @@ function save() { const enableHomeTimeline = computed(defaultStore.makeGetterSetter('enableHomeTimeline')); const enableLocalTimeline = computed(defaultStore.makeGetterSetter('enableLocalTimeline')); const enableSocialTimeline = computed(defaultStore.makeGetterSetter('enableSocialTimeline')); -const enableCatTimeline = computed(defaultStore.makeGetterSetter('enableCatTimeline')); const enableGlobalTimeline = computed(defaultStore.makeGetterSetter('enableGlobalTimeline')); const enableListTimeline = computed(defaultStore.makeGetterSetter('enableListTimeline')); const enableAntennaTimeline = computed(defaultStore.makeGetterSetter('enableAntennaTimeline')); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index b909960111..03c78a7e82 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -33,12 +33,13 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -82,7 +83,6 @@ const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue')); const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); -const isCatTimelineAvailable = ($i == null && instance.policies.ctlAvailable) || ($i != null && $i.policies.ctlAvailable); const keymap = { 't': focus, }; @@ -96,6 +96,7 @@ const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src const withRenotes = $ref(true); const withReplies = $ref(false); const onlyFiles = $ref(false); +const onlyCats = $ref(false); const friendlyEnableNotifications = computed(defaultStore.makeGetterSetter('friendlyEnableNotifications')); const friendlyEnableWidgets = computed(defaultStore.makeGetterSetter('friendlyEnableWidgets')); @@ -160,7 +161,7 @@ async function chooseChannel(ev: MouseEvent): Promise { os.popupMenu(items, ev.currentTarget ?? ev.target); } -function saveSrc(newSrc: 'home' | 'local' | 'social' | 'cat' | 'global' | `list:${string}`): void { +function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global' | `list:${string}`): void { let userList = null; if (newSrc.startsWith('userList:')) { const id = newSrc.substring('userList:'.length); @@ -209,7 +210,6 @@ const headerActions = $computed(() => [{ ref: friendlyEnableNotifications, action: () => { friendlyEnableNotifications.value = !friendlyEnableNotifications.value; - reloadAsk; }, }, { type: 'switch', @@ -218,7 +218,6 @@ const headerActions = $computed(() => [{ ref: friendlyEnableWidgets, action: () => { friendlyEnableWidgets.value = !friendlyEnableWidgets.value; - reloadAsk; }, }, { type: 'switch', @@ -235,6 +234,11 @@ const headerActions = $computed(() => [{ text: i18n.ts.fileAttachedOnly, icon: 'ti ti-photo', ref: $$(onlyFiles), + }, { + type: 'switch', + text: i18n.ts.showCatOnly, + icon: 'ti ti-cat', + ref: $$(onlyCats), }], ev.currentTarget ?? ev.target); }, }]); @@ -259,11 +263,6 @@ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLis title: i18n.ts._timelines.social, icon: 'ti ti-universe', iconOnly: true, -}] : []), ...(isCatTimelineAvailable && defaultStore.state.enableCatTimeline ? [{ - key: 'cat', - title: i18n.ts._timelines.cat, - icon: 'ti ti-cat', - iconOnly: true, }] : [])] : []), ...(isGlobalTimelineAvailable && defaultStore.state.enableGlobalTimeline ? [{ key: 'global', title: i18n.ts._timelines.global, @@ -303,7 +302,7 @@ const headerTabsWhenNotLogin = $computed(() => [ definePageMetadata(computed(() => ({ title: i18n.ts.timeline, - icon: src === 'local' ? 'ti ti-planet' : src === 'social' ? 'ti ti-universe' : src === 'cat' ? 'ti ti-cat' : src === 'global' ? 'ti ti-world' : 'ti ti-home', + icon: src === 'local' ? 'ti ti-planet' : src === 'social' ? 'ti ti-universe' : src === 'global' ? 'ti ti-world' : 'ti ti-home', }))); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index c251e69668..3f0d7b3287 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -61,18 +61,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, - tlMediaHintClosed: { - where: 'device', - default: false, - }, tlSocialHintClosed: { where: 'device', default: false, }, - tlCatHintClosed: { - where: 'device', - default: false, - }, tlGlobalHintClosed: { where: 'device', default: false, @@ -189,7 +181,7 @@ export const defaultStore = markRaw(new Storage('base', { tl: { where: 'deviceAccount', default: { - src: 'home' as 'home' | 'local' | 'media' | 'social' | 'cat' | 'global' | `list:${string}`, + src: 'home' as 'home' | 'local' | 'social' | 'global' | `list:${string}`, userList: null as Misskey.entities.UserList | null, }, }, @@ -463,10 +455,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: true, }, - enableCatTimeline: { - where: 'device', - default: true, - }, enableGlobalTimeline: { where: 'device', default: true, diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts index e1468e1770..67649a90ab 100644 --- a/packages/frontend/src/ui/deck/deck-store.ts +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -29,10 +29,11 @@ export type Column = { channelId?: string; roleId?: string; excludeTypes?: typeof notificationTypes[number][]; - tl?: 'home' | 'local' | 'media' | 'social' | 'cat' | 'global'; + tl?: 'home' | 'local' | 'social' | 'global'; withRenotes?: boolean; withReplies?: boolean; onlyFiles?: boolean; + onlyCats?: boolean; }; export const deckStore = markRaw(new Storage('deck', { diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index c0c21b604d..396281dc06 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -9,12 +9,11 @@ SPDX-License-Identifier: AGPL-3.0-only - {{ column.name }} -
+

{{ i18n.ts._disabledTimeline.title }} @@ -24,11 +23,12 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -52,10 +52,10 @@ let disabled = $ref(false); const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable)); const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable)); -const isCatTimelineAvailable = (($i == null && instance.policies.ctlAvailable) || ($i != null && $i.policies.ctlAvailable)); const withRenotes = $ref(props.column.withRenotes ?? true); const withReplies = $ref(props.column.withReplies ?? false); const onlyFiles = $ref(props.column.onlyFiles ?? false); +const onlyCats = $ref(props.column.onlyCats ?? false); watch($$(withRenotes), v => { updateColumn(props.column.id, { @@ -75,13 +75,18 @@ watch($$(onlyFiles), v => { }); }); +watch($$(onlyCats), v => { + updateColumn(props.column.id, { + onlyCats: v, + }); +}); + onMounted(() => { if (props.column.tl == null) { setType(); } else if ($i) { disabled = ( (!((instance.policies.ltlAvailable) || ($i.policies.ltlAvailable)) && ['local', 'social'].includes(props.column.tl)) || - (!((instance.policies.ctlAvailable) || ($i.policies.ctlAvailable)) && ['cat'].includes(props.column.tl)) || (!((instance.policies.gtlAvailable) || ($i.policies.gtlAvailable)) && ['global'].includes(props.column.tl))); } }); @@ -95,8 +100,6 @@ async function setType() { value: 'local' as const, text: i18n.ts._timelines.local, }, { value: 'social' as const, text: i18n.ts._timelines.social, - }, { - value: 'cat' as const, text: i18n.ts._timelines.cat, }, { value: 'global' as const, text: i18n.ts._timelines.global, }], @@ -128,6 +131,10 @@ const menu = [{ type: 'switch', text: i18n.ts.fileAttachedOnly, ref: $$(onlyFiles), +}, { + type: 'switch', + text: i18n.ts.showCatOnly, + ref: $$(onlyCats), }]; diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index 4280d0ef54..a493ea267a 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only -

+

{{ i18n.ts._disabledTimeline.title }} @@ -48,7 +48,6 @@ import { instance } from '@/instance.js'; const name = 'timeline'; const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable)); const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable)); -const isCatTimelineAvailable = (($i == null && instance.policies.ctlAvailable) || ($i != null && $i.policies.ctlAvailable)); const widgetPropsDef = { showHeader: { @@ -128,10 +127,6 @@ const choose = async (ev) => { text: i18n.ts._timelines.social, icon: 'ti ti-universe', action: () => { setSrc('social'); }, - }, { - text: i18n.ts._timelines.cat, - icon: 'ti ti-cat', - action: () => { setSrc('cat'); }, }, { text: i18n.ts._timelines.global, icon: 'ti ti-world',