feat: more list support (#1479)

This commit is contained in:
Evan Boehs 2023-01-27 10:15:46 -05:00 committed by GitHub
parent 9d353fa07b
commit e393049f04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 212 additions and 1 deletions

View file

@ -117,6 +117,20 @@ const isNotifiedOnPost = $computed(() => !!relationship?.notifying)
<span v-else i-ri-notification-4-line block text-current />
</button>
</CommonTooltip>
<CommonTooltip :content="$t('list.modify_account')">
<VDropdown v-if="!isSelf && relationship?.following">
<button
:aria-label="$t('list.modify_account')"
rounded-full text-sm p2 border-1 transition-colors
border-base hover:text-primary
>
<span i-ri:play-list-add-fill block text-current />
</button>
<template #popper>
<ListLists :user-id="account.id" />
</template>
</VDropdown>
</CommonTooltip>
<AccountFollowButton :account="account" :command="command" />
<!-- Edit profile -->
<NuxtLink

View file

@ -11,6 +11,7 @@ const {
virtualScroller = false,
eventType = 'update',
preprocess,
noEndMessage = false,
} = defineProps<{
paginator: Paginator<T[], O>
keyProp?: keyof T
@ -18,6 +19,7 @@ const {
stream?: Promise<WsEvents>
eventType?: 'notification' | 'update'
preprocess?: (items: (U | T)[]) => U[]
noEndMessage?: boolean
}>()
defineSlots<{
@ -84,7 +86,7 @@ const { items, prevItems, update, state, endAnchor, error } = usePaginator(pagin
<slot v-if="state === 'loading'" name="loading">
<TimelineSkeleton />
</slot>
<slot v-else-if="state === 'done'" name="done">
<slot v-else-if="state === 'done' && !noEndMessage" name="done">
<div p5 text-secondary italic text-center>
{{ t('common.end_of_list') }}
</div>

View file

@ -0,0 +1,44 @@
<script setup lang="ts">
import type { mastodon } from 'masto'
const { account, list } = defineProps<{
account: mastodon.v1.Account
hoverCard?: boolean
list: string
}>()
cacheAccount(account)
const client = useMastoClient()
const isRemoved = ref(false)
async function edit() {
try {
isRemoved.value
? await client.v1.lists.addAccount(list, { accountIds: [account.id] })
: await client.v1.lists.removeAccount(list, { accountIds: [account.id] })
isRemoved.value = !isRemoved.value
}
catch (err) {
console.error(err)
}
}
</script>
<template>
<div flex justify-between hover:bg-active transition-100 items-center>
<AccountInfo
:account="account" hover p1 as="router-link"
:hover-card="hoverCard"
shrink
overflow-hidden
:to="getAccountRoute(account)"
/>
<div>
<CommonTooltip :content="isRemoved ? $t('list.add_account') : $t('list.remove_account')" :hover="isRemoved ? 'text-green' : 'text-red'">
<button :class="isRemoved ? 'i-ri:user-add-line' : 'i-ri:user-unfollow-line'" text-xl @click="edit" />
</CommonTooltip>
</div>
</div>
</template>

49
components/list/Lists.vue Normal file
View file

@ -0,0 +1,49 @@
<script lang="ts" setup>
const { userId } = defineProps<{
userId: string
}>()
const { client } = $(useMasto())
const paginator = client.v1.lists.list()
const listsWithUser = ref((await client.v1.accounts.listLists(userId)).map(list => list.id))
function indexOfUserInList(listId: string) {
return listsWithUser.value.indexOf(listId)
}
async function edit(listId: string) {
try {
const index = indexOfUserInList(listId)
if (index === -1) {
await client.v1.lists.addAccount(listId, { accountIds: [userId] })
listsWithUser.value.push(listId)
}
else {
await client.v1.lists.removeAccount(listId, { accountIds: [userId] })
listsWithUser.value = listsWithUser.value.filter(id => id !== listId)
}
}
catch (err) {
console.error(err)
}
}
</script>
<template>
<CommonPaginator no-end-message :paginator="paginator">
<template #default="{ item }">
<div p4 hover:bg-active block w="100%" flex justify-between items-center gap-4>
<p>{{ item.title }}</p>
<CommonTooltip
:content="indexOfUserInList(item.id) === -1 ? $t('list.add_account') : $t('list.remove_account')"
:hover="indexOfUserInList(item.id) === -1 ? 'text-green' : 'text-red'"
>
<button
:class="indexOfUserInList(item.id) === -1 ? 'i-ri:user-add-line' : 'i-ri:user-unfollow-line'"
text-xl @click="() => edit(item.id)"
/>
</CommonTooltip>
</div>
</template>
</CommonPaginator>
</template>

View file

@ -174,6 +174,11 @@
"language": {
"search": "Search"
},
"list": {
"add_account": "Add account to list",
"modify_account": "Modify lists with account",
"remove_account": "Remove account from list"
},
"menu": {
"block_account": "Block {0}",
"block_domain": "Block domain {0}",
@ -447,8 +452,10 @@
"edited": "edited {0}"
},
"tab": {
"accounts": "Accounts",
"for_you": "For you",
"hashtags": "Hashtags",
"list": "List",
"media": "Media",
"news": "News",
"notifications_all": "All",

55
pages/[[server]]/list.vue Normal file
View file

@ -0,0 +1,55 @@
<script setup lang="ts">
import type { CommonRouteTabOption } from '~/components/common/CommonRouteTabs.vue'
const list = $computed(() => useRoute().params.list as string)
const server = $computed(() => useRoute().params.server as string)
const { t } = useI18n()
const tabs = $computed<CommonRouteTabOption[]>(() => [
{
to: {
name: 'list',
params: { server, list },
},
display: t('tab.list'),
icon: 'i-ri:list-unordered',
},
{
to: {
name: 'list-accounts',
params: { server, list },
},
display: t('tab.accounts'),
icon: 'i-ri:user-line',
},
],
)
const { client } = $(useMasto())
const { data: listInfo, refresh } = $(await useAsyncData(() => client.v1.lists.fetch(list), { default: () => shallowRef() }))
if (listInfo) {
useHeadFixed({
title: () => `${listInfo.title}`,
})
}
onReactivated(() => {
// Silently update data when reentering the page
// The user will see the previous content first, and any changes will be updated to the UI when the request is completed
refresh()
})
</script>
<template>
<MainContent back>
<template #title>
<span text-lg font-bold>{{ listInfo ? listInfo.title : t('nav.list') }}</span>
</template>
<template #header>
<CommonRouteTabs replace :options="tabs" />
</template>
<NuxtPage v-if="isHydrated" />
</MainContent>
</template>

View file

@ -0,0 +1,23 @@
<script setup lang="ts">
definePageMeta({
name: 'list-accounts',
})
const params = useRoute().params
const listId = $(computedEager(() => params.list as string))
const paginator = useMastoClient().v1.lists.listAccounts(listId)
</script>
<template>
<CommonPaginator :paginator="paginator">
<template #default="{ item }">
<ListAccount
:account="item"
:list="listId"
hover-card
border="b base" py2 px4
/>
</template>
</CommonPaginator>
</template>

View file

@ -0,0 +1,17 @@
<script setup lang="ts">
definePageMeta({
name: 'list',
})
const params = useRoute().params
const listId = $(computedEager(() => params.list as string))
const { client } = $(useMasto())
const paginator = client.v1.timelines.listList(listId)
const stream = useStreaming(client => client.v1.stream.streamListTimeline(listId))
</script>
<template>
<TimelinePaginator v-bind="{ paginator, stream }" :preprocess="reorderedTimeline" context="home" />
</template>