Skip to content

Commit

Permalink
enhance: タイムラインからRenoteを除外するオプションを追加
Browse files Browse the repository at this point in the history
  • Loading branch information
syuilo committed Sep 28, 2023
1 parent d854942 commit eb740e2
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
## next

### General
- Enhance: タイムラインからRenoteを除外するオプションを追加
- Enhance: ユーザーページのノート一覧でRenoteを除外できるように

### Client
Expand Down
1 change: 1 addition & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,7 @@ export interface Locale {
"authentication": string;
"authenticationRequiredToContinue": string;
"dateAndTime": string;
"showRenotes": string;
"_announcement": {
"forExistingUsers": string;
"forExistingUsersDescription": string;
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,7 @@ unnotifyNotes: "投稿の通知を解除"
authentication: "認証"
authenticationRequiredToContinue: "続けるには認証を行ってください"
dateAndTime: "日時"
showRenotes: "リノートを表示"

_announcement:
forExistingUsers: "既存ユーザーのみ"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export const paramDef = {
includeMyRenotes: { type: 'boolean', default: true },
includeRenotedMyNotes: { type: 'boolean', default: true },
includeLocalRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withFiles: {
type: 'boolean',
default: false,
Expand Down Expand Up @@ -130,6 +132,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}));
}

if (!ps.withReplies) {
query.andWhere('note.replyId IS NULL');
}

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 != \'{}\'');
}));
}));
}

