feat: finish list CRUD (#1532)

Co-authored-by: userquin <userquin@gmail.com>
closes https://github.com/elk-zone/elk/issues/372
This commit is contained in:
Evan Boehs 2023-02-02 16:02:39 -05:00 committed by GitHub
parent 809d4a6eb3
commit e58d09b6cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 383 additions and 150 deletions

View file

@ -91,56 +91,58 @@ const isNotifiedOnPost = $computed(() => !!relationship?.notifying)
</component>
<div p4 mt--18 flex flex-col gap-4>
<div relative>
<div flex="~ col gap-2 1">
<button :class="{ 'rounded-full': !isSelf, 'squircle': isSelf }" w-30 h-30 p1 bg-base border-bg-base z-2 @click="previewAvatar">
<div flex justify-between>
<button shrink-0 :class="{ 'rounded-full': !isSelf, 'squircle': isSelf }" w-30 h-30 p1 bg-base border-bg-base z-2 @click="previewAvatar">
<AccountAvatar :square="isSelf" :account="account" hover:opacity-90 transition-opacity />
</button>
<div flex="~ col gap1">
<div flex justify-between>
<AccountDisplayName :account="account" font-bold sm:text-2xl text-xl />
<AccountBotIndicator v-if="account.bot" show-label />
</div>
<AccountHandle :account="account" />
<div inset-ie-0 flex="~ wrap row-reverse" gap-2 items-center pt18 justify-start>
<!-- Edit profile -->
<NuxtLink
v-if="isSelf"
to="/settings/profile/appearance"
gap-1 items-center border="1" rounded-full flex="~ gap2 center" font-500 min-w-30 h-fit px3 py1
hover="border-primary text-primary bg-active"
>
{{ $t('settings.profile.appearance.title') }}
</NuxtLink>
<AccountFollowButton :account="account" :command="command" />
<span inset-ie-0 flex gap-2 items-center>
<AccountMoreButton :account="account" :command="command" />
<CommonTooltip v-if="!isSelf && relationship?.following" :content="getNotificationIconTitle()">
<button
:aria-pressed="isNotifiedOnPost"
:aria-label="t('account.notifications_on_post_enable', { username: `@${account.username}` })"
rounded-full text-sm p2 border-1 transition-colors
:class="isNotifiedOnPost ? 'text-primary border-primary hover:bg-red/20 hover:text-red hover:border-red' : 'border-base hover:text-primary'"
@click="toggleNotifications"
>
<span v-if="isNotifiedOnPost" i-ri:notification-4-fill block text-current />
<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>
</span>
</div>
</div>
<div absolute top-18 inset-ie-0 flex gap-2 items-center>
<AccountMoreButton :account="account" :command="command" />
<CommonTooltip v-if="!isSelf && relationship?.following" :content="getNotificationIconTitle()">
<button
:aria-pressed="isNotifiedOnPost"
:aria-label="t('account.notifications_on_post_enable', { username: `@${account.username}` })"
rounded-full text-sm p2 border-1 transition-colors
:class="isNotifiedOnPost ? 'text-primary border-primary hover:bg-red/20 hover:text-red hover:border-red' : 'border-base hover:text-primary'"
@click="toggleNotifications"
>
<span v-if="isNotifiedOnPost" i-ri:notification-4-fill block text-current />
<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
v-if="isSelf"
to="/settings/profile/appearance"
gap-1 items-center border="1" rounded-full flex="~ gap2 center" font-500 min-w-30 h-fit px3 py1
hover="border-primary text-primary bg-active"
>
{{ $t('settings.profile.appearance.title') }}
</NuxtLink>
<div flex="~ col gap1" pt2>
<div flex justify-between>
<AccountDisplayName :account="account" font-bold sm:text-2xl text-xl />
<AccountBotIndicator v-if="account.bot" show-label />
</div>
<AccountHandle :account="account" />
</div>
</div>
<div v-if="account.note" max-h-100 overflow-y-auto>

View file

@ -51,6 +51,23 @@ nuxtApp.hook('elk-logo:click', () => {
update()
nuxtApp.$scrollToTop()
})
function createEntry(item: any) {
items.value = [...items.value, preprocess?.([item]) ?? item]
}
function updateEntry(item: any) {
const id = item[keyProp]
const index = items.value.findIndex(i => (i as any)[keyProp] === id)
if (index > -1)
items.value = [...items.value.slice(0, index), preprocess?.([item]) ?? item, ...items.value.slice(index + 1)]
}
function removeEntry(entryId: any) {
items.value = items.value.filter(i => (i as any)[keyProp] !== entryId)
}
defineExpose({ createEntry, removeEntry, updateEntry })
</script>
<template>

View file

@ -0,0 +1,169 @@
<script setup lang="ts">
import type { mastodon } from 'masto'
const emit = defineEmits<{
(e: 'listUpdated', list: mastodon.v1.List): void
(e: 'listRemoved', id: string): void
}>()
const { list } = $defineProps<{
list: mastodon.v1.List
}>()
const { modelValue } = defineModel<{
modelValue: string
}>()
modelValue.value = list.title
const { t } = useI18n()
const client = useMastoClient()
let isEditing = $ref<boolean>(false)
let busy = $ref<boolean>(false)
let deleteBusy = $ref<boolean>(false)
const enableSaveButton = computed(() => list.title !== modelValue.value)
const edit = ref()
const input = ref()
const prepareEdit = () => {
isEditing = true
nextTick(() => {
input.value.focus()
})
}
const cancelEdit = (updateTitle = true) => {
isEditing = false
if (updateTitle)
modelValue.value = list.title
nextTick(() => {
edit.value.focus()
})
}
async function finishEditing() {
if (busy || !isEditing || !enableSaveButton.value)
return
busy = true
await nextTick()
try {
const updateList = await client.v1.lists.update(list.id, {
title: modelValue.value,
})
cancelEdit(false)
emit('listUpdated', updateList)
}
finally {
busy = false
}
}
async function removeList() {
if (deleteBusy)
return
deleteBusy = true
await nextTick()
const confirmDelete = await openConfirmDialog({
title: t('confirm.delete_list.title'),
confirm: t('confirm.delete_list.confirm'),
cancel: t('confirm.delete_list.cancel'),
})
if (confirmDelete === 'confirm') {
await nextTick()
try {
await client.v1.lists.remove(list.id)
emit('listRemoved', list.id)
}
finally {
deleteBusy = false
}
}
else {
deleteBusy = false
}
}
onBeforeUnmount(() => cancelEdit(false))
</script>
<template>
<form hover:bg-active flex justify-between items-center @submit.prevent="finishEditing">
<div
v-if="isEditing"
bg-base border="~ base" h10 m2 ps-1 pe-4 rounded-3 w-full flex="~ row"
items-center relative focus-within:box-shadow-outline gap-3
>
<CommonTooltip v-if="isEditing" :content="$t('list.cancel_edit')">
<button
type="button"
rounded-full text-sm p2 transition-colors
hover:text-primary
@click="cancelEdit(true)"
>
<span block text-current i-ri:close-fill />
</button>
</CommonTooltip>
<input
ref="input"
v-model="modelValue"
rounded-3
w-full
bg-transparent
outline="focus:none"
pe-4
pb="1px"
flex-1
placeholder-text-secondary
@keydown.esc="cancelEdit(true)"
>
</div>
<NuxtLink v-else :to="`list/${list.id}`" block grow p4>
{{ list.title }}
</NuxtLink>
<div mr4 flex gap2>
<CommonTooltip v-if="isEditing" :content="$t('list.save')">
<button
type="submit"
text-sm p2 border-1 transition-colors
border-dark hover:text-primary
btn-action-icon
:disabled="deleteBusy || !enableSaveButton || busy"
>
<template v-if="isEditing">
<span v-if="busy" aria-hidden="true" block animate animate-spin preserve-3d class="rtl-flip">
<span block i-ri:loader-2-fill aria-hidden="true" />
</span>
<span v-else block text-current i-ri:save-2-fill class="rtl-flip" />
</template>
</button>
</CommonTooltip>
<CommonTooltip v-else :content="$t('list.edit')">
<button
ref="edit"
type="button"
text-sm p2 border-1 transition-colors
border-dark hover:text-primary
btn-action-icon
@click.prevent="prepareEdit"
>
<span block text-current i-ri:edit-2-line class="rtl-flip" />
</button>
</CommonTooltip>
<CommonTooltip :content="$t('list.delete')">
<button
type="button"
text-sm p2 border-1 transition-colors
border-dark hover:text-primary
btn-action-icon
:disabled="isEditing"
@click.prevent="removeList"
>
<span v-if="deleteBusy" aria-hidden="true" block animate animate-spin preserve-3d class="rtl-flip">
<span block i-ri:loader-2-fill aria-hidden="true" />
</span>
<span v-else block text-current i-ri:delete-bin-2-line class="rtl-flip" />
</button>
</CommonTooltip>
</div>
</form>
</template>