feat: 過去のノートを非公開化/フォロワーのみ表示可能にできる機能 (#14814)
* wip * Update CHANGELOG.md * wip * wip * wip * Update privacy.vue * wip
This commit is contained in:
parent
70b2a8f72e
commit
952fec5665
17 changed files with 282 additions and 43 deletions
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- Feat: コンテンツの表示にログインを必須にできるように
|
- Feat: コンテンツの表示にログインを必須にできるように
|
||||||
|
- Feat: 過去のノートを非公開化/フォロワーのみ表示可能にできるように
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Enhance: Bull DashboardでRelationship Queueの状態も確認できるように
|
- Enhance: Bull DashboardでRelationship Queueの状態も確認できるように
|
||||||
|
|
42
locales/index.d.ts
vendored
42
locales/index.d.ts
vendored
|
@ -3806,6 +3806,18 @@ export interface Locale extends ILocale {
|
||||||
* 1ヶ月
|
* 1ヶ月
|
||||||
*/
|
*/
|
||||||
"oneMonth": string;
|
"oneMonth": string;
|
||||||
|
/**
|
||||||
|
* 3ヶ月
|
||||||
|
*/
|
||||||
|
"threeMonths": string;
|
||||||
|
/**
|
||||||
|
* 1年
|
||||||
|
*/
|
||||||
|
"oneYear": string;
|
||||||
|
/**
|
||||||
|
* 3日
|
||||||
|
*/
|
||||||
|
"threeDays": string;
|
||||||
/**
|
/**
|
||||||
* 反映されるまで時間がかかる場合があります。
|
* 反映されるまで時間がかかる場合があります。
|
||||||
*/
|
*/
|
||||||
|
@ -5204,7 +5216,7 @@ export interface Locale extends ILocale {
|
||||||
*/
|
*/
|
||||||
"requireSigninToViewContents": string;
|
"requireSigninToViewContents": string;
|
||||||
/**
|
/**
|
||||||
* あなたが作成した全てのノートなどのコンテンツを表示するのにログインを必須にします。クローラーから情報を収集されるのを防ぐ効果が期待できます。
|
* あなたが作成した全てのノートなどのコンテンツを表示するのにログインを必須にします。クローラーに情報が収集されるのを防ぐ効果が期待できます。
|
||||||
*/
|
*/
|
||||||
"requireSigninToViewContentsDescription1": string;
|
"requireSigninToViewContentsDescription1": string;
|
||||||
/**
|
/**
|
||||||
|
@ -5215,6 +5227,34 @@ export interface Locale extends ILocale {
|
||||||
* リモートサーバーに連合されたコンテンツでは、これらの制限が適用されない場合があります。
|
* リモートサーバーに連合されたコンテンツでは、これらの制限が適用されない場合があります。
|
||||||
*/
|
*/
|
||||||
"requireSigninToViewContentsDescription3": string;
|
"requireSigninToViewContentsDescription3": string;
|
||||||
|
/**
|
||||||
|
* 過去のノートをフォロワーのみ表示可能にする
|
||||||
|
*/
|
||||||
|
"makeNotesFollowersOnlyBefore": string;
|
||||||
|
/**
|
||||||
|
* この機能が有効になっている間、設定された日時より過去、または設定された時間を経過しているノートがフォロワーのみ表示可能になります。無効に戻すと、ノートの公開状態も元に戻ります。
|
||||||
|
*/
|
||||||
|
"makeNotesFollowersOnlyBeforeDescription": string;
|
||||||
|
/**
|
||||||
|
* 過去のノートを非公開化する
|
||||||
|
*/
|
||||||
|
"makeNotesHiddenBefore": string;
|
||||||
|
/**
|
||||||
|
* この機能が有効になっている間、設定された日時より過去、または設定された時間を経過しているノートが自分のみ表示可能(非公開化)になります。無効に戻すと、ノートの公開状態も元に戻ります。
|
||||||
|
*/
|
||||||
|
"makeNotesHiddenBeforeDescription": string;
|
||||||
|
/**
|
||||||
|
* リモートサーバーに連合されたノートには効果が及ばない場合があります。
|
||||||
|
*/
|
||||||
|
"mayNotEffectForFederatedNotes": string;
|
||||||
|
/**
|
||||||
|
* 指定した時間を経過しているノート
|
||||||
|
*/
|
||||||
|
"notesHavePassedSpecifiedPeriod": string;
|
||||||
|
/**
|
||||||
|
* 指定した日時より前のノート
|
||||||
|
*/
|
||||||
|
"notesOlderThanSpecifiedDateAndTime": string;
|
||||||
};
|
};
|
||||||
"_abuseUserReport": {
|
"_abuseUserReport": {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -947,6 +947,9 @@ oneHour: "1時間"
|
||||||
oneDay: "1日"
|
oneDay: "1日"
|
||||||
oneWeek: "1週間"
|
oneWeek: "1週間"
|
||||||
oneMonth: "1ヶ月"
|
oneMonth: "1ヶ月"
|
||||||
|
threeMonths: "3ヶ月"
|
||||||
|
oneYear: "1年"
|
||||||
|
threeDays: "3日"
|
||||||
reflectMayTakeTime: "反映されるまで時間がかかる場合があります。"
|
reflectMayTakeTime: "反映されるまで時間がかかる場合があります。"
|
||||||
failedToFetchAccountInformation: "アカウント情報の取得に失敗しました"
|
failedToFetchAccountInformation: "アカウント情報の取得に失敗しました"
|
||||||
rateLimitExceeded: "レート制限を超えました"
|
rateLimitExceeded: "レート制限を超えました"
|
||||||
|
@ -1298,9 +1301,16 @@ lockdown: "ロックダウン"
|
||||||
|
|
||||||
_accountSettings:
|
_accountSettings:
|
||||||
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
|
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
|
||||||
requireSigninToViewContentsDescription1: "あなたが作成した全てのノートなどのコンテンツを表示するのにログインを必須にします。クローラーから情報を収集されるのを防ぐ効果が期待できます。"
|
requireSigninToViewContentsDescription1: "あなたが作成した全てのノートなどのコンテンツを表示するのにログインを必須にします。クローラーに情報が収集されるのを防ぐ効果が期待できます。"
|
||||||
requireSigninToViewContentsDescription2: "URLプレビュー(OGP)、Webページへの埋め込み、ノートの引用に対応していないサーバーからの表示も不可になります。"
|
requireSigninToViewContentsDescription2: "URLプレビュー(OGP)、Webページへの埋め込み、ノートの引用に対応していないサーバーからの表示も不可になります。"
|
||||||
requireSigninToViewContentsDescription3: "リモートサーバーに連合されたコンテンツでは、これらの制限が適用されない場合があります。"
|
requireSigninToViewContentsDescription3: "リモートサーバーに連合されたコンテンツでは、これらの制限が適用されない場合があります。"
|
||||||
|
makeNotesFollowersOnlyBefore: "過去のノートをフォロワーのみ表示可能にする"
|
||||||
|
makeNotesFollowersOnlyBeforeDescription: "この機能が有効になっている間、設定された日時より過去、または設定された時間を経過しているノートがフォロワーのみ表示可能になります。無効に戻すと、ノートの公開状態も元に戻ります。"
|
||||||
|
makeNotesHiddenBefore: "過去のノートを非公開化する"
|
||||||
|
makeNotesHiddenBeforeDescription: "この機能が有効になっている間、設定された日時より過去、または設定された時間を経過しているノートが自分のみ表示可能(非公開化)になります。無効に戻すと、ノートの公開状態も元に戻ります。"
|
||||||
|
mayNotEffectForFederatedNotes: "リモートサーバーに連合されたノートには効果が及ばない場合があります。"
|
||||||
|
notesHavePassedSpecifiedPeriod: "指定した時間を経過しているノート"
|
||||||
|
notesOlderThanSpecifiedDateAndTime: "指定した日時より前のノート"
|
||||||
|
|
||||||
_abuseUserReport:
|
_abuseUserReport:
|
||||||
forward: "転送"
|
forward: "転送"
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MakeNotesHiddenBefore1729486255072 {
|
||||||
|
name = 'MakeNotesHiddenBefore1729486255072'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user" ADD "makeNotesFollowersOnlyBefore" integer`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user" ADD "makeNotesHiddenBefore" integer`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "makeNotesHiddenBefore"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "makeNotesFollowersOnlyBefore"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,6 +84,8 @@ function generateDummyUser(override?: Partial<MiUser>): MiUser {
|
||||||
isHibernated: false,
|
isHibernated: false,
|
||||||
isDeleted: false,
|
isDeleted: false,
|
||||||
requireSigninToViewContents: false,
|
requireSigninToViewContents: false,
|
||||||
|
makeNotesFollowersOnlyBefore: null,
|
||||||
|
makeNotesHiddenBefore: null,
|
||||||
emojis: [],
|
emojis: [],
|
||||||
score: 0,
|
score: 0,
|
||||||
host: null,
|
host: null,
|
||||||
|
|
|
@ -496,6 +496,8 @@ export class ApRendererService {
|
||||||
_misskey_summary: profile.description,
|
_misskey_summary: profile.description,
|
||||||
_misskey_followedMessage: profile.followedMessage,
|
_misskey_followedMessage: profile.followedMessage,
|
||||||
_misskey_requireSigninToViewContents: user.requireSigninToViewContents,
|
_misskey_requireSigninToViewContents: user.requireSigninToViewContents,
|
||||||
|
_misskey_makeNotesFollowersOnlyBefore: user.makeNotesFollowersOnlyBefore,
|
||||||
|
_misskey_makeNotesHiddenBefore: user.makeNotesHiddenBefore,
|
||||||
icon: avatar ? this.renderImage(avatar) : null,
|
icon: avatar ? this.renderImage(avatar) : null,
|
||||||
image: banner ? this.renderImage(banner) : null,
|
image: banner ? this.renderImage(banner) : null,
|
||||||
tag,
|
tag,
|
||||||
|
|
|
@ -556,6 +556,8 @@ const extension_context_definition = {
|
||||||
'_misskey_summary': 'misskey:_misskey_summary',
|
'_misskey_summary': 'misskey:_misskey_summary',
|
||||||
'_misskey_followedMessage': 'misskey:_misskey_followedMessage',
|
'_misskey_followedMessage': 'misskey:_misskey_followedMessage',
|
||||||
'_misskey_requireSigninToViewContents': 'misskey:_misskey_requireSigninToViewContents',
|
'_misskey_requireSigninToViewContents': 'misskey:_misskey_requireSigninToViewContents',
|
||||||
|
'_misskey_makeNotesFollowersOnlyBefore': 'misskey:_misskey_makeNotesFollowersOnlyBefore',
|
||||||
|
'_misskey_makeNotesHiddenBefore': 'misskey:_misskey_makeNotesHiddenBefore',
|
||||||
'isCat': 'misskey:isCat',
|
'isCat': 'misskey:isCat',
|
||||||
// vcard
|
// vcard
|
||||||
vcard: 'http://www.w3.org/2006/vcard/ns#',
|
vcard: 'http://www.w3.org/2006/vcard/ns#',
|
||||||
|
|
|
@ -357,6 +357,8 @@ export class ApPersonService implements OnModuleInit {
|
||||||
isBot,
|
isBot,
|
||||||
isCat: (person as any).isCat === true,
|
isCat: (person as any).isCat === true,
|
||||||
requireSigninToViewContents: (person as any).requireSigninToViewContents === true,
|
requireSigninToViewContents: (person as any).requireSigninToViewContents === true,
|
||||||
|
makeNotesFollowersOnlyBefore: (person as any).makeNotesFollowersOnlyBefore ?? null,
|
||||||
|
makeNotesHiddenBefore: (person as any).makeNotesHiddenBefore ?? null,
|
||||||
emojis,
|
emojis,
|
||||||
})) as MiRemoteUser;
|
})) as MiRemoteUser;
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ export interface IObject {
|
||||||
_misskey_summary?: string;
|
_misskey_summary?: string;
|
||||||
_misskey_followedMessage?: string | null;
|
_misskey_followedMessage?: string | null;
|
||||||
_misskey_requireSigninToViewContents?: boolean;
|
_misskey_requireSigninToViewContents?: boolean;
|
||||||
|
_misskey_makeNotesFollowersOnlyBefore?: number | null;
|
||||||
|
_misskey_makeNotesHiddenBefore?: number | null;
|
||||||
published?: string;
|
published?: string;
|
||||||
cc?: ApObject;
|
cc?: ApObject;
|
||||||
to?: ApObject;
|
to?: ApObject;
|
||||||
|
|
|
@ -102,57 +102,83 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null) {
|
private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise<void> {
|
||||||
|
// FIXME: このvisibility変更処理が当関数にあるのは若干不自然かもしれない(関数名を treatVisibility とかに変える手もある)
|
||||||
|
if (packedNote.visibility === 'public' || packedNote.visibility === 'home') {
|
||||||
|
const followersOnlyBefore = packedNote.user.makeNotesFollowersOnlyBefore;
|
||||||
|
if ((followersOnlyBefore != null)
|
||||||
|
&& (
|
||||||
|
(followersOnlyBefore <= 0 && (Date.now() - new Date(packedNote.createdAt).getTime() > 0 - (followersOnlyBefore * 1000)))
|
||||||
|
|| (followersOnlyBefore > 0 && (new Date(packedNote.createdAt).getTime() < followersOnlyBefore * 1000))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
packedNote.visibility = 'followers';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meId === packedNote.userId) return;
|
||||||
|
|
||||||
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
|
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
|
||||||
let hide = false;
|
let hide = false;
|
||||||
|
|
||||||
// visibility が specified かつ自分が指定されていなかったら非表示
|
if (packedNote.user.requireSigninToViewContents && meId == null) {
|
||||||
if (packedNote.visibility === 'specified') {
|
hide = true;
|
||||||
if (meId == null) {
|
}
|
||||||
hide = true;
|
|
||||||
} else if (meId === packedNote.userId) {
|
|
||||||
hide = false;
|
|
||||||
} else {
|
|
||||||
// 指定されているかどうか
|
|
||||||
const specified = packedNote.visibleUserIds!.some(id => meId === id);
|
|
||||||
|
|
||||||
if (specified) {
|
if (!hide) {
|
||||||
hide = false;
|
const hiddenBefore = packedNote.user.makeNotesHiddenBefore;
|
||||||
} else {
|
if ((hiddenBefore != null)
|
||||||
|
&& (
|
||||||
|
(hiddenBefore <= 0 && (Date.now() - new Date(packedNote.createdAt).getTime() > 0 - (hiddenBefore * 1000)))
|
||||||
|
|| (hiddenBefore > 0 && (new Date(packedNote.createdAt).getTime() < hiddenBefore * 1000))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
hide = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// visibility が specified かつ自分が指定されていなかったら非表示
|
||||||
|
if (!hide) {
|
||||||
|
if (packedNote.visibility === 'specified') {
|
||||||
|
if (meId == null) {
|
||||||
hide = true;
|
hide = true;
|
||||||
|
} else {
|
||||||
|
// 指定されているかどうか
|
||||||
|
const specified = packedNote.visibleUserIds!.some(id => meId === id);
|
||||||
|
|
||||||
|
if (!specified) {
|
||||||
|
hide = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
|
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
|
||||||
if (packedNote.visibility === 'followers') {
|
if (!hide) {
|
||||||
if (meId == null) {
|
if (packedNote.visibility === 'followers') {
|
||||||
hide = true;
|
if (meId == null) {
|
||||||
} else if (meId === packedNote.userId) {
|
hide = true;
|
||||||
hide = false;
|
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
|
||||||
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
|
// 自分の投稿に対するリプライ
|
||||||
// 自分の投稿に対するリプライ
|
hide = false;
|
||||||
hide = false;
|
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
|
||||||
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
|
// 自分へのメンション
|
||||||
// 自分へのメンション
|
hide = false;
|
||||||
hide = false;
|
} else {
|
||||||
} else {
|
// フォロワーかどうか
|
||||||
// フォロワーかどうか
|
// TODO: 当関数呼び出しごとにクエリが走るのは重そうだからなんとかする
|
||||||
const isFollowing = await this.followingsRepository.exists({
|
const isFollowing = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followeeId: packedNote.userId,
|
followeeId: packedNote.userId,
|
||||||
followerId: meId,
|
followerId: meId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
hide = !isFollowing;
|
hide = !isFollowing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packedNote.user.requireSigninToViewContents && meId == null) {
|
|
||||||
hide = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hide) {
|
if (hide) {
|
||||||
packedNote.visibleUserIds = undefined;
|
packedNote.visibleUserIds = undefined;
|
||||||
packedNote.fileIds = [];
|
packedNote.fileIds = [];
|
||||||
|
@ -161,6 +187,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
packedNote.poll = undefined;
|
packedNote.poll = undefined;
|
||||||
packedNote.cw = null;
|
packedNote.cw = null;
|
||||||
packedNote.isHidden = true;
|
packedNote.isHidden = true;
|
||||||
|
// TODO: hiddenReason みたいなのを提供しても良さそう
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -491,6 +491,8 @@ export class UserEntityService implements OnModuleInit {
|
||||||
isBot: user.isBot,
|
isBot: user.isBot,
|
||||||
isCat: user.isCat,
|
isCat: user.isCat,
|
||||||
requireSigninToViewContents: user.requireSigninToViewContents === false ? undefined : true,
|
requireSigninToViewContents: user.requireSigninToViewContents === false ? undefined : true,
|
||||||
|
makeNotesFollowersOnlyBefore: user.makeNotesFollowersOnlyBefore ?? undefined,
|
||||||
|
makeNotesHiddenBefore: user.makeNotesHiddenBefore ?? undefined,
|
||||||
instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? {
|
instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? {
|
||||||
name: instance.name,
|
name: instance.name,
|
||||||
softwareName: instance.softwareName,
|
softwareName: instance.softwareName,
|
||||||
|
|
|
@ -207,6 +207,18 @@ export class MiUser {
|
||||||
})
|
})
|
||||||
public requireSigninToViewContents: boolean;
|
public requireSigninToViewContents: boolean;
|
||||||
|
|
||||||
|
// in sec, マイナスで相対時間
|
||||||
|
@Column('integer', {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public makeNotesFollowersOnlyBefore: number | null;
|
||||||
|
|
||||||
|
// in sec, マイナスで相対時間
|
||||||
|
@Column('integer', {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public makeNotesHiddenBefore: number | null;
|
||||||
|
|
||||||
// アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ
|
// アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -119,6 +119,14 @@ export const packedUserLiteSchema = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
nullable: false, optional: true,
|
nullable: false, optional: true,
|
||||||
},
|
},
|
||||||
|
makeNotesFollowersOnlyBefore: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: true, optional: true,
|
||||||
|
},
|
||||||
|
makeNotesHiddenBefore: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: true, optional: true,
|
||||||
|
},
|
||||||
instance: {
|
instance: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: true,
|
nullable: false, optional: true,
|
||||||
|
|
|
@ -180,6 +180,8 @@ export const paramDef = {
|
||||||
noCrawle: { type: 'boolean' },
|
noCrawle: { type: 'boolean' },
|
||||||
preventAiLearning: { type: 'boolean' },
|
preventAiLearning: { type: 'boolean' },
|
||||||
requireSigninToViewContents: { type: 'boolean' },
|
requireSigninToViewContents: { type: 'boolean' },
|
||||||
|
makeNotesFollowersOnlyBefore: { type: 'integer', nullable: true },
|
||||||
|
makeNotesHiddenBefore: { type: 'integer', nullable: true },
|
||||||
isBot: { type: 'boolean' },
|
isBot: { type: 'boolean' },
|
||||||
isCat: { type: 'boolean' },
|
isCat: { type: 'boolean' },
|
||||||
injectFeaturedNote: { type: 'boolean' },
|
injectFeaturedNote: { type: 'boolean' },
|
||||||
|
@ -336,6 +338,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle;
|
if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle;
|
||||||
if (typeof ps.preventAiLearning === 'boolean') profileUpdates.preventAiLearning = ps.preventAiLearning;
|
if (typeof ps.preventAiLearning === 'boolean') profileUpdates.preventAiLearning = ps.preventAiLearning;
|
||||||
if (typeof ps.requireSigninToViewContents === 'boolean') updates.requireSigninToViewContents = ps.requireSigninToViewContents;
|
if (typeof ps.requireSigninToViewContents === 'boolean') updates.requireSigninToViewContents = ps.requireSigninToViewContents;
|
||||||
|
if ((typeof ps.makeNotesFollowersOnlyBefore === 'number') || (ps.makeNotesFollowersOnlyBefore === null)) updates.makeNotesFollowersOnlyBefore = ps.makeNotesFollowersOnlyBefore;
|
||||||
|
if ((typeof ps.makeNotesHiddenBefore === 'number') || (ps.makeNotesHiddenBefore === null)) updates.makeNotesHiddenBefore = ps.makeNotesHiddenBefore;
|
||||||
if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat;
|
if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat;
|
||||||
if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
|
if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
|
||||||
if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail;
|
if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail;
|
||||||
|
|
|
@ -46,7 +46,7 @@ import type { MenuItem } from '@/types/menu.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string | null;
|
modelValue: string | number | null;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
|
|
@ -45,17 +45,89 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label>{{ i18n.ts.lockdown }}</template>
|
<template #label>{{ i18n.ts.lockdown }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="requireSigninToViewContents" @update:modelValue="save()">
|
<MkSwitch v-model="requireSigninToViewContents" @update:modelValue="save()">
|
||||||
{{ i18n.ts._accountSettings.requireSigninToViewContents }}<span class="_beta">{{ i18n.ts.beta }}</span>
|
{{ i18n.ts._accountSettings.requireSigninToViewContents }}
|
||||||
<template #caption>
|
<template #caption>
|
||||||
<div>{{ i18n.ts._accountSettings.requireSigninToViewContentsDescription1 }}</div>
|
<div>{{ i18n.ts._accountSettings.requireSigninToViewContentsDescription1 }}</div>
|
||||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription2 }}</div>
|
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription2 }}</div>
|
||||||
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription3 }}</div>
|
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription3 }}</div>
|
||||||
</template>
|
</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
|
||||||
|
<FormSlot>
|
||||||
|
<template #label>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBefore }}</template>
|
||||||
|
|
||||||
|
<div class="_gaps_s">
|
||||||
|
<MkSelect :modelValue="makeNotesFollowersOnlyBefore_type" @update:modelValue="makeNotesFollowersOnlyBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null">
|
||||||
|
<option :value="null">{{ i18n.ts.none }}</option>
|
||||||
|
<option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option>
|
||||||
|
<option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
|
||||||
|
</MkSelect>
|
||||||
|
|
||||||
|
<MkSelect v-if="makeNotesFollowersOnlyBefore_type === 'relative'" v-model="makeNotesFollowersOnlyBefore">
|
||||||
|
<option :value="-3600">{{ i18n.ts.oneHour }}</option>
|
||||||
|
<option :value="-86400">{{ i18n.ts.oneDay }}</option>
|
||||||
|
<option :value="-259200">{{ i18n.ts.threeDays }}</option>
|
||||||
|
<option :value="-604800">{{ i18n.ts.oneWeek }}</option>
|
||||||
|
<option :value="-2592000">{{ i18n.ts.oneMonth }}</option>
|
||||||
|
<option :value="-7776000">{{ i18n.ts.threeMonths }}</option>
|
||||||
|
<option :value="-31104000">{{ i18n.ts.oneYear }}</option>
|
||||||
|
</MkSelect>
|
||||||
|
|
||||||
|
<MkInput
|
||||||
|
v-if="makeNotesFollowersOnlyBefore_type === 'absolute'"
|
||||||
|
:modelValue="formatDateTimeString(new Date(makeNotesFollowersOnlyBefore * 1000), 'yyyy-MM-dd')"
|
||||||
|
type="date"
|
||||||
|
:manualSave="true"
|
||||||
|
@update:modelValue="makeNotesFollowersOnlyBefore = Math.floor(new Date($event).getTime() / 1000)"
|
||||||
|
>
|
||||||
|
</MkInput>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #caption>
|
||||||
|
<div>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</div>
|
||||||
|
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.mayNotEffectForFederatedNotes }}</div>
|
||||||
|
</template>
|
||||||
|
</FormSlot>
|
||||||
|
|
||||||
|
<FormSlot>
|
||||||
|
<template #label>{{ i18n.ts._accountSettings.makeNotesHiddenBefore }}</template>
|
||||||
|
|
||||||
|
<div class="_gaps_s">
|
||||||
|
<MkSelect :modelValue="makeNotesHiddenBefore_type" @update:modelValue="makeNotesHiddenBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null">
|
||||||
|
<option :value="null">{{ i18n.ts.none }}</option>
|
||||||
|
<option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option>
|
||||||
|
<option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
|
||||||
|
</MkSelect>
|
||||||
|
|
||||||
|
<MkSelect v-if="makeNotesHiddenBefore_type === 'relative'" v-model="makeNotesHiddenBefore">
|
||||||
|
<option :value="-3600">{{ i18n.ts.oneHour }}</option>
|
||||||
|
<option :value="-86400">{{ i18n.ts.oneDay }}</option>
|
||||||
|
<option :value="-259200">{{ i18n.ts.threeDays }}</option>
|
||||||
|
<option :value="-604800">{{ i18n.ts.oneWeek }}</option>
|
||||||
|
<option :value="-2592000">{{ i18n.ts.oneMonth }}</option>
|
||||||
|
<option :value="-7776000">{{ i18n.ts.threeMonths }}</option>
|
||||||
|
<option :value="-31104000">{{ i18n.ts.oneYear }}</option>
|
||||||
|
</MkSelect>
|
||||||
|
|
||||||
|
<MkInput
|
||||||
|
v-if="makeNotesHiddenBefore_type === 'absolute'"
|
||||||
|
:modelValue="formatDateTimeString(new Date(makeNotesHiddenBefore * 1000), 'yyyy-MM-dd')"
|
||||||
|
type="date"
|
||||||
|
:manualSave="true"
|
||||||
|
@update:modelValue="makeNotesHiddenBefore = Math.floor(new Date($event).getTime() / 1000)"
|
||||||
|
>
|
||||||
|
</MkInput>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #caption>
|
||||||
|
<div>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</div>
|
||||||
|
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.mayNotEffectForFederatedNotes }}</div>
|
||||||
|
</template>
|
||||||
|
</FormSlot>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
|
@ -87,7 +159,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
|
@ -97,6 +169,9 @@ import { defaultStore } from '@/store.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { signinRequired } from '@/account.js';
|
import { signinRequired } from '@/account.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
import FormSlot from '@/components/form/slot.vue';
|
||||||
|
import { formatDateTimeString } from '@/scripts/format-time-string.js';
|
||||||
|
import MkInput from '@/components/MkInput.vue';
|
||||||
|
|
||||||
const $i = signinRequired();
|
const $i = signinRequired();
|
||||||
|
|
||||||
|
@ -106,6 +181,8 @@ const noCrawle = ref($i.noCrawle);
|
||||||
const preventAiLearning = ref($i.preventAiLearning);
|
const preventAiLearning = ref($i.preventAiLearning);
|
||||||
const isExplorable = ref($i.isExplorable);
|
const isExplorable = ref($i.isExplorable);
|
||||||
const requireSigninToViewContents = ref($i.requireSigninToViewContents ?? false);
|
const requireSigninToViewContents = ref($i.requireSigninToViewContents ?? false);
|
||||||
|
const makeNotesFollowersOnlyBefore = ref($i.makeNotesFollowersOnlyBefore ?? null);
|
||||||
|
const makeNotesHiddenBefore = ref($i.makeNotesHiddenBefore ?? null);
|
||||||
const hideOnlineStatus = ref($i.hideOnlineStatus);
|
const hideOnlineStatus = ref($i.hideOnlineStatus);
|
||||||
const publicReactions = ref($i.publicReactions);
|
const publicReactions = ref($i.publicReactions);
|
||||||
const followingVisibility = ref($i.followingVisibility);
|
const followingVisibility = ref($i.followingVisibility);
|
||||||
|
@ -116,6 +193,30 @@ const defaultNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultNote
|
||||||
const rememberNoteVisibility = computed(defaultStore.makeGetterSetter('rememberNoteVisibility'));
|
const rememberNoteVisibility = computed(defaultStore.makeGetterSetter('rememberNoteVisibility'));
|
||||||
const keepCw = computed(defaultStore.makeGetterSetter('keepCw'));
|
const keepCw = computed(defaultStore.makeGetterSetter('keepCw'));
|
||||||
|
|
||||||
|
const makeNotesFollowersOnlyBefore_type = computed(() => {
|
||||||
|
if (makeNotesFollowersOnlyBefore.value == null) {
|
||||||
|
return null;
|
||||||
|
} else if (makeNotesFollowersOnlyBefore.value >= 0) {
|
||||||
|
return 'absolute';
|
||||||
|
} else {
|
||||||
|
return 'relative';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const makeNotesHiddenBefore_type = computed(() => {
|
||||||
|
if (makeNotesHiddenBefore.value == null) {
|
||||||
|
return null;
|
||||||
|
} else if (makeNotesHiddenBefore.value >= 0) {
|
||||||
|
return 'absolute';
|
||||||
|
} else {
|
||||||
|
return 'relative';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch([makeNotesFollowersOnlyBefore, makeNotesHiddenBefore], () => {
|
||||||
|
save();
|
||||||
|
});
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
misskeyApi('i/update', {
|
misskeyApi('i/update', {
|
||||||
isLocked: !!isLocked.value,
|
isLocked: !!isLocked.value,
|
||||||
|
@ -124,6 +225,8 @@ function save() {
|
||||||
preventAiLearning: !!preventAiLearning.value,
|
preventAiLearning: !!preventAiLearning.value,
|
||||||
isExplorable: !!isExplorable.value,
|
isExplorable: !!isExplorable.value,
|
||||||
requireSigninToViewContents: !!requireSigninToViewContents.value,
|
requireSigninToViewContents: !!requireSigninToViewContents.value,
|
||||||
|
makeNotesFollowersOnlyBefore: makeNotesFollowersOnlyBefore.value,
|
||||||
|
makeNotesHiddenBefore: makeNotesHiddenBefore.value,
|
||||||
hideOnlineStatus: !!hideOnlineStatus.value,
|
hideOnlineStatus: !!hideOnlineStatus.value,
|
||||||
publicReactions: !!publicReactions.value,
|
publicReactions: !!publicReactions.value,
|
||||||
followingVisibility: followingVisibility.value,
|
followingVisibility: followingVisibility.value,
|
||||||
|
|
|
@ -3737,6 +3737,8 @@ export type components = {
|
||||||
isBot?: boolean;
|
isBot?: boolean;
|
||||||
isCat?: boolean;
|
isCat?: boolean;
|
||||||
requireSigninToViewContents?: boolean;
|
requireSigninToViewContents?: boolean;
|
||||||
|
makeNotesFollowersOnlyBefore?: number | null;
|
||||||
|
makeNotesHiddenBefore?: number | null;
|
||||||
instance?: {
|
instance?: {
|
||||||
name: string | null;
|
name: string | null;
|
||||||
softwareName: string | null;
|
softwareName: string | null;
|
||||||
|
@ -19846,6 +19848,8 @@ export type operations = {
|
||||||
noCrawle?: boolean;
|
noCrawle?: boolean;
|
||||||
preventAiLearning?: boolean;
|
preventAiLearning?: boolean;
|
||||||
requireSigninToViewContents?: boolean;
|
requireSigninToViewContents?: boolean;
|
||||||
|
makeNotesFollowersOnlyBefore?: number | null;
|
||||||
|
makeNotesHiddenBefore?: number | null;
|
||||||
isBot?: boolean;
|
isBot?: boolean;
|
||||||
isCat?: boolean;
|
isCat?: boolean;
|
||||||
injectFeaturedNote?: boolean;
|
injectFeaturedNote?: boolean;
|
||||||
|
|
Loading…
Reference in a new issue