diff options
| author | ikasoba <57828948+ikasoba@users.noreply.github.com> | 2023-12-22 20:41:42 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-22 20:41:42 +0900 |
| commit | 5eb944ecdeb0d65ec82b89522dfdf37d980bdb51 (patch) | |
| tree | 64f65c61b3f82db257090def7e8918528415488e /packages/frontend/src/components | |
| parent | [Hub Next] Misskey Hubのリンクを変更 (#12699) (diff) | |
| download | sharkey-5eb944ecdeb0d65ec82b89522dfdf37d980bdb51.tar.gz sharkey-5eb944ecdeb0d65ec82b89522dfdf37d980bdb51.tar.bz2 sharkey-5eb944ecdeb0d65ec82b89522dfdf37d980bdb51.zip | |
enhance: チャンネルに新規の投稿がある場合にバッジを表示させる (#12690)
* 多分できたかも
* 不要なpropsを削除
* 不要なimportを削除
* 縁を付けた
* 枠線の位置を端に寄せた
* やっぱり内側へ寄せることにした
* できたかも
* 修正
* 修正
* クラスにまとめた
* 微調整
* 直せたかも
* importを付け足し
* 多分できたかも
* Update channel.vue
* Update MkMenu.vue
* Update channel.vue
* Update CHANGELOG.md
---------
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Diffstat (limited to 'packages/frontend/src/components')
| -rw-r--r-- | packages/frontend/src/components/MkChannelPreview.vue | 98 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkMenu.vue | 64 |
2 files changed, 111 insertions, 51 deletions
diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue index 4512f2dd60..bf6504d6bf 100644 --- a/packages/frontend/src/components/MkChannelPreview.vue +++ b/packages/frontend/src/components/MkChannelPreview.vue @@ -4,49 +4,70 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1"> - <div class="banner" :style="bannerStyle"> - <div class="fade"></div> - <div class="name"><i class="ti ti-device-tv"></i> {{ channel.name }}</div> - <div v-if="channel.isSensitive" class="sensitiveIndicator">{{ i18n.ts.sensitive }}</div> - <div class="status"> - <div> - <i class="ti ti-users ti-fw"></i> - <I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;"> - <template #n> - <b>{{ channel.usersCount }}</b> - </template> - </I18n> - </div> - <div> - <i class="ti ti-pencil ti-fw"></i> - <I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;"> - <template #n> - <b>{{ channel.notesCount }}</b> - </template> - </I18n> +<div style="position: relative;"> + <MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1" @click="updateLastReadedAt"> + <div class="banner" :style="bannerStyle"> + <div class="fade"></div> + <div class="name"><i class="ti ti-device-tv"></i> {{ channel.name }}</div> + <div v-if="channel.isSensitive" class="sensitiveIndicator">{{ i18n.ts.sensitive }}</div> + <div class="status"> + <div> + <i class="ti ti-users ti-fw"></i> + <I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;"> + <template #n> + <b>{{ channel.usersCount }}</b> + </template> + </I18n> + </div> + <div> + <i class="ti ti-pencil ti-fw"></i> + <I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;"> + <template #n> + <b>{{ channel.notesCount }}</b> + </template> + </I18n> + </div> </div> </div> - </div> - <article v-if="channel.description"> - <p :title="channel.description">{{ channel.description.length > 85 ? channel.description.slice(0, 85) + '…' : channel.description }}</p> - </article> - <footer> - <span v-if="channel.lastNotedAt"> - {{ i18n.ts.updatedAt }}: <MkTime :time="channel.lastNotedAt"/> - </span> - </footer> -</MkA> + <article v-if="channel.description"> + <p :title="channel.description">{{ channel.description.length > 85 ? channel.description.slice(0, 85) + '…' : channel.description }}</p> + </article> + <footer> + <span v-if="channel.lastNotedAt"> + {{ i18n.ts.updatedAt }}: <MkTime :time="channel.lastNotedAt"/> + </span> + </footer> + </MkA> + <div + v-if="channel.lastNotedAt && (channel.isFavorited || channel.isFollowing) && (!lastReadedAt || Date.parse(channel.lastNotedAt) > lastReadedAt)" + class="indicator" + ></div> +</div> </template> <script lang="ts" setup> -import { computed } from 'vue'; +import { computed, ref, watch } from 'vue'; import { i18n } from '@/i18n.js'; +import { miLocalStorage } from '@/local-storage.js'; const props = defineProps<{ channel: Record<string, any>; }>(); +const getLastReadedAt = (): number | null => { + return miLocalStorage.getItemAsJson(`channelLastReadedAt:${props.channel.id}`) ?? null; +}; + +const lastReadedAt = ref(getLastReadedAt()); + +watch(() => props.channel.id, () => { + lastReadedAt.value = getLastReadedAt(); +}); + +const updateLastReadedAt = () => { + lastReadedAt.value = props.channel.lastNotedAt ? Date.parse(props.channel.lastNotedAt) : Date.now(); +}; + const bannerStyle = computed(() => { if (props.channel.bannerUrl) { return { backgroundImage: `url(${props.channel.bannerUrl})` }; @@ -170,4 +191,17 @@ const bannerStyle = computed(() => { } } +.indicator { + position: absolute; + top: 0; + right: 0; + transform: translate(25%, -25%); + background-color: var(--accent); + border: solid var(--bg) 4px; + border-radius: 100%; + width: 1.5rem; + height: 1.5rem; + aspect-ratio: 1 / 1; +} + </style> diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 8e4b86f1c7..0a97cf3dba 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-for="(item, i) in items2"> <div v-if="item.type === 'divider'" role="separator" :class="$style.divider"></div> <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]"> - <span>{{ item.text }}</span> + <span style="opacity: 0.7;">{{ item.text }}</span> </span> <span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item]"> <span><MkEllipsis/></span> @@ -23,32 +23,44 @@ SPDX-License-Identifier: AGPL-3.0-only <MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="$style.item" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/> - <span>{{ item.text }}</span> - <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> + <div :class="$style.item_content"> + <span :class="$style.item_content_text">{{ item.text }}</span> + <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> + </div> </MkA> <a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> - <span>{{ item.text }}</span> - <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> + <div :class="$style.item_content"> + <span :class="$style.item_content_text">{{ item.text }}</span> + <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> + </div> </a> <button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/> - <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> + <div v-if="item.indicate" :class="$style.item_content"> + <span :class="$style.indicator"><i class="_indicatorCircle"></i></span> + </div> </button> <button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } ]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/> - <span :class="$style.switchText">{{ item.text }}</span> + <div :class="$style.item_content"> + <span :class="[$style.item_content_text, $style.switchText]">{{ item.text }}</span> + </div> </button> <button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]" style="pointer-events: none;"></i> - <span style="pointer-events: none;">{{ item.text }}</span> - <span :class="$style.caret" style="pointer-events: none;"><i class="ti ti-chevron-right ti-fw"></i></span> + <div :class="$style.item_content"> + <span :class="$style.item_content_text" style="pointer-events: none;">{{ item.text }}</span> + <span :class="$style.caret" style="pointer-events: none;"><i class="ti ti-chevron-right ti-fw"></i></span> + </div> </button> <button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.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="ti-fw" :class="[$style.icon, item.icon]"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/> - <span>{{ item.text }}</span> - <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> + <div :class="$style.item_content"> + <span :class="$style.item_content_text">{{ item.text }}</span> + <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> + </div> </button> </template> <span v-if="items2.length === 0" :class="[$style.none, $style.item]"> @@ -228,6 +240,7 @@ onBeforeUnmount(() => { .root { padding: 8px 0; box-sizing: border-box; + max-width: 100vw; min-width: 200px; overflow: auto; overscroll-behavior: contain; @@ -267,7 +280,8 @@ onBeforeUnmount(() => { } .item { - display: block; + display: flex; + align-items: center; position: relative; padding: 5px 16px; width: 100%; @@ -340,10 +354,6 @@ onBeforeUnmount(() => { pointer-events: none; font-size: 0.7em; padding-bottom: 4px; - - > span { - opacity: 0.7; - } } &.pending { @@ -373,6 +383,22 @@ onBeforeUnmount(() => { } } +.item_content { + width: 100%; + max-width: 100vw; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + text-overflow: ellipsis; +} + +.item_content_text { + max-width: calc(100vw - 4rem); + text-overflow: ellipsis; + overflow: hidden; +} + .switch { position: relative; display: flex; @@ -406,6 +432,7 @@ onBeforeUnmount(() => { .icon { margin-right: 8px; + line-height: 1; } .caret { @@ -419,9 +446,8 @@ onBeforeUnmount(() => { } .indicator { - position: absolute; - top: 5px; - left: 13px; + display: flex; + align-items: center; color: var(--indicator); font-size: 12px; animation: blink 1s infinite; |