feat (client): add private visibility

This commit is contained in:
naskya 2024-02-21 07:24:52 +09:00
parent f04bf9f78e
commit fa0e65cc1b
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
10 changed files with 75 additions and 9 deletions

View file

@ -6,6 +6,8 @@ Critical security updates are indicated by the :warning: icon.
- Add the ability to give regular (non-moderator) users permission to manage custom emojis - Add the ability to give regular (non-moderator) users permission to manage custom emojis
- Fix a bug that made impossible to update user profiles under some conditions - Fix a bug that made impossible to update user profiles under some conditions
- Add "private" (only me) post visibility
- It's just a paraphrase of DMs without recipients
## :warning: v20240217-1 ## :warning: v20240217-1

View file

@ -1172,12 +1172,14 @@ postSearch: "Post search on this server"
showBigPostButton: "Show a huge post button on the posting form" showBigPostButton: "Show a huge post button on the posting form"
emojiModPerm: "Custom emoji management permission" emojiModPerm: "Custom emoji management permission"
emojiModPermDescription: "Add: Allow this user to add new custom emojis and to set tag/category/license to newly added custom emojis.\nAdd and Edit: \"Add\" Permission + Allow this user to edit the name/category/tag/license of the existing custom emojis.\nAllow All: \"Add and Edit\" Permission + Allow this user to delete existing custom emojis." emojiModPermDescription: "Add: Allow this user to add new custom emojis and to set tag/category/license to newly added custom emojis.\nAdd and Edit: \"Add\" Permission + Allow this user to edit the name/category/tag/license of the existing custom emojis.\nAllow All: \"Add and Edit\" Permission + Allow this user to delete existing custom emojis."
private: "Private"
privateDescription: "Make visible for you only"
_emojiModPerm: _emojiModPerm:
unauthorized: "None" unauthorized: "None"
add: "Add" add: "Add"
mod: "Add and Edit" mod: "Add and Edit"
full: "Allow All" full: "Allow All"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing description: "Reduces the effort of server moderation through automatically recognizing
NSFW media via Machine Learning. This will slightly increase the load on the server." NSFW media via Machine Learning. This will slightly increase the load on the server."

View file

@ -2026,3 +2026,5 @@ _emojiModPerm:
add: "追加" add: "追加"
mod: "追加と変更" mod: "追加と変更"
full: "全て許可" full: "全て許可"
private: "秘密"
privateDescription: "あなた以外には非公開"

View file

@ -1995,3 +1995,5 @@ languageForTranslation: 帖子翻译语言
vibrate: 播放振动 vibrate: 播放振动
openServerInfo: 点击帖子上的服务器滚动条时显示服务器信息 openServerInfo: 点击帖子上的服务器滚动条时显示服务器信息
clickToShowPatterns: 点击显示模块模式 clickToShowPatterns: 点击显示模块模式
private: "秘密"
privateDescription: "仅你可见"

View file

@ -2012,3 +2012,5 @@ moreUrlsDescription: "請以下列形式輸入欲釘選在左下角幫助選單
showPreviewByDefault: "自動開啟發文介面中的預覽顯示" showPreviewByDefault: "自動開啟發文介面中的預覽顯示"
preventMisclick: "預防誤觸" preventMisclick: "預防誤觸"
hideFollowButtons: "隱藏會誤觸的追隨按鈕" hideFollowButtons: "隱藏會誤觸的追隨按鈕"
private: "祕密"
privateDescription: "僅你可見"

View file

