This commit is contained in:
mesi 2024-09-15 13:11:03 +00:00 committed by GitHub
commit 59b0f0191b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 264 additions and 72 deletions

View File

@ -9,6 +9,8 @@
- Enhance: サイズ制限を超過するファイルをアップロードしようとした際にエラーを出すように
- Enhance: アイコンデコレーション管理画面にプレビューを追加
- Enhance: コントロールパネル内のファイル一覧でセンシティブなファイルを区別しやすく
- Enhance: どこで投稿フォームを開いてもお気に入りに登録したチャンネルにノートできるように
- Enhance: チャンネルのページを開いている間はデフォルトの公開範囲がそのチャンネルになるように
- Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正
- Fix: 月の違う同じ日はセパレータが表示されないのを修正

12
locales/index.d.ts vendored
View File

@ -8543,6 +8543,18 @@ export interface Locale extends ILocale {
*
*/
"specifiedDescription": string;
/**
*
*/
"channel": string;
/**
*
*/
"channelDescription": string;
/**
* :{name}
*/
"channelSelected": ParameterizedString<"name">;
/**
*
*/

View File

@ -2248,6 +2248,9 @@ _visibility:
followersDescription: "自分のフォロワーのみに公開"
specified: "ダイレクト"
specifiedDescription: "指定したユーザーのみに公開"
channel: "チャンネル"
channelDescription: "選択したチャンネルに公開"
channelSelected: "選択中:{name}"
disableFederation: "連合なし"
disableFederationDescription: "他サーバーへの配信を行いません"

View File

@ -23,6 +23,7 @@ import { emojiPicker } from '@/scripts/emoji-picker.js';
import { mainRouter } from '@/router/main.js';
import { type Keymap, makeHotkey } from '@/scripts/hotkey.js';
import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js';
import { postButtonHandler } from '@/scripts/post-button-handler.js';
export async function mainBoot() {
const { isClientUpdated } = await common(() => createApp(
@ -346,7 +347,7 @@ export async function mainBoot() {
const keymap = {
'p|n': () => {
if ($i == null) return;
post();
postButtonHandler(mainRouter.currentRef.value);
},
'd': () => {
defaultStore.set('darkMode', !defaultStore.state.darkMode);

View File

@ -19,21 +19,21 @@ SPDX-License-Identifier: AGPL-3.0-only
</button>
</div>
<div :class="$style.headerRight">
<template v-if="!(channel != null && fixed)">
<button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility">
<span v-if="visibility === 'public'"><i class="ti ti-world"></i></span>
<span v-if="visibility === 'home'"><i class="ti ti-home"></i></span>
<span v-if="visibility === 'followers'"><i class="ti ti-lock"></i></span>
<span v-if="visibility === 'specified'"><i class="ti ti-mail"></i></span>
<span :class="$style.headerRightButtonText">{{ i18n.ts._visibility[visibility] }}</span>
</button>
<button v-else class="_button" :class="[$style.headerRightItem, $style.visibility]" disabled>
<button ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility">
<template v-if="postChannel">
<span><i class="ti ti-device-tv"></i></span>
<span :class="$style.headerRightButtonText">{{ channel.name }}</span>
</button>
</template>
<button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified'" @click="toggleLocalOnly">
<span v-if="!localOnly"><i class="ti ti-rocket"></i></span>
<span v-if="postChannel" :class="$style.headerRightButtonText">{{ postChannelName }}</span>
</template>
<template v-else>
<span v-if="actualVisibility === 'public'"><i class="ti ti-world"></i></span>
<span v-if="actualVisibility === 'home'"><i class="ti ti-home"></i></span>
<span v-if="actualVisibility === 'followers'"><i class="ti ti-lock"></i></span>
<span v-if="actualVisibility === 'specified'"><i class="ti ti-mail"></i></span>
<span :class="$style.headerRightButtonText">{{ i18n.ts._visibility[actualVisibility] }}</span>
</template>
</button>
<button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: actualLocalOnly }]" :disabled="postChannel != null || actualVisibility === 'specified'" @click="toggleLocalOnly">
<span v-if="!actualLocalOnly"><i class="ti ti-rocket"></i></span>
<span v-else><i class="ti ti-rocket-off"></i></span>
</button>
<button v-click-anime v-tooltip="i18n.ts.reactionAcceptance" class="_button" :class="[$style.headerRightItem, { [$style.danger]: reactionAcceptance === 'likeOnly' }]" @click="toggleReactionAcceptance">
@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkNoteSimple v-if="reply" :class="$style.targetNote" :note="reply"/>
<MkNoteSimple v-if="renote" :class="$style.targetNote" :note="renote"/>
<div v-if="quoteId" :class="$style.withQuote"><i class="ti ti-quote"></i> {{ i18n.ts.quoteAttached }}<button @click="quoteId = null"><i class="ti ti-x"></i></button></div>
<div v-if="visibility === 'specified'" :class="$style.toSpecified">
<div v-if="actualVisibility === 'specified'" :class="$style.toSpecified">
<span style="margin-right: 8px;">{{ i18n.ts.recipient }}</span>
<div :class="$style.visibleUsers">
<span v-for="u in visibleUsers" :key="u.id" :class="$style.visibleUser">
@ -66,8 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo>
<input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown">
<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
<div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div>
<div :class="[$style.textOuter, { [$style.withCw]: useCw }]" :style="postChannel && postChannel.color ? `--channel-color: ${postChannel.color}` : undefined">
<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
</div>
@ -137,7 +136,7 @@ const modal = inject('modal');
const props = withDefaults(defineProps<{
reply?: Misskey.entities.Note;
renote?: Misskey.entities.Note;
channel?: Misskey.entities.Channel; // TODO
channel?: Misskey.entities.Channel;
mention?: Misskey.entities.User;
specified?: Misskey.entities.UserDetailed;
initialText?: string;
@ -202,8 +201,22 @@ const imeText = ref('');
const showingOptions = ref(false);
const textAreaReadOnly = ref(false);
const postChannel = ref<Misskey.entities.Channel | null>(props.channel ?? null);
const postChannelName = computed<string>(() => postChannel.value?.name ?? '');
/**
* {@link localOnly}が持つ値にチャンネル選択有無を加味した値を計算するチャンネル選択時は強制的にfalse
* チャンネル選択有無を考慮する必要がある場面では{@link localOnly}ではなくこの値を使用する
*/
const actualLocalOnly = computed<boolean>(() => postChannel.value ? true : localOnly.value);
/**
* {@link visibility}が持つ値にチャンネル選択有無を加味した値を計算するチャンネル選択時は強制的にpublic
* チャンネル選択有無を考慮する必要がある場面では{@link actualVisibility}ではなくこの値を使用する
*/
const actualVisibility = computed<typeof Misskey.noteVisibilities[number]>(() => postChannel.value ? 'public' : visibility.value);
const draftKey = computed((): string => {
let key = props.channel ? `channel:${props.channel.id}` : '';
let key = postChannel.value ? `channel:${postChannel.value.id}` : '';
if (props.renote) {
key += `renote:${props.renote.id}`;
@ -221,7 +234,7 @@ const placeholder = computed((): string => {
return i18n.ts._postForm.quotePlaceholder;
} else if (props.reply) {
return i18n.ts._postForm.replyPlaceholder;
} else if (props.channel) {
} else if (postChannel.value) {
return i18n.ts._postForm.channelPlaceholder;
} else {
const xs = [
@ -272,7 +285,7 @@ watch(text, () => {
checkMissingMention();
}, { immediate: true });
watch(visibility, () => {
watch(actualVisibility, () => {
checkMissingMention();
}, { immediate: true });
@ -316,11 +329,6 @@ if ($i.isSilenced && visibility.value === 'public') {
visibility.value = 'home';
}
if (props.channel) {
visibility.value = 'public';
localOnly.value = true; // TODO:
}
//
if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) {
if (props.reply.visibility === 'home' && visibility.value === 'followers') {
@ -372,7 +380,7 @@ function watchForDraft() {
}
function checkMissingMention() {
if (visibility.value === 'specified') {
if (actualVisibility.value === 'specified') {
const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) {
@ -459,36 +467,31 @@ function upload(file: File, name?: string): void {
}
function setVisibility() {
if (props.channel) {
visibility.value = 'public';
localOnly.value = true; // TODO:
return;
}
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
currentVisibility: visibility.value,
currentVisibility: actualVisibility.value,
isSilenced: $i.isSilenced,
localOnly: localOnly.value,
// false
localOnly: postChannel.value ? false : actualLocalOnly.value,
src: visibilityButton.value,
currentChannel: postChannel.value,
...(props.reply ? { isReplyVisibilitySpecified: props.reply.visibility === 'specified' } : {}),
}, {
changeVisibility: v => {
visibility.value = v;
postChannel.value = null;
if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set('visibility', visibility.value);
}
},
changeChannel: channel => {
// computedlocalOnlyvisibility
postChannel.value = channel;
},
closed: () => dispose(),
});
}
async function toggleLocalOnly() {
if (props.channel) {
visibility.value = 'public';
localOnly.value = true; // TODO:
return;
}
const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo');
if (!localOnly.value && neverShowInfo !== 'true') {
@ -716,6 +719,15 @@ function saveDraft() {
function deleteDraft() {
const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
if (postChannel.value) {
// draftKey.valuechannel:${postChannel.value.id}partialDraftKey
// 稿稿稿
const partialDraftKey = draftKey.value.replace(`channel:${postChannel.value.id}`, '');
if (draftData[partialDraftKey]) {
delete draftData[partialDraftKey];
}
}
delete draftData[draftKey.value];
miLocalStorage.setItem('drafts', JSON.stringify(draftData));
@ -752,7 +764,8 @@ async function post(ev?: MouseEvent) {
text.value.includes('$[scale') ||
text.value.includes('$[position');
if (annoying && visibility.value === 'public') {
// 稿public
if (annoying && actualVisibility.value === 'public' && !postChannel.value) {
const { canceled, result } = await os.actions({
type: 'warning',
text: i18n.ts.thisPostMayBeAnnoying,
@ -781,12 +794,12 @@ async function post(ev?: MouseEvent) {
fileIds: files.value.length > 0 ? files.value.map(f => f.id) : undefined,
replyId: props.reply ? props.reply.id : undefined,
renoteId: props.renote ? props.renote.id : quoteId.value ? quoteId.value : undefined,
channelId: props.channel ? props.channel.id : undefined,
channelId: postChannel.value ? postChannel.value.id : undefined,
poll: poll.value,
cw: useCw.value ? cw.value ?? '' : null,
localOnly: localOnly.value,
visibility: visibility.value,
visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
localOnly: actualLocalOnly.value,
visibility: actualVisibility.value,
visibleUserIds: actualVisibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
reactionAcceptance: reactionAcceptance.value,
};
@ -1139,16 +1152,6 @@ defineExpose({
}
}
.colorBar {
position: absolute;
top: 0px;
left: 12px;
width: 5px;
height: 100% ;
border-radius: 999px;
pointer-events: none;
}
.submitInner {
padding: 0 12px;
line-height: 34px;
@ -1277,6 +1280,18 @@ defineExpose({
width: 100%;
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
left: 9.5px; // 9.5px + 5px + 9.5px = 24px
width: 5px;
height: 100%;
background: var(--channel-color, transparent);
border-radius: 2.5px;
pointer-events: none;
}
&.withCw {
padding-top: 8px;
}
@ -1382,6 +1397,11 @@ defineExpose({
padding: 0 16px;
}
.textOuter::before {
left: 0;
border-radius: 0 5px 5px 0;
}
.text {
min-height: 80px;
}

View File

@ -9,32 +9,50 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="[$style.label, $style.item]">
{{ i18n.ts.visibility }}
</div>
<button key="public" :disabled="isSilenced || isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')">
<button key="public" :disabled="isSilenced || isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'public' && !currentChannel }]" data-index="1" @click="choose('public')">
<div :class="$style.icon"><i class="ti ti-world"></i></div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{ i18n.ts._visibility.public }}</span>
<span :class="$style.itemDescription">{{ i18n.ts._visibility.publicDescription }}</span>
<div :class="$style.itemTitle">{{ i18n.ts._visibility.public }}</div>
<div :class="$style.itemDescription">{{ i18n.ts._visibility.publicDescription }}</div>
</div>
</button>
<button key="home" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')">
<button key="home" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'home' && !currentChannel }]" data-index="2" @click="choose('home')">
<div :class="$style.icon"><i class="ti ti-home"></i></div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{ i18n.ts._visibility.home }}</span>
<span :class="$style.itemDescription">{{ i18n.ts._visibility.homeDescription }}</span>
<div :class="$style.itemTitle">{{ i18n.ts._visibility.home }}</div>
<div :class="$style.itemDescription">{{ i18n.ts._visibility.homeDescription }}</div>
</div>
</button>
<button key="followers" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')">
<button key="followers" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' && !currentChannel }]" data-index="3" @click="choose('followers')">
<div :class="$style.icon"><i class="ti ti-lock"></i></div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{ i18n.ts._visibility.followers }}</span>
<span :class="$style.itemDescription">{{ i18n.ts._visibility.followersDescription }}</span>
<div :class="$style.itemTitle">{{ i18n.ts._visibility.followers }}</div>
<div :class="$style.itemDescription">{{ i18n.ts._visibility.followersDescription }}</div>
</div>
</button>
<button key="specified" :disabled="localOnly" class="_button" :class="[$style.item, { [$style.active]: v === 'specified' }]" data-index="4" @click="choose('specified')">
<button key="specified" :disabled="localOnly" class="_button" :class="[$style.item, { [$style.active]: v === 'specified' && !currentChannel }]" data-index="4" @click="choose('specified')">
<div :class="$style.icon"><i class="ti ti-mail"></i></div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{ i18n.ts._visibility.specified }}</span>
<span :class="$style.itemDescription">{{ i18n.ts._visibility.specifiedDescription }}</span>
<div :class="$style.itemTitle">{{ i18n.ts._visibility.specified }}</div>
<div :class="$style.itemDescription">{{ i18n.ts._visibility.specifiedDescription }}</div>
</div>
</button>
<button
ref="channelsButton"
class="_button"
:class="[$style.item, $style.channelButton, { [$style.active]: currentChannel }]"
:style="currentChannel && currentChannel.color ? `--channel-color: ${currentChannel.color}` : undefined"
data-index="5"
@click="chooseChannel"
>
<div :class="$style.icon">
<i class="ti ti-device-tv"></i>
</div>
<div :class="$style.body">
<div :class="$style.itemTitle">{{ i18n.ts._visibility.channel }}</div>
<div :class="$style.itemDescription">
{{ currentChannelName ? i18n.tsx._visibility.channelSelected({ name: currentChannelName }) : i18n.ts._visibility.channelDescription }}
</div>
</div>
</button>
</div>
@ -42,12 +60,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { nextTick, shallowRef, ref } from 'vue';
import { nextTick, shallowRef, ref, computed } from 'vue';
import * as Misskey from 'misskey-js';
import type { MenuItem } from '@/types/menu.js';
import MkModal from '@/components/MkModal.vue';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { favoritedChannelsCache } from '@/cache.js';
const modal = shallowRef<InstanceType<typeof MkModal>>();
const channelsButton = shallowRef<InstanceType<typeof HTMLButtonElement>>();
const props = withDefaults(defineProps<{
currentVisibility: typeof Misskey.noteVisibilities[number];
@ -55,16 +77,66 @@ const props = withDefaults(defineProps<{
localOnly: boolean;
src?: HTMLElement;
isReplyVisibilitySpecified?: boolean;
currentChannel?: Misskey.entities.Channel;
}>(), {
});
const emit = defineEmits<{
(ev: 'changeVisibility', v: typeof Misskey.noteVisibilities[number]): void;
(ev: 'changeChannel', v: Misskey.entities.Channel) : void
(ev: 'closed'): void;
}>();
const v = ref(props.currentVisibility);
/**
* Visibility とチャンネルはそれぞれ独立だけど今のところはチャンネル投稿は連合なしだし公開範囲も変更できないようである
packages/frontend/src/components/MkPostForm.vue :475
if (props.channel) {
visibility.value = 'public';
localOnly.value = true; // TODO:
return;
}
*/
const channels = ref<Misskey.entities.Channel[]>([]);
const currentChannel = ref<Misskey.entities.Channel | undefined>(props.currentChannel);
const currentChannelName = computed<string | null>(() => currentChannel.value?.name ?? null);
async function fetchChannels() {
const res = await favoritedChannelsCache.fetch();
channels.value.splice(0, 0, ...res);
}
async function chooseChannel() {
let selectedChannel: Misskey.entities.Channel | null = null;
await os.popupMenu([
{
type: 'label',
text: i18n.ts.selectChannel,
},
...channels.value.map<MenuItem>(it => ({
type: 'button',
text: it.name,
active: it.id === currentChannel.value?.id,
action: (_) => {
selectedChannel = it;
currentChannel.value = it;
},
}))],
channelsButton.value,
);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (selectedChannel) {
emit('changeChannel', selectedChannel);
nextTick(() => {
if (modal.value) modal.value.close();
});
}
}
function choose(visibility: typeof Misskey.noteVisibilities[number]): void {
v.value = visibility;
emit('changeVisibility', visibility);
@ -72,6 +144,8 @@ function choose(visibility: typeof Misskey.noteVisibilities[number]): void {
if (modal.value) modal.value.close();
});
}
fetchChannels();
</script>
<style lang="scss" module>
@ -142,6 +216,11 @@ function choose(visibility: typeof Misskey.noteVisibilities[number]): void {
.body {
flex: 1 1 auto;
min-width: 0;
}
.itemTitle,
.itemDescription {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -155,4 +234,28 @@ function choose(visibility: typeof Misskey.noteVisibilities[number]): void {
.itemDescription {
opacity: 0.6;
}
.channelButton {
position: relative;
.itemDescription {
max-width: 200px;
}
&::before {
content: '';
position: absolute;
top: 5px;
left: 0;
width: 4px;
height: calc(100% - 10px);
border-radius: 0 4px 4px 0;
background: var(--channel-color, transparent);
pointer-events: none;
}
}
.root.asDrawer .channelButton .itemDescription {
max-width: 100%;
}
</style>

View File

@ -32,6 +32,7 @@ export type Keys =
'message_drafts' |
'scratchpad' |
'debug' |
`channel:${string}` |
`miux:${string}` |
`ui:folder:${string}` |
`themes:${string}` |

View File

@ -122,6 +122,7 @@ watch(() => props.channelId, async () => {
channel.value = await misskeyApi('channels/show', {
channelId: props.channelId,
});
miLocalStorage.setItem(`channel:${props.channelId}`, JSON.stringify(channel.value));
favorited.value = channel.value.isFavorited ?? false;
if (favorited.value || channel.value.isFollowing) {
tab.value = 'timeline';

View File

@ -332,6 +332,7 @@ const routes: RouteDef[] = [{
component: page(() => import('@/pages/channel-editor.vue')),
loginRequired: true,
}, {
name: 'channel',
path: '/channels/:channelId',
component: page(() => import('@/pages/channel.vue')),
}, {

View File

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Resolved } from '@/nirax.js';
import { post } from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { miLocalStorage } from '@/local-storage.js';
/** 「ノート」というボタンを押させるときはこっちを呼ぶ */
export async function postButtonHandler(currentRef: Resolved) {
if (currentRef.route.name === 'channel') {
const channelId = currentRef.props.get('channelId');
if (typeof channelId === 'string') {
// NOTE: チャンネルを開いているならば、チャンネルの情報がキャッシュされていることを期待できるはずである
const channelJSON = miLocalStorage.getItem(`channel:${channelId}`);
if (channelJSON) {
const channel = JSON.parse(channelJSON);
if (channel) {
await post({ channel });
return;
}
miLocalStorage.removeItem(`channel:${channelId}`);
}
const channel = await misskeyApi('channels/show', { channelId });
await post({ channel });
return;
}
}
await post();
}

View File

@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</div>
<div :class="$style.bottom">
<button class="_button" :class="$style.post" data-cy-open-post-form @click="os.post">
<button class="_button" :class="$style.post" data-cy-open-post-form @click="post">
<i :class="$style.postIcon" class="ti ti-pencil ti-fw"></i><span style="position: relative;">{{ i18n.ts.note }}</span>
</button>
<button class="_button" :class="$style.account" @click="openAccountMenu">
@ -57,7 +57,10 @@ import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { useRouter } from '@/router/supplier.js';
import { postButtonHandler } from '@/scripts/post-button-handler.js';
const router = useRouter();
const menu = toRef(defaultStore.state, 'menu');
const otherMenuItemIndicated = computed(() => {
for (const def in navbarItemDef) {
@ -78,6 +81,10 @@ function more() {
closed: () => dispose(),
});
}
function post() {
postButtonHandler(router.currentRef.value);
}
</script>
<style lang="scss" module>

View File

@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA>
</div>
<div :class="$style.bottom">
<button v-tooltip.noDelay.right="i18n.ts.note" class="_button" :class="[$style.post]" data-cy-open-post-form @click="os.post">
<button v-tooltip.noDelay.right="i18n.ts.note" class="_button" :class="[$style.post]" data-cy-open-post-form @click="post">
<i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note }}</span>
</button>
<button v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="_button" :class="[$style.account]" @click="openAccountMenu">
@ -68,7 +68,10 @@ import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { useRouter } from '@/router/supplier.js';
import { postButtonHandler } from '@/scripts/post-button-handler.js';
const router = useRouter();
const iconOnly = ref(false);
const menu = computed(() => defaultStore.state.menu);
@ -105,6 +108,10 @@ function more(ev: MouseEvent) {
closed: () => dispose(),
});
}
function post() {
postButtonHandler(router.currentRef.value);
}
</script>
<style lang="scss" module>