Re-implement user timeline in profile
This commit is contained in:
parent
1724c808bf
commit
6be10b2e09
|
@ -36,6 +36,7 @@ module.exports = {
|
|||
'space-before-function-paren': 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/attributes-order': 'off',
|
||||
'vue/attribute-hyphenation': 'off'
|
||||
'vue/attribute-hyphenation': 'off',
|
||||
'vue/no-v-html': 'off'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
.detail {
|
||||
width: 340px;
|
||||
width: 380px;
|
||||
height: 100%;
|
||||
border-left: 1px solid var(--theme-border-color);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
:server="account.server"
|
||||
v-on:update="updateToot"
|
||||
v-on:delete="deleteToot"
|
||||
@focusRight="focusSidebar"
|
||||
@selectToot="focusToot(item)"
|
||||
>
|
||||
</toot>
|
||||
|
@ -41,7 +40,6 @@ import { useRoute } from 'vue-router'
|
|||
import { useStore } from '@/store'
|
||||
import Toot from '@/components/organisms/Toot.vue'
|
||||
import StatusLoading from '@/components/organisms/StatusLoading.vue'
|
||||
import { EventEmitter } from '@/components/event'
|
||||
import { ACTION_TYPES, MUTATION_TYPES } from '@/store/TimelineSpace/Contents/Home'
|
||||
import { MUTATION_TYPES as SIDE_MENU_MUTATION } from '@/store/TimelineSpace/SideMenu'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
|
@ -215,9 +213,6 @@ export default defineComponent({
|
|||
const focusToot = (message: Entity.Status) => {
|
||||
focusedId.value = message.uri + message.id
|
||||
}
|
||||
const focusSidebar = () => {
|
||||
EventEmitter.emit('focus-sidebar')
|
||||
}
|
||||
|
||||
return {
|
||||
filteredTimeline,
|
||||
|
@ -231,7 +226,6 @@ export default defineComponent({
|
|||
deleteToot,
|
||||
focusNext,
|
||||
focusPrev,
|
||||
focusSidebar,
|
||||
focusToot,
|
||||
openSideBar,
|
||||
heading,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div id="account_profile" role="article" aria-label="account profile" v-if="user">
|
||||
<div class="header-background" v-bind:style="{ backgroundImage: 'url(' + user.header + ')' }">
|
||||
<div class="header-background" :style="{ backgroundImage: 'url(' + user.header + ')' }">
|
||||
<div class="header">
|
||||
<div class="relationship" v-if="relationship !== null && !isOwnProfile">
|
||||
<div class="follower-status">
|
||||
|
@ -93,13 +93,18 @@
|
|||
<dd v-html="data.value" @click.capture.prevent="metadataClick"></dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="timeline"></div>
|
||||
<el-tabs class="timeline" v-model="activeTab" stretch v-if="account.account && account.server">
|
||||
<el-tab-pane :label="precision(user.statuses_count) + ' Posts'" name="posts"
|
||||
><Posts :user="user" :account="account.account" :server="account.server"
|
||||
/></el-tab-pane>
|
||||
<el-tab-pane :label="precision(user.following_count) + ' Following'" name="following">Following</el-tab-pane>
|
||||
<el-tab-pane :label="precision(user.followers_count) + ' Followers'" name="followers">Followers</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, reactive, onMounted, watch } from 'vue'
|
||||
import { useI18next } from 'vue3-i18next'
|
||||
import { useRoute } from 'vue-router'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import { useStore } from '@/store'
|
||||
|
@ -111,17 +116,18 @@ import { LocalServer } from '~/src/types/localServer'
|
|||
import { ACTION_TYPES as LIST_MEMBERSHIP_ACTION } from '@/store/TimelineSpace/Modals/ListMembership'
|
||||
import { ACTION_TYPES as MUTE_ACTION } from '@/store/TimelineSpace/Modals/MuteConfirm'
|
||||
import FailoverImg from '@/components/atoms/FailoverImg.vue'
|
||||
import Posts from './Profile/Posts.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Profile',
|
||||
components: {
|
||||
FailoverImg
|
||||
FailoverImg,
|
||||
Posts
|
||||
},
|
||||
setup() {
|
||||
const win = (window as any) as MyWindow
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const i18n = useI18next()
|
||||
|
||||
const theme = computed(() => {
|
||||
return {
|
||||
|
@ -149,6 +155,7 @@ export default defineComponent({
|
|||
return false
|
||||
})
|
||||
const identityProofs = ref<Array<Entity.IdentityProof>>([])
|
||||
const activeTab = ref<'posts' | 'following' | 'followers'>('posts')
|
||||
|
||||
onMounted(async () => {
|
||||
const [a, s]: [LocalAccount, LocalServer] = await win.ipcRenderer.invoke('get-local-account', id.value)
|
||||
|
@ -274,6 +281,16 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
const precision = (num: number) => {
|
||||
if (num > 1000) {
|
||||
return `${(num / 1000).toPrecision(3)}K`
|
||||
} else if (num > 1000000) {
|
||||
return `${(num / 1000000).toPrecision(3)}M`
|
||||
} else {
|
||||
return num.toString()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
relationship,
|
||||
|
@ -293,7 +310,10 @@ export default defineComponent({
|
|||
note,
|
||||
noteClick,
|
||||
identityProofs,
|
||||
metadataClick
|
||||
metadataClick,
|
||||
precision,
|
||||
activeTab,
|
||||
account
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -494,6 +514,10 @@ export default defineComponent({
|
|||
.timeline {
|
||||
font-size: calc(var(--base-font-size) * 0.85);
|
||||
}
|
||||
|
||||
.timeline :deep(.el-tabs__item) {
|
||||
--el-text-color-primary: var(--theme-secondary-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<DynamicScroller :items="statuses" :min-item-size="86">
|
||||
<template v-slot="{ item, index, active }">
|
||||
<DynamicScrollerItem :item="item" :active="active" :size-dependencies="[item.uri]" :data-index="index" :watchData="true">
|
||||
<Toot
|
||||
v-if="account && server"
|
||||
:message="item"
|
||||
:focused="item.uri + item.id === focusedId"
|
||||
:overlaid="modalOpened"
|
||||
:account="account"
|
||||
:server="server"
|
||||
@update="updateToot"
|
||||
@delete="deleteToot"
|
||||
@select-toot="focusToot(item)"
|
||||
/>
|
||||
</DynamicScrollerItem>
|
||||
</template>
|
||||
</DynamicScroller>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, onMounted, ref, watch, toRefs, computed } from 'vue'
|
||||
import generator, { Entity, MegalodonInterface } from 'megalodon'
|
||||
import Toot from '@/components/organisms/Toot.vue'
|
||||
import { LocalAccount } from '~/src/types/localAccount'
|
||||
import { LocalServer } from '~/src/types/localServer'
|
||||
import { useStore } from '@/store'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Posts',
|
||||
components: { Toot },
|
||||
props: {
|
||||
user: {
|
||||
type: Object as PropType<Entity.Account>,
|
||||
required: true
|
||||
},
|
||||
account: {
|
||||
type: Object as PropType<LocalAccount>,
|
||||
required: true
|
||||
},
|
||||
server: {
|
||||
type: Object as PropType<LocalServer>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { user, account, server } = toRefs(props)
|
||||
const store = useStore()
|
||||
|
||||
const statuses = ref<Array<Entity.Status>>([])
|
||||
const client = ref<MegalodonInterface | null>(null)
|
||||
const focusedId = ref<string | null>(null)
|
||||
|
||||
const userAgent = computed(() => store.state.App.userAgent)
|
||||
const modalOpened = computed<boolean>(() => store.getters[`TimelineSpace/Modals/modalOpened`])
|
||||
|
||||
onMounted(async () => {
|
||||
client.value = generator(server.value.sns, server.value.baseURL, account.value.accessToken, userAgent.value)
|
||||
const res = await client.value.getAccountStatuses(user.value.id)
|
||||
statuses.value = res.data
|
||||
})
|
||||
|
||||
watch(user, async current => {
|
||||
if (client.value) {
|
||||
const res = await client.value.getAccountStatuses(current.id)
|
||||
statuses.value = res.data
|
||||
}
|
||||
})
|
||||
|
||||
const updateToot = (toot: Entity.Status) => {
|
||||
statuses.value = statuses.value.map(s => {
|
||||
if (s.id === toot.id) {
|
||||
return toot
|
||||
}
|
||||
return s
|
||||
})
|
||||
}
|
||||
|
||||
const deleteToot = (toot: Entity.Status) => {
|
||||
statuses.value = statuses.value.filter(s => s.id !== toot.id)
|
||||
}
|
||||
|
||||
const focusToot = (toot: Entity.Status) => {
|
||||
focusedId.value = toot.uri + toot.id
|
||||
}
|
||||
|
||||
return {
|
||||
statuses,
|
||||
modalOpened,
|
||||
updateToot,
|
||||
deleteToot,
|
||||
focusedId,
|
||||
focusToot
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -305,7 +305,7 @@ export default defineComponent({
|
|||
required: true
|
||||
}
|
||||
},
|
||||
emits: ['selectToot', 'focusRight', 'focusLeft', 'update', 'delete', 'sizeChanged'],
|
||||
emits: ['selectToot', 'update', 'delete', 'sizeChanged'],
|
||||
setup(props, ctx) {
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
|
@ -313,7 +313,7 @@ export default defineComponent({
|
|||
const i18n = useI18next()
|
||||
const win = (window as any) as MyWindow
|
||||
const { focused, overlaid, message, filters, account, server } = toRefs(props)
|
||||
const { l, h, r, b, f, o, p, i, x } = useMagicKeys()
|
||||
const { r, b, f, o, p, i, x } = useMagicKeys()
|
||||
|
||||
const statusRef = ref<any>(null)
|
||||
const showContent = ref(store.state.App.ignoreCW)
|
||||
|
@ -397,12 +397,6 @@ export default defineComponent({
|
|||
client.value = generator(server.value.sns, server.value.baseURL, account.value.accessToken, userAgent.value)
|
||||
})
|
||||
|
||||
whenever(logicAnd(l, shortcutEnabled), () => {
|
||||
ctx.emit('focusRight')
|
||||
})
|
||||
whenever(logicAnd(h, shortcutEnabled), () => {
|
||||
ctx.emit('focusLeft')
|
||||
})
|
||||
whenever(logicAnd(r, shortcutEnabled), () => {
|
||||
openReply()
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue