feat: add option to boost with Home and Followers-only visibility (#9788)
Closes: #9777 This pull request includes UI changes (please check the attached images). Co-authored-by: naskya <m@naskya.net> Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9788 Co-authored-by: naskya <naskya@noreply.codeberg.org> Co-committed-by: naskya <naskya@noreply.codeberg.org>
This commit is contained in:
parent
f70c5da0bd
commit
0e8fe41aaa
|
@ -96,6 +96,9 @@ unfollow: "Unfollow"
|
|||
followRequestPending: "Follow request pending"
|
||||
enterEmoji: "Enter an emoji"
|
||||
renote: "Boost"
|
||||
renoteAsUnlisted: "Boost (Unlisted)"
|
||||
renoteToFollowers: "Boost (Followers)"
|
||||
renoteToRecipients: "Boost (Recipients)"
|
||||
unrenote: "Take back boost"
|
||||
renoted: "Boosted."
|
||||
cantRenote: "This post can't be boosted."
|
||||
|
|
|
@ -96,6 +96,9 @@ unfollow: "フォロー解除"
|
|||
followRequestPending: "フォロー許可待ち"
|
||||
enterEmoji: "絵文字を入力"
|
||||
renote: "ブースト"
|
||||
renoteAsUnlisted: "ホームにブースト"
|
||||
renoteToFollowers: "フォロワー限定でブースト"
|
||||
renoteToRecipients: "宛先のユーザーにブースト"
|
||||
unrenote: "ブースト解除"
|
||||
renoted: "ブーストしました。"
|
||||
cantRenote: "この投稿はブーストできません。"
|
||||
|
|
|
@ -10,20 +10,26 @@
|
|||
<template v-for="(item, i) in items2">
|
||||
<div v-if="item === null" class="divider"></div>
|
||||
<span v-else-if="item.type === 'label'" class="label item">
|
||||
<span>{{ item.text }}</span>
|
||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||
</span>
|
||||
<span v-else-if="item.type === 'pending'" :tabindex="i" class="pending item">
|
||||
<span><MkEllipsis/></span>
|
||||
</span>
|
||||
<MkA v-else-if="item.type === 'link'" :to="item.to" :tabindex="i" class="_button item" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<i v-if="item.icon" class="ph-fw ph-lg" :class="item.icon"></i>
|
||||
<span v-else-if="item.icons">
|
||||
<i v-for="icon in item.icons" class="ph-fw ph-lg" :class="icon"></i>
|
||||
</span>
|
||||
<MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
|
||||
<span>{{ item.text }}</span>
|
||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||
<span v-if="item.indicate" class="indicator"><i class="ph-circle ph-fill"></i></span>
|
||||
</MkA>
|
||||
<a v-else-if="item.type === 'a'" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<i v-if="item.icon" class="ph-fw ph-lg" :class="item.icon"></i>
|
||||
<span>{{ item.text }}</span>
|
||||
<span v-else-if="item.icons">
|
||||
<i v-for="icon in item.icons" class="ph-fw ph-lg" :class="icon"></i>
|
||||
</span>
|
||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||
<span v-if="item.indicate" class="indicator"><i class="ph-circle ph-fill"></i></span>
|
||||
</a>
|
||||
<button v-else-if="item.type === 'user' && !items.hidden" :tabindex="i" class="_button item" :class="{ active: item.active }" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
|
@ -31,17 +37,23 @@
|
|||
<span v-if="item.indicate" class="indicator"><i class="ph-circle ph-fill"></i></span>
|
||||
</button>
|
||||
<span v-else-if="item.type === 'switch'" :tabindex="i" class="item" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<FormSwitch v-model="item.ref" :disabled="item.disabled" class="form-switch">{{ item.text }}</FormSwitch>
|
||||
<FormSwitch v-model="item.ref" :disabled="item.disabled" class="form-switch" :style="item.textStyle || ''">{{ item.text }}</FormSwitch>
|
||||
</span>
|
||||
<button v-else-if="item.type === 'parent'" :tabindex="i" class="_button item parent" :class="{ childShowing: childShowingItem === item }" @mouseenter="showChildren(item, $event)">
|
||||
<i v-if="item.icon" class="ph-fw ph-lg" :class="item.icon"></i>
|
||||
<span>{{ item.text }}</span>
|
||||
<span v-else-if="item.icons">
|
||||
<i v-for="icon in item.icons" class="ph-fw ph-lg" :class="icon"></i>
|
||||
</span>
|
||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||
<span class="caret"><i class="ph-caret-right ph-bold ph-lg ph-fw ph-lg"></i></span>
|
||||
</button>
|
||||
<button v-else-if="!item.hidden" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<i v-if="item.icon" class="ph-fw ph-lg" :class="item.icon"></i>
|
||||
<span v-else-if="item.icons">
|
||||
<i v-for="icon in item.icons" class="ph-fw ph-lg" :class="icon"></i>
|
||||
</span>
|
||||
<MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
|
||||
<span>{{ item.text }}</span>
|
||||
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||
<span v-if="item.indicate" class="indicator"><i class="ph-circle ph-fill"></i></span>
|
||||
</button>
|
||||
</template>
|
||||
|
|
|
@ -64,24 +64,91 @@ const renote = async (viaKeyboard = false, ev?: MouseEvent) => {
|
|||
const users = renotes.map(x => x.user.id);
|
||||
const hasRenotedBefore = users.includes($i.id);
|
||||
|
||||
let buttonActions = [{
|
||||
text: i18n.ts.renote,
|
||||
icon: 'ph-repeat ph-bold ph-lg',
|
||||
danger: false,
|
||||
action: () => {
|
||||
os.api('notes/create', {
|
||||
renoteId: props.note.id,
|
||||
visibility: props.note.visibility,
|
||||
});
|
||||
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = rect.left + (el.offsetWidth / 2);
|
||||
const y = rect.top + (el.offsetHeight / 2);
|
||||
os.popup(Ripple, { x, y }, {}, 'end');
|
||||
}
|
||||
},
|
||||
}];
|
||||
let buttonActions = [];
|
||||
|
||||
if (props.note.visibility === 'public') {
|
||||
buttonActions.push({
|
||||
text: i18n.ts.renote,
|
||||
textStyle: 'font-weight: bold',
|
||||
icon: 'ph-repeat ph-bold ph-lg',
|
||||
danger: false,
|
||||
action: () => {
|
||||
os.api('notes/create', {
|
||||
renoteId: props.note.id,
|
||||
visibility: 'public',
|
||||
});
|
||||
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = rect.left + (el.offsetWidth / 2);
|
||||
const y = rect.top + (el.offsetHeight / 2);
|
||||
os.popup(Ripple, { x, y }, {}, 'end');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (['public', 'home'].includes(props.note.visibility)) {
|
||||
buttonActions.push({
|
||||
text: i18n.ts.renoteAsUnlisted,
|
||||
icons: ['ph-repeat ph-bold ph-lg', 'ph-house ph-bold ph-lg'],
|
||||
danger: false,
|
||||
action: () => {
|
||||
os.api('notes/create', {
|
||||
renoteId: props.note.id,
|
||||
visibility: 'home',
|
||||
});
|
||||
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = rect.left + (el.offsetWidth / 2);
|
||||
const y = rect.top + (el.offsetHeight / 2);
|
||||
os.popup(Ripple, { x, y }, {}, 'end');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (props.note.visibility === 'specified') {
|
||||
buttonActions.push({
|
||||
text: i18n.ts.renoteToRecipients,
|
||||
icons: ['ph-repeat ph-bold ph-lg', 'ph-envelope-simple-open ph-bold ph-lg'],
|
||||
danger: false,
|
||||
action: () => {
|
||||
os.api('notes/create', {
|
||||
renoteId: props.note.id,
|
||||
visibility: 'specified',
|
||||
visibleUserIds: props.note.visibleUserIds,
|
||||
});
|
||||
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = rect.left + (el.offsetWidth / 2);
|
||||
const y = rect.top + (el.offsetHeight / 2);
|
||||
os.popup(Ripple, { x, y }, {}, 'end');
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
buttonActions.push({
|
||||
text: i18n.ts.renoteToFollowers,
|
||||
icons: ['ph-repeat ph-bold ph-lg', 'ph-lock-simple-open ph-bold ph-lg'],
|
||||
danger: false,
|
||||
action: () => {
|
||||
os.api('notes/create', {
|
||||
renoteId: props.note.id,
|
||||
visibility: 'followers',
|
||||
});
|
||||
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
|
||||
if (el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = rect.left + (el.offsetWidth / 2);
|
||||
const y = rect.top + (el.offsetHeight / 2);
|
||||
os.popup(Ripple, { x, y }, {}, 'end');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!defaultStore.state.seperateRenoteQuote) {
|
||||
buttonActions.push({
|
||||
|
|
|
@ -5,11 +5,16 @@ export type MenuAction = (ev: MouseEvent) => void;
|
|||
|
||||
export type MenuDivider = null;
|
||||
export type MenuNull = undefined;
|
||||
export type MenuLabel = { type: "label"; text: string };
|
||||
export type MenuLabel = {
|
||||
type: "label";
|
||||
text: string;
|
||||
textStyle?: string;
|
||||
};
|
||||
export type MenuLink = {
|
||||
type: "link";
|
||||
to: string;
|
||||
text: string;
|
||||
textStyle?: string;
|
||||
icon?: string;
|
||||
indicate?: boolean;
|
||||
avatar?: Misskey.entities.User;
|
||||
|
@ -20,6 +25,7 @@ export type MenuA = {
|
|||
target?: string;
|
||||
download?: string;
|
||||
text: string;
|
||||
textStyle?: string;
|
||||
icon?: string;
|
||||
indicate?: boolean;
|
||||
};
|
||||
|
@ -35,11 +41,13 @@ export type MenuSwitch = {
|
|||
type: "switch";
|
||||
ref: Ref<boolean>;
|
||||
text: string;
|
||||
textStyle?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
export type MenuButton = {
|
||||
type?: "button";
|
||||
text: string;
|
||||
textStyle?: string;
|
||||
icon?: string;
|
||||
indicate?: boolean;
|
||||
danger?: boolean;
|
||||
|
@ -48,9 +56,22 @@ export type MenuButton = {
|
|||
avatar?: Misskey.entities.User;
|
||||
action: MenuAction;
|
||||
};
|
||||
export type MenuButtonMultipleIcons = {
|
||||
type?: "button";
|
||||
text: string;
|
||||
textStyle?: string;
|
||||
icons: string[];
|
||||
indicate?: boolean;
|
||||
danger?: boolean;
|
||||
active?: boolean;
|
||||
hidden?: boolean;
|
||||
avatar?: Misskey.entities.User;
|
||||
action: MenuAction;
|
||||
};
|
||||
export type MenuParent = {
|
||||
type: "parent";
|
||||
text: string;
|
||||
textStyle?: string;
|
||||
icon?: string;
|
||||
children: OuterMenuItem[];
|
||||
};
|
||||
|
@ -66,9 +87,10 @@ type OuterMenuItem =
|
|||
| MenuUser
|
||||
| MenuSwitch
|
||||
| MenuButton
|
||||
| MenuButtonMultipleIcons
|
||||
| MenuParent;
|
||||
type OuterPromiseMenuItem = Promise<
|
||||
MenuLabel | MenuLink | MenuA | MenuUser | MenuSwitch | MenuButton | MenuParent
|
||||
MenuLabel | MenuLink | MenuA | MenuUser | MenuSwitch | MenuButton | MenuButtonMultipleIcons | MenuParent
|
||||
>;
|
||||
export type MenuItem = OuterMenuItem | OuterPromiseMenuItem;
|
||||
export type InnerMenuItem =
|
||||
|
@ -80,4 +102,5 @@ export type InnerMenuItem =
|
|||
| MenuUser
|
||||
| MenuSwitch
|
||||
| MenuButton
|
||||
| MenuButtonMultipleIcons
|
||||
| MenuParent;
|
||||
|
|
Loading…
Reference in New Issue