if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
}
Expand Down
8 changes: 4 additions & 4 deletions packages/backend/src/server/api/endpoints/users/notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export const paramDef = {
type: 'object',
properties: {
userId: { type: 'string', format: 'misskey:id' },
includeReplies: { type: 'boolean', default: true },
includeRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
Expand Down Expand Up @@ -115,11 +115,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
}

if (!ps.includeReplies) {
if (!ps.withReplies) {
query.andWhere('note.replyId IS NULL');
}

if (ps.includeRenotes === false) {
if (ps.withRenotes === false) {
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteId IS NULL');
qb.orWhere(new Brackets(qb => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class GlobalTimelineChannel extends Channel {
public static shouldShare = true;
public static requireCredential = false;
private withReplies: boolean;
private withRenotes: boolean;

constructor(
private metaService: MetaService,
Expand All @@ -37,7 +38,8 @@ class GlobalTimelineChannel extends Channel {
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
if (!policies.gtlAvailable) return;

this.withReplies = params.withReplies as boolean;
this.withReplies = params.withReplies ?? false;
this.withRenotes = params.withRenotes ?? true;

// Subscribe events
this.subscriber.on('notesStream', this.onNote);
Expand Down Expand Up @@ -68,6 +70,8 @@ class GlobalTimelineChannel extends Channel {
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;

// Ignore notes from instances the user has muted
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class HomeTimelineChannel extends Channel {
public static shouldShare = true;
public static requireCredential = true;
private withReplies: boolean;
private withRenotes: boolean;

constructor(
private noteEntityService: NoteEntityService,
Expand All @@ -30,7 +31,8 @@ class HomeTimelineChannel extends Channel {

@bindThis
public async init(params: any) {
this.withReplies = params.withReplies as boolean;
this.withReplies = params.withReplies ?? false;
this.withRenotes = params.withRenotes ?? true;

this.subscriber.on('notesStream', this.onNote);
}
Expand Down Expand Up @@ -77,6 +79,8 @@ class HomeTimelineChannel extends Channel {
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がブロックされているユーザーが関わるものだったら無視する
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class HybridTimelineChannel extends Channel {
public static shouldShare = true;
public static requireCredential = true;
private withReplies: boolean;
private withRenotes: boolean;

constructor(
private metaService: MetaService,
Expand All @@ -37,7 +38,8 @@ class HybridTimelineChannel extends Channel {
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
if (!policies.ltlAvailable) return;

this.withReplies = params.withReplies as boolean;
this.withReplies = params.withReplies ?? false;
this.withRenotes = params.withRenotes ?? true;

// Subscribe events
this.subscriber.on('notesStream', this.onNote);
Expand Down Expand Up @@ -89,6 +91,8 @@ class HybridTimelineChannel extends Channel {
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がブロックされているユーザーが関わるものだったら無視する
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class LocalTimelineChannel extends Channel {
public static shouldShare = true;
public static requireCredential = false;
private withReplies: boolean;
private withRenotes: boolean;

constructor(
private metaService: MetaService,
Expand All @@ -36,7 +37,8 @@ class LocalTimelineChannel extends Channel {
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
if (!policies.ltlAvailable) return;

this.withReplies = params.withReplies as boolean;
this.withReplies = params.withReplies ?? false;
this.withRenotes = params.withRenotes ?? true;

// Subscribe events
this.subscriber.on('notesStream', this.onNote);
Expand Down Expand Up @@ -68,6 +70,8 @@ class LocalTimelineChannel extends Channel {
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がブロックされているユーザーが関わるものだったら無視する
Expand Down
37 changes: 27 additions & 10 deletions packages/frontend/src/components/MkTimeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@ import * as sound from '@/scripts/sound.js';
import { $i } from '@/account.js';
import { defaultStore } from '@/store.js';

const props = defineProps<{
const props = withDefaults(defineProps<{
src: string;
list?: string;
antenna?: string;
channel?: string;
role?: string;
sound?: boolean;
}>();
withRenotes?: boolean;
withReplies?: boolean;
}>(), {
withRenotes: true,
withReplies: false,
});

const emit = defineEmits<{
(ev: 'note'): void;
Expand Down Expand Up @@ -62,39 +67,47 @@ if (props.src === 'antenna') {
} else if (props.src === 'home') {
endpoint = 'notes/timeline';
query = {
withReplies: defaultStore.state.showTimelineReplies,
withRenotes: props.withRenotes,
withReplies: props.withReplies,
};
connection = stream.useChannel('homeTimeline', {
withReplies: defaultStore.state.showTimelineReplies,
withRenotes: props.withRenotes,
withReplies: props.withReplies,
});
connection.on('note', prepend);

connection2 = stream.useChannel('main');
} else if (props.src === 'local') {
endpoint = 'notes/local-timeline';
query = {
withReplies: defaultStore.state.showTimelineReplies,
withRenotes: props.withRenotes,
withReplies: props.withReplies,
};
connection = stream.useChannel('localTimeline', {
withReplies: defaultStore.state.showTimelineReplies,
withRenotes: props.withRenotes,
withReplies: props.withReplies,
});
connection.on('note', prepend);
} else if (props.src === 'social') {
endpoint = 'notes/hybrid-timeline';
query = {
withReplies: defaultStore.state.showTimelineReplies,
withRenotes: props.withRenotes,
withReplies: props.withReplies,
};
connection = stream.useChannel('hybridTimeline', {
withReplies: defaultStore.state.showTimelineReplies,
withRenotes: props.withRenotes,
withReplies: props.withReplies,
});
connection.on('note', prepend);
} else if (props.src === 'global') {
endpoint = 'notes/global-timeline';
query = {
withReplies: defaultStore.state.showTimelineReplies,
withRenotes: props.withRenotes,
withReplies: props.withReplies,
};
connection = stream.useChannel('globalTimeline', {
withReplies: defaultStore.state.showTimelineReplies,
withRenotes: props.withRenotes,
withReplies: props.withReplies,
});
connection.on('note', prepend);
} else if (props.src === 'mentions') {
Expand All @@ -116,9 +129,13 @@ if (props.src === 'antenna') {
} else if (props.src === 'list') {
endpoint = 'notes/user-list-timeline';
query = {
withRenotes: props.withRenotes,
withReplies: props.withReplies,
listId: props.list,
};
connection = stream.useChannel('userList', {
withRenotes: props.withRenotes,
withReplies: props.withReplies,
listId: props.list,
});
connection.on('note', prepend);
Expand Down
2 changes: 0 additions & 2 deletions packages/frontend/src/pages/settings/general.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_s">
<MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch>
<MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch>
<MkSwitch v-model="showTimelineReplies">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></MkSwitch>
<MkFolder>
<template #label>{{ i18n.ts.pinnedList }}</template>
<!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ -->
Expand Down Expand Up @@ -249,7 +248,6 @@ const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars'));
const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance'));
const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies'));
const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));

watch(lang, () => {
Expand Down
24 changes: 22 additions & 2 deletions packages/frontend/src/pages/timeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.tl">
<MkTimeline
ref="tlComponent"
:key="src"
:key="src + withRenotes + withReplies"
:src="src.split(':')[0]"
:list="src.split(':')[1]"
:withRenotes="withRenotes"
:withReplies="withReplies"
:sound="true"
@queue="queueUpdated"
/>
Expand Down Expand Up @@ -58,6 +60,8 @@ const rootEl = $shallowRef<HTMLElement>();
let queue = $ref(0);
let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global');
const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) });
const withRenotes = $ref(true);
const withReplies = $ref(false);

watch($$(src), () => queue = 0);

Expand Down Expand Up @@ -129,7 +133,23 @@ function focus(): void {
tlComponent.focus();
}

const headerActions = $computed(() => []);
const headerActions = $computed(() => [{
icon: 'ti ti-dots',
text: i18n.ts.options,
handler: (ev) => {
os.popupMenu([{
type: 'switch',
text: i18n.ts.showRenotes,
icon: 'ti ti-repeat',
ref: $$(withRenotes),
}, {
type: 'switch',
text: i18n.ts.withReplies,
icon: 'ti ti-arrow-back-up',
ref: $$(withReplies),
}], ev.currentTarget ?? ev.target);
},
}]);

const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({
key: 'list:' + l.id,
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/src/pages/user/index.timeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ const pagination = {
limit: 10,
params: computed(() => ({
userId: props.user.id,
includeRenotes: include.value === 'all',
includeReplies: include.value === 'all' || include.value === 'files',
withRenotes: include.value === 'all',
withReplies: include.value === 'all' || include.value === 'files',
withFiles: include.value === 'files',
})),
};
Expand Down
4 changes: 0 additions & 4 deletions packages/frontend/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,6 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'account',
default: [] as string[],
},
showTimelineReplies: {
where: 'account',
default: false,
},

menu: {
where: 'deviceAccount',
Expand Down

0 comments on commit eb740e2

Please sign in to comment.