Skip to content
Snippets Groups Projects
Commit 22049b10 authored by syuilo's avatar syuilo
Browse files

Improve timeline page

parent de86644c
No related branches found
No related tags found
No related merge requests found
......@@ -115,6 +115,22 @@ export default defineComponent({
endpoint = 'notes/global-timeline';
this.connection = os.stream.useSharedConnection('globalTimeline');
this.connection.on('note', prepend);
} else if (this.src == 'mentions') {
endpoint = 'notes/mentions';
this.connection = os.stream.useSharedConnection('main');
this.connection.on('mention', prepend);
} else if (this.src == 'directs') {
endpoint = 'notes/mentions';
this.query = {
visibility: 'specified'
};
const onNote = note => {
if (note.visibility == 'specified') {
prepend(note);
}
};
this.connection = os.stream.useSharedConnection('main');
this.connection.on('mention', onNote);
} else if (this.src == 'list') {
endpoint = 'notes/user-list-timeline';
this.query = {
......
<template>
<div class="mk-home" v-hotkey.global="keymap">
<div class="cmuxhskf" v-hotkey.global="keymap">
<div class="new" v-if="queue > 0" :style="{ width: width + 'px' }"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
<div class="_section">
<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _content _vMargin"/>
<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _panel _content _vMargin" fixed/>
<div class="tabs _panel _vMargin">
<div class="left">
<button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><Fa :icon="faHome"/></button>
<button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local"><Fa :icon="faComments"/></button>
<button class="_button tab" @click="() => { src = 'social'; saveSrc(); }" :class="{ active: src === 'social' }" v-tooltip="$ts._timelines.social"><Fa :icon="faShareAlt"/></button>
<button class="_button tab" @click="() => { src = 'global'; saveSrc(); }" :class="{ active: src === 'global' }" v-tooltip="$ts._timelines.global"><Fa :icon="faGlobe"/></button>
</div>
<div class="right">
<button class="_button tab" @click="chooseChannel" :class="{ active: src === 'channel' }" v-tooltip="$ts.channel"><Fa :icon="faSatelliteDish"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadChannel"/></button>
<button class="_button tab" @click="chooseAntenna" :class="{ active: src === 'antenna' }" v-tooltip="$ts.antennas"><Fa :icon="faSatellite"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadAntenna"/></button>
<button class="_button tab" @click="chooseList" :class="{ active: src === 'list' }" v-tooltip="$ts.lists"><Fa :icon="faListUl"/></button>
<button class="_button tab" @click="() => { src = 'directs'; saveSrc(); }" :class="{ active: src === 'directs' }" v-tooltip="$ts.directNotes"><Fa :icon="faEnvelope"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadSpecifiedNotes"/></button>
<button class="_button tab" @click="() => { src = 'mentions'; saveSrc(); }" :class="{ active: src === 'mentions' }" v-tooltip="$ts.mentions"><Fa :icon="faAt"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadMentions"/></button>
<button class="_button tab" @click="chooseTl"><Fa :icon="faEllipsisH"/></button>
</div>
</div>
<XTimeline ref="tl"
class="_content _vMargin"
:key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src"
......@@ -23,8 +39,8 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent, computed } from 'vue';
import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { faComments } from '@fortawesome/free-regular-svg-icons';
import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faPencilAlt, faAt } from '@fortawesome/free-solid-svg-icons';
import { faComments, faEnvelope } from '@fortawesome/free-regular-svg-icons';
import Progress from '@/scripts/loading';
import XTimeline from '@/components/timeline.vue';
import XPostForm from '@/components/post-form.vue';
......@@ -49,64 +65,15 @@ export default defineComponent({
menuOpened: false,
queue: 0,
width: 0,
INFO: computed(() => {
const tabs = [{
id: 'home',
title: null,
tooltip: this.$ts._timelines.home,
icon: faHome,
onClick: () => { this.src = 'home'; this.saveSrc(); },
selected: computed(() => this.src === 'home')
}];
if (!this.$instance.disableLocalTimeline || this.$i.isModerator || this.$i.isAdmin) {
tabs.push({
id: 'local',
title: null,
tooltip: this.$ts._timelines.local,
icon: faComments,
onClick: () => { this.src = 'local'; this.saveSrc(); },
selected: computed(() => this.src === 'local')
});
tabs.push({
id: 'social',
title: null,
tooltip: this.$ts._timelines.social,
icon: faShareAlt,
onClick: () => { this.src = 'social'; this.saveSrc(); },
selected: computed(() => this.src === 'social')
});
INFO: computed(() => ({
title: this.$ts.timeline,
icon: this.src === 'local' ? faComments : this.src === 'social' ? faShareAlt : this.src === 'global' ? faGlobe : faHome,
action: {
icon: faPencilAlt,
handler: () => os.post()
}
if (!this.$instance.disableGlobalTimeline || this.$i.isModerator || this.$i.isAdmin) {
tabs.push({
id: 'global',
title: null,
tooltip: this.$ts._timelines.global,
icon: faGlobe,
onClick: () => { this.src = 'global'; this.saveSrc(); },
selected: computed(() => this.src === 'global')
});
}
tabs.push({
id: 'other',
title: null,
icon: faEllipsisH,
onClick: this.choose,
indicate: computed(() => this.$i.hasUnreadAntenna || this.$i.hasUnreadChannel)
});
return {
tabs,
action: {
icon: faPencilAlt,
handler: () => os.post()
}
};
}),
faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faSatelliteDish, faCircle
})),
faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faAt, faEnvelope,
};
},
......@@ -176,35 +143,37 @@ export default defineComponent({
scroll(this.$el, 0);
},
async choose(ev) {
if (this.meta == null) return;
const [antennas, lists, channels] = await Promise.all([
os.api('antennas/list'),
os.api('users/lists/list'),
os.api('channels/followed'),
]);
const antennaItems = antennas.map(antenna => ({
text: antenna.name,
icon: faSatellite,
indicate: antenna.hasUnreadNote,
async chooseList(ev) {
const lists = await os.api('users/lists/list');
const items = lists.map(list => ({
text: list.name,
action: () => {
this.antenna = antenna;
this.src = 'antenna';
this.list = list;
this.src = 'list';
this.saveSrc();
}
}));
const listItems = lists.map(list => ({
text: list.name,
icon: faListUl,
os.modalMenu(items, ev.currentTarget || ev.target);
},
async chooseAntenna(ev) {
const antennas = await os.api('antennas/list');
const items = antennas.map(antenna => ({
text: antenna.name,
indicate: antenna.hasUnreadNote,
action: () => {
this.list = list;
this.src = 'list';
this.antenna = antenna;
this.src = 'antenna';
this.saveSrc();
}
}));
const channelItems = channels.map(channel => ({
os.modalMenu(items, ev.currentTarget || ev.target);
},
async chooseChannel(ev) {
const channels = await os.api('channels/followed');
const items = channels.map(channel => ({
text: channel.name,
icon: faSatelliteDish,
indicate: channel.hasUnreadNote,
action: () => {
// NOTE: チャンネルタイムラインをこのコンポーネントで表示するようにすると投稿フォームはどうするかなどの問題が生じるのでとりあえずページ遷移で
......@@ -214,7 +183,7 @@ export default defineComponent({
this.$router.push(`/channels/${channel.id}`);
}
}));
os.modalMenu([...antennaItems, listItems.length > 0 ? null : undefined, ...listItems, channelItems.length > 0 ? null : undefined, ...channelItems], ev.currentTarget || ev.target);
os.modalMenu(items, ev.currentTarget || ev.target);
},
saveSrc() {
......@@ -235,7 +204,7 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
.mk-home {
.cmuxhskf {
> .new {
position: fixed;
z-index: 1000;
......@@ -249,7 +218,59 @@ export default defineComponent({
}
> ._section {
> .tabs {
display: flex;
box-sizing: border-box;
padding: 0 8px;
max-width: var(--baseContentWidth);
margin-left: auto;
margin-right: auto;
white-space: nowrap;
overflow: auto;
> .right {
margin-left: auto;
}
> .left, > .right {
> .tab {
position: relative;
height: 50px;
padding: 0 12px;
&:hover {
color: var(--fgHighlighted);
}
&.active {
color: var(--fgHighlighted);
&:after {
content: "";
display: block;
position: absolute;
bottom: 0;
left: 0;
right: 0;
margin: 0 auto;
width: calc(100% - 16px);
height: 4px;
background: var(--accent);
border-radius: 8px 8px 0 0;
}
}
> .i {
position: absolute;
top: 16px;
right: 8px;
color: var(--indicator);
font-size: 8px;
animation: blink 1s infinite;
}
}
}
}
}
}
</style>
......@@ -5,21 +5,12 @@
</transition>
<template v-if="info">
<div class="titleContainer">
<template v-if="info.tabs">
<div class="title" v-for="tab in info.tabs" :key="tab.id" :class="{ _button: tab.onClick, selected: tab.selected }" @click.stop="tab.onClick" v-tooltip="tab.tooltip">
<Fa v-if="tab.icon" :icon="tab.icon" :key="tab.icon" class="icon"/>
<span v-if="tab.title" class="text">{{ tab.title }}</span>
<Fa class="indicator" v-if="tab.indicate" :icon="faCircle"/>
</div>
</template>
<template v-else>
<div class="title">
<Fa v-if="info.icon" :icon="info.icon" :key="info.icon" class="icon"/>
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true"/>
<span v-if="info.title" class="text">{{ info.title }}</span>
<MkUserName v-else-if="info.userName" :user="info.userName" :nowrap="false" class="text"/>
</div>
</template>
<div class="title">
<Fa v-if="info.icon" :icon="info.icon" :key="info.icon" class="icon"/>
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true"/>
<span v-if="info.title" class="text">{{ info.title }}</span>
<MkUserName v-else-if="info.userName" :user="info.userName" :nowrap="false" class="text"/>
</div>
</div>
<button class="_button action" v-if="info.action" @click.stop="info.action.handler"><Fa :icon="info.action.icon" :key="info.action.icon"/></button>
</template>
......@@ -155,17 +146,6 @@ export default defineComponent({
height: $size;
vertical-align: bottom;
}
&._button {
&:hover {
color: var(--fgHighlighted);
}
}
&.selected {
box-shadow: 0 -2px 0 0 var(--accent) inset;
color: var(--fgHighlighted);
}
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment