fix: 🚸 make "show replies in timeline" work as expected
Co-authored-by: Syuilo <syuilotan@yahoo.co.jp>
This commit is contained in:
parent
50031f1150
commit
8f61ff7f33
|
@ -0,0 +1,15 @@
|
||||||
|
export class RemoveShowTimelineReplies1684206886988 {
|
||||||
|
name = "RemoveShowTimelineReplies1684206886988";
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "user" ADD "showTimelineReplies" boolean NOT NULL DEFAULT false`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,8 +63,6 @@ pub struct Model {
|
||||||
pub hide_online_status: bool,
|
pub hide_online_status: bool,
|
||||||
#[sea_orm(column_name = "isDeleted")]
|
#[sea_orm(column_name = "isDeleted")]
|
||||||
pub is_deleted: bool,
|
pub is_deleted: bool,
|
||||||
#[sea_orm(column_name = "showTimelineReplies")]
|
|
||||||
pub show_timeline_replies: bool,
|
|
||||||
#[sea_orm(column_name = "driveCapacityOverrideMb")]
|
#[sea_orm(column_name = "driveCapacityOverrideMb")]
|
||||||
pub drive_capacity_override_mb: Option<i32>,
|
pub drive_capacity_override_mb: Option<i32>,
|
||||||
#[sea_orm(column_name = "movedToUri")]
|
#[sea_orm(column_name = "movedToUri")]
|
||||||
|
|
|
@ -249,12 +249,6 @@ export class User {
|
||||||
})
|
})
|
||||||
public followersUri: string | null;
|
public followersUri: string | null;
|
||||||
|
|
||||||
@Column("boolean", {
|
|
||||||
default: false,
|
|
||||||
comment: "Whether to show users replying to other users in the timeline.",
|
|
||||||
})
|
|
||||||
public showTimelineReplies: boolean;
|
|
||||||
|
|
||||||
@Index({ unique: true })
|
@Index({ unique: true })
|
||||||
@Column("char", {
|
@Column("char", {
|
||||||
length: 16,
|
length: 16,
|
||||||
|
|
|
@ -567,7 +567,6 @@ export const UserRepository = db.getRepository(User).extend({
|
||||||
mutedInstances: profile!.mutedInstances,
|
mutedInstances: profile!.mutedInstances,
|
||||||
mutingNotificationTypes: profile!.mutingNotificationTypes,
|
mutingNotificationTypes: profile!.mutingNotificationTypes,
|
||||||
emailNotificationTypes: profile!.emailNotificationTypes,
|
emailNotificationTypes: profile!.emailNotificationTypes,
|
||||||
showTimelineReplies: user.showTimelineReplies || falsy,
|
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
|
||||||
|
|
|
@ -279,7 +279,6 @@ export async function createPerson(
|
||||||
tags,
|
tags,
|
||||||
isBot,
|
isBot,
|
||||||
isCat: (person as any).isCat === true,
|
isCat: (person as any).isCat === true,
|
||||||
showTimelineReplies: false,
|
|
||||||
}),
|
}),
|
||||||
)) as IRemoteUser;
|
)) as IRemoteUser;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ import { Brackets } from "typeorm";
|
||||||
|
|
||||||
export function generateRepliesQuery(
|
export function generateRepliesQuery(
|
||||||
q: SelectQueryBuilder<any>,
|
q: SelectQueryBuilder<any>,
|
||||||
me?: Pick<User, "id" | "showTimelineReplies"> | null,
|
withReplies: boolean,
|
||||||
|
me?: Pick<User, "id"> | null,
|
||||||
) {
|
) {
|
||||||
if (me == null) {
|
if (me == null) {
|
||||||
q.andWhere(
|
q.andWhere(
|
||||||
|
@ -20,25 +21,21 @@ export function generateRepliesQuery(
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else if (!me.showTimelineReplies) {
|
} else if (!withReplies) {
|
||||||
q.andWhere(
|
q.andWhere(
|
||||||
new Brackets((qb) => {
|
new Brackets((qb) => {
|
||||||
qb.where("note.replyId IS NULL") // 返信ではない
|
qb.where("note.replyId IS NULL") // 返信ではない
|
||||||
.orWhere("note.replyUserId = :meId", { meId: me.id }) // 返信だけど自分のノートへの返信
|
.orWhere("note.replyUserId = :meId", { meId: me.id }) // 返信だけど自分のノートへの返信
|
||||||
.orWhere(
|
.orWhere(
|
||||||
new Brackets((qb) => {
|
new Brackets((qb) => {
|
||||||
qb.where(
|
qb.where("note.replyId IS NOT NULL") // 返信だけど自分の行った返信
|
||||||
// 返信だけど自分の行った返信
|
.andWhere("note.userId = :meId", { meId: me.id });
|
||||||
"note.replyId IS NOT NULL",
|
|
||||||
).andWhere("note.userId = :meId", { meId: me.id });
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.orWhere(
|
.orWhere(
|
||||||
new Brackets((qb) => {
|
new Brackets((qb) => {
|
||||||
qb.where(
|
qb.where("note.replyId IS NOT NULL") // 返信だけど投稿者自身への返信
|
||||||
// 返信だけど投稿者自身への返信
|
.andWhere("note.replyUserId = note.userId");
|
||||||
"note.replyId IS NOT NULL",
|
|
||||||
).andWhere("note.replyUserId = note.userId");
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -106,7 +106,6 @@ export const paramDef = {
|
||||||
isBot: { type: "boolean" },
|
isBot: { type: "boolean" },
|
||||||
isCat: { type: "boolean" },
|
isCat: { type: "boolean" },
|
||||||
speakAsCat: { type: "boolean" },
|
speakAsCat: { type: "boolean" },
|
||||||
showTimelineReplies: { type: "boolean" },
|
|
||||||
injectFeaturedNote: { type: "boolean" },
|
injectFeaturedNote: { type: "boolean" },
|
||||||
receiveAnnouncementEmail: { type: "boolean" },
|
receiveAnnouncementEmail: { type: "boolean" },
|
||||||
alwaysMarkNsfw: { type: "boolean" },
|
alwaysMarkNsfw: { type: "boolean" },
|
||||||
|
@ -185,8 +184,6 @@ export default define(meta, paramDef, async (ps, _user, token) => {
|
||||||
if (typeof ps.publicReactions === "boolean")
|
if (typeof ps.publicReactions === "boolean")
|
||||||
profileUpdates.publicReactions = ps.publicReactions;
|
profileUpdates.publicReactions = ps.publicReactions;
|
||||||
if (typeof ps.isBot === "boolean") updates.isBot = ps.isBot;
|
if (typeof ps.isBot === "boolean") updates.isBot = ps.isBot;
|
||||||
if (typeof ps.showTimelineReplies === "boolean")
|
|
||||||
updates.showTimelineReplies = ps.showTimelineReplies;
|
|
||||||
if (typeof ps.carefulBot === "boolean")
|
if (typeof ps.carefulBot === "boolean")
|
||||||
profileUpdates.carefulBot = ps.carefulBot;
|
profileUpdates.carefulBot = ps.carefulBot;
|
||||||
if (typeof ps.autoAcceptFollowed === "boolean")
|
if (typeof ps.autoAcceptFollowed === "boolean")
|
||||||
|
|
|
@ -53,6 +53,11 @@ export const paramDef = {
|
||||||
untilId: { type: "string", format: "misskey:id" },
|
untilId: { type: "string", format: "misskey:id" },
|
||||||
sinceDate: { type: "integer" },
|
sinceDate: { type: "integer" },
|
||||||
untilDate: { type: "integer" },
|
untilDate: { type: "integer" },
|
||||||
|
withReplies: {
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
description: "Show replies in the timeline",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -87,7 +92,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
.leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar")
|
.leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar")
|
||||||
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
|
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
|
||||||
|
|
||||||
generateRepliesQuery(query, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
if (user) {
|
if (user) {
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
generateMutedNoteQuery(query, user);
|
||||||
|
|
|
@ -60,6 +60,11 @@ export const paramDef = {
|
||||||
default: false,
|
default: false,
|
||||||
description: "Only show notes that have attached files.",
|
description: "Only show notes that have attached files.",
|
||||||
},
|
},
|
||||||
|
withReplies: {
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
description: "Show replies in the timeline",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -104,7 +109,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
.setParameters(followingQuery.getParameters());
|
.setParameters(followingQuery.getParameters());
|
||||||
|
|
||||||
generateChannelQuery(query, user);
|
generateChannelQuery(query, user);
|
||||||
generateRepliesQuery(query, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
generateMutedNoteQuery(query, user);
|
||||||
|
|
|
@ -63,6 +63,11 @@ export const paramDef = {
|
||||||
untilId: { type: "string", format: "misskey:id" },
|
untilId: { type: "string", format: "misskey:id" },
|
||||||
sinceDate: { type: "integer" },
|
sinceDate: { type: "integer" },
|
||||||
untilDate: { type: "integer" },
|
untilDate: { type: "integer" },
|
||||||
|
withReplies: {
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
description: "Show replies in the timeline",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -97,7 +102,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
|
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
|
||||||
|
|
||||||
generateChannelQuery(query, user);
|
generateChannelQuery(query, user);
|
||||||
generateRepliesQuery(query, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
if (user) generateMutedUserQuery(query, user);
|
if (user) generateMutedUserQuery(query, user);
|
||||||
if (user) generateMutedNoteQuery(query, user);
|
if (user) generateMutedNoteQuery(query, user);
|
||||||
|
|
|
@ -63,6 +63,11 @@ export const paramDef = {
|
||||||
untilId: { type: "string", format: "misskey:id" },
|
untilId: { type: "string", format: "misskey:id" },
|
||||||
sinceDate: { type: "integer" },
|
sinceDate: { type: "integer" },
|
||||||
untilDate: { type: "integer" },
|
untilDate: { type: "integer" },
|
||||||
|
withReplies: {
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
description: "Show replies in the timeline",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -100,7 +105,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
|
.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
|
||||||
|
|
||||||
generateChannelQuery(query, user);
|
generateChannelQuery(query, user);
|
||||||
generateRepliesQuery(query, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
if (user) generateMutedUserQuery(query, user);
|
if (user) generateMutedUserQuery(query, user);
|
||||||
if (user) generateMutedNoteQuery(query, user);
|
if (user) generateMutedNoteQuery(query, user);
|
||||||
|
|
|
@ -54,6 +54,11 @@ export const paramDef = {
|
||||||
default: false,
|
default: false,
|
||||||
description: "Only show notes that have attached files.",
|
description: "Only show notes that have attached files.",
|
||||||
},
|
},
|
||||||
|
withReplies: {
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
description: "Show replies in the timeline",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -100,7 +105,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
.setParameters(followingQuery.getParameters());
|
.setParameters(followingQuery.getParameters());
|
||||||
|
|
||||||
generateChannelQuery(query, user);
|
generateChannelQuery(query, user);
|
||||||
generateRepliesQuery(query, user);
|
generateRepliesQuery(query, ps.withReplies, user);
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
generateMutedNoteQuery(query, user);
|
generateMutedNoteQuery(query, user);
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default class extends Channel {
|
||||||
public readonly chName = "globalTimeline";
|
public readonly chName = "globalTimeline";
|
||||||
public static shouldShare = true;
|
public static shouldShare = true;
|
||||||
public static requireCredential = false;
|
public static requireCredential = false;
|
||||||
|
private withReplies: boolean;
|
||||||
|
|
||||||
constructor(id: string, connection: Channel["connection"]) {
|
constructor(id: string, connection: Channel["connection"]) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
|
@ -22,6 +23,8 @@ export default class extends Channel {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.withReplies = params.withReplies as boolean;
|
||||||
|
|
||||||
// Subscribe events
|
// Subscribe events
|
||||||
this.subscriber.on("notesStream", this.onNote);
|
this.subscriber.on("notesStream", this.onNote);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +34,7 @@ export default class extends Channel {
|
||||||
if (note.channelId != null) return;
|
if (note.channelId != null) return;
|
||||||
|
|
||||||
// 関係ない返信は除外
|
// 関係ない返信は除外
|
||||||
if (note.reply && !this.user!.showTimelineReplies) {
|
if (note.reply && !this.withReplies) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -8,6 +8,7 @@ export default class extends Channel {
|
||||||
public readonly chName = "homeTimeline";
|
public readonly chName = "homeTimeline";
|
||||||
public static shouldShare = true;
|
public static shouldShare = true;
|
||||||
public static requireCredential = true;
|
public static requireCredential = true;
|
||||||
|
private withReplies: boolean;
|
||||||
|
|
||||||
constructor(id: string, connection: Channel["connection"]) {
|
constructor(id: string, connection: Channel["connection"]) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
|
@ -15,6 +16,8 @@ export default class extends Channel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init(params: any) {
|
public async init(params: any) {
|
||||||
|
this.withReplies = params.withReplies as boolean;
|
||||||
|
|
||||||
// Subscribe events
|
// Subscribe events
|
||||||
this.subscriber.on("notesStream", this.onNote);
|
this.subscriber.on("notesStream", this.onNote);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +42,7 @@ export default class extends Channel {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 関係ない返信は除外
|
// 関係ない返信は除外
|
||||||
if (note.reply && !this.user!.showTimelineReplies) {
|
if (note.reply && !this.withReplies) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default class extends Channel {
|
||||||
public readonly chName = "hybridTimeline";
|
public readonly chName = "hybridTimeline";
|
||||||
public static shouldShare = true;
|
public static shouldShare = true;
|
||||||
public static requireCredential = true;
|
public static requireCredential = true;
|
||||||
|
private withReplies: boolean;
|
||||||
|
|
||||||
constructor(id: string, connection: Channel["connection"]) {
|
constructor(id: string, connection: Channel["connection"]) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
|
@ -24,6 +25,8 @@ export default class extends Channel {
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this.withReplies = params.withReplies as boolean;
|
||||||
|
|
||||||
// Subscribe events
|
// Subscribe events
|
||||||
this.subscriber.on("notesStream", this.onNote);
|
this.subscriber.on("notesStream", this.onNote);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +59,7 @@ export default class extends Channel {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 関係ない返信は除外
|
// 関係ない返信は除外
|
||||||
if (note.reply && !this.user!.showTimelineReplies) {
|
if (note.reply && !this.withReplies) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -8,6 +8,7 @@ export default class extends Channel {
|
||||||
public readonly chName = "localTimeline";
|
public readonly chName = "localTimeline";
|
||||||
public static shouldShare = true;
|
public static shouldShare = true;
|
||||||
public static requireCredential = false;
|
public static requireCredential = false;
|
||||||
|
private withReplies: boolean;
|
||||||
|
|
||||||
constructor(id: string, connection: Channel["connection"]) {
|
constructor(id: string, connection: Channel["connection"]) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
|
@ -21,6 +22,8 @@ export default class extends Channel {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.withReplies = params.withReplies as boolean;
|
||||||
|
|
||||||
// Subscribe events
|
// Subscribe events
|
||||||
this.subscriber.on("notesStream", this.onNote);
|
this.subscriber.on("notesStream", this.onNote);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +35,7 @@ export default class extends Channel {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 関係ない返信は除外
|
// 関係ない返信は除外
|
||||||
if (note.reply && !this.user!.showTimelineReplies) {
|
if (note.reply && !this.withReplies) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default class extends Channel {
|
||||||
public readonly chName = "recommendedTimeline";
|
public readonly chName = "recommendedTimeline";
|
||||||
public static shouldShare = true;
|
public static shouldShare = true;
|
||||||
public static requireCredential = true;
|
public static requireCredential = true;
|
||||||
|
private withReplies: boolean;
|
||||||
|
|
||||||
constructor(id: string, connection: Channel["connection"]) {
|
constructor(id: string, connection: Channel["connection"]) {
|
||||||
super(id, connection);
|
super(id, connection);
|
||||||
|
@ -24,6 +25,8 @@ export default class extends Channel {
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this.withReplies = params.withReplies as boolean;
|
||||||
|
|
||||||
// Subscribe events
|
// Subscribe events
|
||||||
this.subscriber.on("notesStream", this.onNote);
|
this.subscriber.on("notesStream", this.onNote);
|
||||||
}
|
}
|
||||||
|
@ -54,7 +57,7 @@ export default class extends Channel {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 関係ない返信は除外
|
// 関係ない返信は除外
|
||||||
if (note.reply && !this.user!.showTimelineReplies) {
|
if (note.reply && !this.withReplies) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -44,12 +44,7 @@ describe("ユーザー", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
type MeDetailed = UserDetailedNotMe &
|
type MeDetailed = UserDetailedNotMe &
|
||||||
misskey.entities.MeDetailed & {
|
misskey.entities.MeDetailed
|
||||||
showTimelineReplies: boolean;
|
|
||||||
achievements: object[];
|
|
||||||
loggedInDays: number;
|
|
||||||
policies: object;
|
|
||||||
};
|
|
||||||
|
|
||||||
type User = MeDetailed & { token: string };
|
type User = MeDetailed & { token: string };
|
||||||
|
|
||||||
|
@ -172,9 +167,6 @@ describe("ユーザー", () => {
|
||||||
mutedInstances: user.mutedInstances,
|
mutedInstances: user.mutedInstances,
|
||||||
mutingNotificationTypes: user.mutingNotificationTypes,
|
mutingNotificationTypes: user.mutingNotificationTypes,
|
||||||
emailNotificationTypes: user.emailNotificationTypes,
|
emailNotificationTypes: user.emailNotificationTypes,
|
||||||
showTimelineReplies: user.showTimelineReplies,
|
|
||||||
achievements: user.achievements,
|
|
||||||
loggedInDays: user.loggedInDays,
|
|
||||||
policies: user.policies,
|
policies: user.policies,
|
||||||
...(security
|
...(security
|
||||||
? {
|
? {
|
||||||
|
@ -479,13 +471,6 @@ describe("ユーザー", () => {
|
||||||
"follow",
|
"follow",
|
||||||
"receiveFollowRequest",
|
"receiveFollowRequest",
|
||||||
]);
|
]);
|
||||||
assert.strictEqual(response.showTimelineReplies, false);
|
|
||||||
assert.deepStrictEqual(response.achievements, []);
|
|
||||||
assert.deepStrictEqual(response.loggedInDays, 0);
|
|
||||||
assert.deepStrictEqual(response.policies, DEFAULT_POLICIES);
|
|
||||||
assert.notStrictEqual(response.email, undefined);
|
|
||||||
assert.strictEqual(response.emailVerified, false);
|
|
||||||
assert.deepStrictEqual(response.securityKeysList, []);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -551,8 +536,6 @@ describe("ユーザー", () => {
|
||||||
{ parameters: (): object => ({ isBot: false }) },
|
{ parameters: (): object => ({ isBot: false }) },
|
||||||
{ parameters: (): object => ({ isCat: true }) },
|
{ parameters: (): object => ({ isCat: true }) },
|
||||||
{ parameters: (): object => ({ isCat: false }) },
|
{ parameters: (): object => ({ isCat: false }) },
|
||||||
{ parameters: (): object => ({ showTimelineReplies: true }) },
|
|
||||||
{ parameters: (): object => ({ showTimelineReplies: false }) },
|
|
||||||
{ parameters: (): object => ({ injectFeaturedNote: true }) },
|
{ parameters: (): object => ({ injectFeaturedNote: true }) },
|
||||||
{ parameters: (): object => ({ injectFeaturedNote: false }) },
|
{ parameters: (): object => ({ injectFeaturedNote: false }) },
|
||||||
{ parameters: (): object => ({ receiveAnnouncementEmail: true }) },
|
{ parameters: (): object => ({ receiveAnnouncementEmail: true }) },
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<button v-if="!hideMenu" class="menu _button" @click.stop="menu" v-tooltip="i18n.ts.menu">
|
<button
|
||||||
|
v-if="!hideMenu"
|
||||||
|
class="menu _button"
|
||||||
|
@click.stop="menu"
|
||||||
|
v-tooltip="i18n.ts.menu"
|
||||||
|
>
|
||||||
<i class="ph-dots-three-outline ph-bold ph-lg"></i>
|
<i class="ph-dots-three-outline ph-bold ph-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -91,7 +91,12 @@ if (props.src === "antenna") {
|
||||||
connection.on("note", prepend);
|
connection.on("note", prepend);
|
||||||
} else if (props.src === "home") {
|
} else if (props.src === "home") {
|
||||||
endpoint = "notes/timeline";
|
endpoint = "notes/timeline";
|
||||||
connection = stream.useChannel("homeTimeline");
|
query = {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
};
|
||||||
|
connection = stream.useChannel("homeTimeline", {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
});
|
||||||
connection.on("note", prepend);
|
connection.on("note", prepend);
|
||||||
|
|
||||||
connection2 = stream.useChannel("main");
|
connection2 = stream.useChannel("main");
|
||||||
|
@ -102,28 +107,48 @@ if (props.src === "antenna") {
|
||||||
tlHintClosed = defaultStore.state.tlHomeHintClosed;
|
tlHintClosed = defaultStore.state.tlHomeHintClosed;
|
||||||
} else if (props.src === "local") {
|
} else if (props.src === "local") {
|
||||||
endpoint = "notes/local-timeline";
|
endpoint = "notes/local-timeline";
|
||||||
connection = stream.useChannel("localTimeline");
|
query = {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
};
|
||||||
|
connection = stream.useChannel("localTimeline", {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
});
|
||||||
connection.on("note", prepend);
|
connection.on("note", prepend);
|
||||||
|
|
||||||
tlHint = i18n.ts._tutorial.step5_4;
|
tlHint = i18n.ts._tutorial.step5_4;
|
||||||
tlHintClosed = defaultStore.state.tlLocalHintClosed;
|
tlHintClosed = defaultStore.state.tlLocalHintClosed;
|
||||||
} else if (props.src === "recommended") {
|
} else if (props.src === "recommended") {
|
||||||
endpoint = "notes/recommended-timeline";
|
endpoint = "notes/recommended-timeline";
|
||||||
connection = stream.useChannel("recommendedTimeline");
|
query = {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
};
|
||||||
|
connection = stream.useChannel("recommendedTimeline", {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
});
|
||||||
connection.on("note", prepend);
|
connection.on("note", prepend);
|
||||||
|
|
||||||
tlHint = i18n.ts._tutorial.step5_6;
|
tlHint = i18n.ts._tutorial.step5_6;
|
||||||
tlHintClosed = defaultStore.state.tlRecommendedHintClosed;
|
tlHintClosed = defaultStore.state.tlRecommendedHintClosed;
|
||||||
} else if (props.src === "social") {
|
} else if (props.src === "social") {
|
||||||
endpoint = "notes/hybrid-timeline";
|
endpoint = "notes/hybrid-timeline";
|
||||||
connection = stream.useChannel("hybridTimeline");
|
query = {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
};
|
||||||
|
connection = stream.useChannel("hybridTimeline", {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
});
|
||||||
connection.on("note", prepend);
|
connection.on("note", prepend);
|
||||||
|
|
||||||
tlHint = i18n.ts._tutorial.step5_5;
|
tlHint = i18n.ts._tutorial.step5_5;
|
||||||
tlHintClosed = defaultStore.state.tlSocialHintClosed;
|
tlHintClosed = defaultStore.state.tlSocialHintClosed;
|
||||||
} else if (props.src === "global") {
|
} else if (props.src === "global") {
|
||||||
endpoint = "notes/global-timeline";
|
endpoint = "notes/global-timeline";
|
||||||
connection = stream.useChannel("globalTimeline");
|
query = {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
};
|
||||||
|
connection = stream.useChannel("globalTimeline", {
|
||||||
|
withReplies: defaultStore.state.showTimelineReplies,
|
||||||
|
});
|
||||||
connection.on("note", prepend);
|
connection.on("note", prepend);
|
||||||
|
|
||||||
tlHint = i18n.ts._tutorial.step5_7;
|
tlHint = i18n.ts._tutorial.step5_7;
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
<FormSwitch v-model="disablePagesScript" class="_formBlock">{{
|
<FormSwitch v-model="disablePagesScript" class="_formBlock">{{
|
||||||
i18n.ts.disablePagesScript
|
i18n.ts.disablePagesScript
|
||||||
}}</FormSwitch>
|
}}</FormSwitch>
|
||||||
<FormSwitch v-model="profile.showTimelineReplies" class="_formBlock"
|
<FormSwitch v-model="showTimelineReplies" class="_formBlock"
|
||||||
>{{ i18n.ts.flagShowTimelineReplies
|
>{{ i18n.ts.flagShowTimelineReplies
|
||||||
}}<template #caption
|
}}<template #caption
|
||||||
>{{ i18n.ts.flagShowTimelineRepliesDescription }}
|
>{{ i18n.ts.flagShowTimelineRepliesDescription }}
|
||||||
|
@ -258,24 +258,6 @@ const lang = ref(localStorage.getItem("lang"));
|
||||||
const fontSize = ref(localStorage.getItem("fontSize"));
|
const fontSize = ref(localStorage.getItem("fontSize"));
|
||||||
const useSystemFont = ref(localStorage.getItem("useSystemFont") != null);
|
const useSystemFont = ref(localStorage.getItem("useSystemFont") != null);
|
||||||
|
|
||||||
const profile = reactive({
|
|
||||||
showTimelineReplies: $i?.showTimelineReplies,
|
|
||||||
});
|
|
||||||
watch(
|
|
||||||
() => profile,
|
|
||||||
() => {
|
|
||||||
save();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
function save() {
|
|
||||||
os.apiWithDialog("i/update", {
|
|
||||||
showTimelineReplies: !!profile.showTimelineReplies,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function reloadAsk() {
|
async function reloadAsk() {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: "info",
|
type: "info",
|
||||||
|
@ -360,6 +342,9 @@ const swipeOnDesktop = computed(
|
||||||
const showAdminUpdates = computed(
|
const showAdminUpdates = computed(
|
||||||
defaultStore.makeGetterSetter("showAdminUpdates")
|
defaultStore.makeGetterSetter("showAdminUpdates")
|
||||||
);
|
);
|
||||||
|
const showTimelineReplies = computed(
|
||||||
|
defaultStore.makeGetterSetter("showTimelineReplies")
|
||||||
|
);
|
||||||
|
|
||||||
watch(lang, () => {
|
watch(lang, () => {
|
||||||
localStorage.setItem("lang", lang.value as string);
|
localStorage.setItem("lang", lang.value as string);
|
||||||
|
|
|
@ -115,6 +115,7 @@ const defaultStoreSaveKeys: (keyof (typeof defaultStore)["state"])[] = [
|
||||||
"enableCustomKaTeXMacro",
|
"enableCustomKaTeXMacro",
|
||||||
"enableEmojiReactions",
|
"enableEmojiReactions",
|
||||||
"showEmojisInReactionNotifications",
|
"showEmojisInReactionNotifications",
|
||||||
|
"showTimelineReplies",
|
||||||
];
|
];
|
||||||
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
|
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
|
||||||
"lightTheme",
|
"lightTheme",
|
||||||
|
|
|
@ -330,6 +330,10 @@ export const defaultStore = markRaw(
|
||||||
where: "account",
|
where: "account",
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
showTimelineReplies: {
|
||||||
|
where: "device",
|
||||||
|
default: true,
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,30 @@ import { markRaw } from "vue";
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
import { url } from "@/config";
|
import { url } from "@/config";
|
||||||
|
|
||||||
export const stream = markRaw(
|
let stream: Misskey.Stream | null = null;
|
||||||
new Misskey.Stream(
|
|
||||||
url,
|
export function useStream(): Misskey.Stream {
|
||||||
$i
|
if (stream) return stream;
|
||||||
? {
|
|
||||||
token: $i.token,
|
stream = markRaw(
|
||||||
}
|
new Misskey.Stream(
|
||||||
: null,
|
url,
|
||||||
),
|
$i
|
||||||
);
|
? {
|
||||||
|
token: $i.token,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
window.setTimeout(heartbeat, 1000 * 60);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
function heartbeat(): void {
|
||||||
|
if (stream != null && document.visibilityState === "visible") {
|
||||||
|
stream.send("ping");
|
||||||
|
}
|
||||||
|
window.setTimeout(heartbeat, 1000 * 60);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue