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
5 changed files with 134 additions and 26 deletions
|
@ -96,6 +96,9 @@ unfollow: "Unfollow"
|
||||||
followRequestPending: "Follow request pending"
|
followRequestPending: "Follow request pending"
|
||||||
enterEmoji: "Enter an emoji"
|
enterEmoji: "Enter an emoji"
|
||||||
renote: "Boost"
|
renote: "Boost"
|
||||||
|
renoteAsUnlisted: "Boost (Unlisted)"
|
||||||
|
renoteToFollowers: "Boost (Followers)"
|
||||||
|
renoteToRecipients: "Boost (Recipients)"
|
||||||
unrenote: "Take back boost"
|
unrenote: "Take back boost"
|
||||||
renoted: "Boosted."
|
renoted: "Boosted."
|
||||||
cantRenote: "This post can't be boosted."
|
cantRenote: "This post can't be boosted."
|
||||||
|
|
|
@ -96,6 +96,9 @@ unfollow: "フォロー解除"
|
||||||
followRequestPending: "フォロー許可待ち"
|
followRequestPending: "フォロー許可待ち"
|
||||||
enterEmoji: "絵文字を入力"
|
enterEmoji: "絵文字を入力"
|
||||||
renote: "ブースト"
|
renote: "ブースト"
|
||||||
|
renoteAsUnlisted: "ホームにブースト"
|
||||||
|
renoteToFollowers: "フォロワー限定でブースト"
|
||||||
|
renoteToRecipients: "宛先のユーザーにブースト"
|
||||||
unrenote: "ブースト解除"
|
unrenote: "ブースト解除"
|
||||||
renoted: "ブーストしました。"
|
renoted: "ブーストしました。"
|
||||||
cantRenote: "この投稿はブーストできません。"
|
cantRenote: "この投稿はブーストできません。"
|
||||||
|
|
|
@ -10,20 +10,26 @@
|
||||||
<template v-for="(item, i) in items2">
|
<template v-for="(item, i) in items2">
|
||||||
<div v-if="item === null" class="divider"></div>
|
<div v-if="item === null" class="divider"></div>
|
||||||
<span v-else-if="item.type === 'label'" class="label item">
|
<span v-else-if="item.type === 'label'" class="label item">
|
||||||
<span>{{ item.text }}</span>
|
<span :style="item.textStyle || ''">{{ item.text }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="item.type === 'pending'" :tabindex="i" class="pending item">
|
<span v-else-if="item.type === 'pending'" :tabindex="i" class="pending item">
|
||||||
<span><MkEllipsis/></span>
|
<span><MkEllipsis/></span>
|
||||||
</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)">
|
<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>
|
<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"/>
|
<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>
|
<span v-if="item.indicate" class="indicator"><i class="ph-circle ph-fill"></i></span>
|
||||||
</MkA>
|
</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)">
|
<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>
|
<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>
|
<span v-if="item.indicate" class="indicator"><i class="ph-circle ph-fill"></i></span>
|
||||||
</a>
|
</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)">
|
<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>
|
<span v-if="item.indicate" class="indicator"><i class="ph-circle ph-fill"></i></span>
|
||||||
</button>
|
</button>
|
||||||
<span v-else-if="item.type === 'switch'" :tabindex="i" class="item" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
<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>
|
</span>
|
||||||
<button v-else-if="item.type === 'parent'" :tabindex="i" class="_button item parent" :class="{ childShowing: childShowingItem === item }" @mouseenter="showChildren(item, $event)">
|
<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>
|
<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>
|
<span class="caret"><i class="ph-caret-right ph-bold ph-lg ph-fw ph-lg"></i></span>
|
||||||
</button>
|
</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)">
|
<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>
|
<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"/>
|
<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>
|
<span v-if="item.indicate" class="indicator"><i class="ph-circle ph-fill"></i></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -64,24 +64,91 @@ const renote = async (viaKeyboard = false, ev?: MouseEvent) => {
|
||||||
const users = renotes.map(x => x.user.id);
|
const users = renotes.map(x => x.user.id);
|
||||||
const hasRenotedBefore = users.includes($i.id);
|
const hasRenotedBefore = users.includes($i.id);
|
||||||
|
|
||||||
let buttonActions = [{
|
let buttonActions = [];
|
||||||
text: i18n.ts.renote,
|
|
||||||
icon: 'ph-repeat ph-bold ph-lg',
|
if (props.note.visibility === 'public') {
|
||||||
danger: false,
|
buttonActions.push({
|
||||||
action: () => {
|
text: i18n.ts.renote,
|
||||||
os.api('notes/create', {
|
textStyle: 'font-weight: bold',
|
||||||
renoteId: props.note.id,
|
icon: 'ph-repeat ph-bold ph-lg',
|
||||||
visibility: props.note.visibility,
|
danger: false,
|
||||||
});
|
action: () => {
|
||||||
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
|
os.api('notes/create', {
|
||||||
if (el) {
|
renoteId: props.note.id,
|
||||||
const rect = el.getBoundingClientRect();
|
visibility: 'public',
|
||||||
const x = rect.left + (el.offsetWidth / 2);
|
});
|
||||||
const y = rect.top + (el.offsetHeight / 2);
|
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
|
||||||
os.popup(Ripple, { x, y }, {}, 'end');
|
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) {
|
if (!defaultStore.state.seperateRenoteQuote) {
|
||||||
buttonActions.push({
|
buttonActions.push({
|
||||||
|
|
|
@ -5,11 +5,16 @@ export type MenuAction = (ev: MouseEvent) => void;
|
||||||
|
|
||||||
export type MenuDivider = null;
|
export type MenuDivider = null;
|
||||||
export type MenuNull = undefined;
|
export type MenuNull = undefined;
|
||||||
export type MenuLabel = { type: "label"; text: string };
|
export type MenuLabel = {
|
||||||
|
type: "label";
|
||||||
|
text: string;
|
||||||
|
textStyle?: string;
|
||||||
|
};
|
||||||
export type MenuLink = {
|
export type MenuLink = {
|
||||||
type: "link";
|
type: "link";
|
||||||
to: string;
|
to: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
textStyle?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
indicate?: boolean;
|
indicate?: boolean;
|
||||||
avatar?: Misskey.entities.User;
|
avatar?: Misskey.entities.User;
|
||||||
|
@ -20,6 +25,7 @@ export type MenuA = {
|
||||||
target?: string;
|
target?: string;
|
||||||
download?: string;
|
download?: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
textStyle?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
indicate?: boolean;
|
indicate?: boolean;
|
||||||
};
|
};
|
||||||
|
@ -35,11 +41,13 @@ export type MenuSwitch = {
|
||||||
type: "switch";
|
type: "switch";
|
||||||
ref: Ref<boolean>;
|
ref: Ref<boolean>;
|
||||||
text: string;
|
text: string;
|
||||||
|
textStyle?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
export type MenuButton = {
|
export type MenuButton = {
|
||||||
type?: "button";
|
type?: "button";
|
||||||
text: string;
|
text: string;
|
||||||
|
textStyle?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
indicate?: boolean;
|
indicate?: boolean;
|
||||||
danger?: boolean;
|
danger?: boolean;
|
||||||
|
@ -48,9 +56,22 @@ export type MenuButton = {
|
||||||
avatar?: Misskey.entities.User;
|
avatar?: Misskey.entities.User;
|
||||||
action: MenuAction;
|
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 = {
|
export type MenuParent = {
|
||||||
type: "parent";
|
type: "parent";
|
||||||
text: string;
|
text: string;
|
||||||
|
textStyle?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
children: OuterMenuItem[];
|
children: OuterMenuItem[];
|
||||||
};
|
};
|
||||||
|
@ -66,9 +87,10 @@ type OuterMenuItem =
|
||||||
| MenuUser
|
| MenuUser
|
||||||
| MenuSwitch
|
| MenuSwitch
|
||||||
| MenuButton
|
| MenuButton
|
||||||
|
| MenuButtonMultipleIcons
|
||||||
| MenuParent;
|
| MenuParent;
|
||||||
type OuterPromiseMenuItem = Promise<
|
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 MenuItem = OuterMenuItem | OuterPromiseMenuItem;
|
||||||
export type InnerMenuItem =
|
export type InnerMenuItem =
|
||||||
|
@ -80,4 +102,5 @@ export type InnerMenuItem =
|
||||||
| MenuUser
|
| MenuUser
|
||||||
| MenuSwitch
|
| MenuSwitch
|
||||||
| MenuButton
|
| MenuButton
|
||||||
|
| MenuButtonMultipleIcons
|
||||||
| MenuParent;
|
| MenuParent;
|
||||||
|
|
Loading…
Reference in a new issue