@ -50,6 +50,9 @@
<span v-if="visibility === 'specified'" <span v-if="visibility === 'specified'"
><i :class="icon('ph-envelope-simple-open')"></i ><i :class="icon('ph-envelope-simple-open')"></i
></span> ></span>
<span v-if="visibility === 'private'"
><i :class="icon('ph-eye-slash')"></i
></span>
</button> </button>
<button <button
ref="languageButton" ref="languageButton"
@ -557,6 +560,8 @@ if (
}).then((users) => { }).then((users) => {
users.forEach(pushVisibleUser); users.forEach(pushVisibleUser);
}); });
} else {
visibility.value = "private";
} }
if (props.reply.userId !== $i.id) { if (props.reply.userId !== $i.id) {
@ -1015,11 +1020,14 @@ async function post() {
cw: useCw.value ? cw.value || "" : undefined, cw: useCw.value ? cw.value || "" : undefined,
lang: language.value ? language.value : undefined, lang: language.value ? language.value : undefined,
localOnly: localOnly.value, localOnly: localOnly.value,
visibility: visibility.value, visibility:
visibility.value === "private" ? "specified" : visibility.value,
visibleUserIds: visibleUserIds:
visibility.value === "specified" visibility.value === "private"
? visibleUsers.value.map((u) => u.id) ? []
: undefined, : visibility.value === "specified"
? visibleUsers.value.map((u) => u.id)
: undefined,
}; };
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== "") { if (withHashtags.value && hashtags.value && hashtags.value.trim() !== "") {

View file

@ -11,10 +11,20 @@
:class="icon('ph-lock')" :class="icon('ph-lock')"
></i> ></i>
<i <i
v-else-if="note.visibility === 'specified'" v-else-if="
note.visibility === 'specified' &&
note.visibleUserIds != null &&
note.visibleUserIds.length > 0
"
ref="specified" ref="specified"
:class="icon('ph-envelope-simple-open')" :class="icon('ph-envelope-simple-open')"
></i> ></i>
<i
v-else-if="note.visibility === 'specified'"
v-tooltip="i18n.ts._visibility.private"
ref="specified"
:class="icon('ph-eye-slash')"
></i>
</span> </span>
<span v-if="note.localOnly" :class="$style.localOnly" <span v-if="note.localOnly" :class="$style.localOnly"
><i ><i
@ -42,7 +52,11 @@ const props = defineProps<{
const specified = ref<HTMLElement>(); const specified = ref<HTMLElement>();
if (props.note.visibility === "specified") { if (
props.note.visibility === "specified" &&
props.note.visibleUserIds != null &&
props.note.visibleUserIds.length > 0
) {
useTooltip(specified, async (showing) => { useTooltip(specified, async (showing) => {
const users = await os.api("users/show", { const users = await os.api("users/show", {
userIds: props.note.visibleUserIds, userIds: props.note.visibleUserIds,

View file

@ -84,6 +84,25 @@
}}</span> }}</span>
</div> </div>
</button> </button>
<button
key="private"
class="_button"
:class="[$style.item, { [$style.active]: v === 'private' }]"
data-index="5"
@click="choose('private')"
>
<div :class="$style.icon">
<i :class="icon('ph-eye-slash')"></i>
</div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{
i18n.ts._visibility.private
}}</span>
<span :class="$style.itemDescription">{{
i18n.ts._visibility.privateDescription
}}</span>
</div>
</button>
<div :class="$style.divider"></div> <div :class="$style.divider"></div>
<button <button
key="localOnly" key="localOnly"
@ -154,7 +173,9 @@ watch(localOnly, () => {
emit("changeLocalOnly", localOnly.value); emit("changeLocalOnly", localOnly.value);
}); });
function choose(visibility: (typeof noteVisibilities)[number]): void { function choose(
visibility: (typeof noteVisibilities)[number] | "private",
): void {
v.value = visibility; v.value = visibility;
emit("changeVisibility", visibility); emit("changeVisibility", visibility);
nextTick(() => { nextTick(() => {

View file

@ -113,6 +113,11 @@
#suffix #suffix
>{{ i18n.ts._visibility.specified }}</template >{{ i18n.ts._visibility.specified }}</template
> >
<template
v-else-if="defaultNoteVisibility === 'private'"
#suffix
>{{ i18n.ts._visibility.private }}</template
>
<FormSelect v-model="defaultNoteVisibility" class="_formBlock"> <FormSelect v-model="defaultNoteVisibility" class="_formBlock">
<option value="public"> <option value="public">
@ -125,6 +130,9 @@
<option value="specified"> <option value="specified">
{{ i18n.ts._visibility.specified }} {{ i18n.ts._visibility.specified }}
</option> </option>
<option value="private">
{{ i18n.ts._visibility.private }}
</option>
</FormSelect> </FormSelect>
<FormSwitch v-model="defaultNoteLocalOnly" class="_formBlock">{{ <FormSwitch v-model="defaultNoteLocalOnly" class="_formBlock">{{
i18n.ts._visibility.localOnly i18n.ts._visibility.localOnly

View file

@ -122,7 +122,12 @@ export const defaultStore = markRaw(
}, },
visibility: { visibility: {
where: "deviceAccount", where: "deviceAccount",
default: "public" as "public" | "home" | "followers" | "specified", default: "public" as
| "public"
| "home"
| "followers"
| "specified"
| "private",
}, },
localOnly: { localOnly: {
where: "deviceAccount", where: "deviceAccount",