forked from Mirrors/elk
fix: type errors (#802)
This commit is contained in:
parent
272fb4a13d
commit
acdb94de62
24 changed files with 113 additions and 74 deletions
|
@ -1,4 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
// type used in <template>
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
import type { Account } from 'masto'
|
||||
|
||||
defineProps<{
|
||||
|
@ -14,8 +16,9 @@ defineProps<{
|
|||
</div>
|
||||
|
||||
<div flex>
|
||||
<NuxtLink :to="getAccountRoute(account.moved as any)">
|
||||
<AccountInfo :account="account.moved" />
|
||||
<!-- type error of masto.js -->
|
||||
<NuxtLink :to="getAccountRoute(account.moved as unknown as Account)">
|
||||
<AccountInfo :account="account.moved as unknown as Account" />
|
||||
</NuxtLink>
|
||||
<div flex-auto />
|
||||
<div flex items-center>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import type { SearchResult as SearchResultType } from '@/components/search/types'
|
||||
import type { AccountResult, HashTagResult, SearchResult as SearchResultType } from '@/components/search/types'
|
||||
import type { CommandScope, QueryResult, QueryResultItem } from '@/composables/command'
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -37,11 +37,23 @@ const searchResult = $computed<QueryResult>(() => {
|
|||
if (query.length === 0 || loading.value)
|
||||
return { length: 0, items: [], grouped: {} as any }
|
||||
|
||||
// TODO extract this scope
|
||||
// duplicate in SearchWidget.vue
|
||||
const hashtagList = hashtags.value.slice(0, 3)
|
||||
.map<SearchResultType>(hashtag => ({ type: 'hashtag', hashtag, to: `/tags/${hashtag.name}` }))
|
||||
.map<HashTagResult>(hashtag => ({
|
||||
type: 'hashtag',
|
||||
id: hashtag.id,
|
||||
hashtag,
|
||||
to: getTagRoute(hashtag.name),
|
||||
}))
|
||||
.map(toSearchQueryResultItem)
|
||||
const accountList = accounts.value
|
||||
.map<SearchResultType>(account => ({ type: 'account', account, to: `/@${account.acct}` }))
|
||||
.map<AccountResult>(account => ({
|
||||
type: 'account',
|
||||
id: account.id,
|
||||
account,
|
||||
to: getAccountRoute(account),
|
||||
}))
|
||||
.map(toSearchQueryResultItem)
|
||||
|
||||
const grouped: QueryResult['grouped'] = new Map()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { decode } from 'blurhash'
|
||||
|
||||
const { blurhash, src, srcset } = defineProps<{
|
||||
blurhash: string
|
||||
blurhash?: string | null | undefined
|
||||
src: string
|
||||
srcset?: string
|
||||
}>()
|
||||
|
@ -11,8 +11,13 @@ defineOptions({
|
|||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const placeholderSrc = ref<string>()
|
||||
const isLoaded = ref(false)
|
||||
const placeholderSrc = $computed(() => {
|
||||
if (!blurhash)
|
||||
return ''
|
||||
const pixels = decode(blurhash, 32, 32)
|
||||
return getDataUrlFromArr(pixels, 32, 32)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const img = document.createElement('img')
|
||||
|
@ -29,11 +34,6 @@ onMounted(() => {
|
|||
setTimeout(() => {
|
||||
isLoaded.value = true
|
||||
}, 3_000)
|
||||
|
||||
if (blurhash) {
|
||||
const pixels = decode(blurhash, 32, 32)
|
||||
placeholderSrc.value = getDataUrlFromArr(pixels, 32, 32)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'vue-advanced-cropper/dist/style.css'
|
|||
|
||||
export interface Props {
|
||||
/** Images to be cropped */
|
||||
modelValue?: File
|
||||
modelValue?: File | null
|
||||
/** Crop frame aspect ratio (width/height), default 1/1 */
|
||||
stencilAspectRatio?: number
|
||||
/** The ratio of the longest edge of the cut box to the length of the cut screen, default 0.9, not more than 1 */
|
||||
|
|
|
@ -3,7 +3,7 @@ import { fileOpen } from 'browser-fs-access'
|
|||
import type { FileWithHandle } from 'browser-fs-access'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue?: FileWithHandle
|
||||
modelValue?: FileWithHandle | null
|
||||
/** The image src before change */
|
||||
original?: string
|
||||
/** Allowed file types */
|
||||
|
|
|
@ -53,6 +53,7 @@ const handlePublishClose = () => {
|
|||
>
|
||||
<!-- This `w-0` style is used to avoid overflow problems in flex layouts,so don't remove it unless you know what you're doing -->
|
||||
<PublishWidget
|
||||
v-if="dialogDraftKey"
|
||||
:draft-key="dialogDraftKey" expanded flex-1 w-0
|
||||
@published="handlePublished"
|
||||
/>
|
||||
|
@ -65,7 +66,7 @@ const handlePublishClose = () => {
|
|||
<ModalMediaPreview v-if="isMediaPreviewOpen" @close="closeMediaPreview()" />
|
||||
</ModalDialog>
|
||||
<ModalDialog v-model="isEditHistoryDialogOpen" max-w-125>
|
||||
<StatusEditPreview :edit="statusEdit" />
|
||||
<StatusEditPreview v-if="statusEdit" :edit="statusEdit" />
|
||||
</ModalDialog>
|
||||
<ModalDialog v-model="isCommandPanelOpen" max-w-fit flex>
|
||||
<CommandPanel @close="closeCommandPanel()" />
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
// type used in <template>
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
import type { Notification, Paginator, WsEvents } from 'masto'
|
||||
// type used in <template>
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
import type { GroupedLikeNotifications } from '~/types'
|
||||
|
||||
import type { GroupedAccountLike, NotificationSlot } from '~/types'
|
||||
|
||||
const { paginator, stream } = defineProps<{
|
||||
|
@ -118,12 +124,12 @@ const { formatNumber } = useHumanReadableNumber()
|
|||
/>
|
||||
<NotificationGroupedLikes
|
||||
v-else-if="item.type === 'grouped-reblogs-and-favourites'"
|
||||
:group="item"
|
||||
:group="item as GroupedLikeNotifications"
|
||||
border="b base"
|
||||
/>
|
||||
<NotificationCard
|
||||
v-else
|
||||
:notification="item"
|
||||
:notification="item as Notification"
|
||||
hover:bg-active
|
||||
border="b base"
|
||||
/>
|
||||
|
|
|
@ -269,7 +269,7 @@ defineExpose({
|
|||
<PublishAttachment
|
||||
v-for="(att, idx) in draft.attachments" :key="att.id"
|
||||
:attachment="att"
|
||||
:dialog-labelled-by="dialogLabelledBy ?? (draft.editingStatus ? 'state-editing' : null)"
|
||||
:dialog-labelled-by="dialogLabelledBy ?? (draft.editingStatus ? 'state-editing' : undefined)"
|
||||
@remove="removeAttachment(idx)"
|
||||
@set-description="setDescription(att, $event)"
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import type { SearchResult } from './types'
|
||||
defineProps<{ result: SearchResult; active: boolean }>()
|
||||
|
||||
defineProps<{
|
||||
result: SearchResult
|
||||
active: boolean
|
||||
}>()
|
||||
|
||||
const onActivate = () => {
|
||||
(document.activeElement as HTMLElement).blur()
|
||||
|
@ -10,10 +14,10 @@ const onActivate = () => {
|
|||
<template>
|
||||
<CommonScrollIntoView as="RouterLink" :active="active" :to="result.to" py2 block px2 :aria-selected="active" :class="{ 'bg-active': active }" hover:bg-active @click="() => onActivate()">
|
||||
<SearchHashtagInfo v-if="result.type === 'hashtag'" :hashtag="result.hashtag" />
|
||||
<AccountInfo v-else-if="result.type === 'account'" :account="result.account" />
|
||||
<StatusCard v-else-if="result.type === 'status'" :status="result.status" :actions="false" :show-reply-to="false" />
|
||||
<div v-else-if="result.type === 'action'" text-center>
|
||||
<AccountInfo v-else-if="result.type === 'account' && result.account" :account="result.account" />
|
||||
<StatusCard v-else-if="result.type === 'status' && result.status" :status="result.status" :actions="false" :show-reply-to="false" />
|
||||
<!-- <div v-else-if="result.type === 'action'" text-center>
|
||||
{{ result.action!.label }}
|
||||
</div>
|
||||
</div> -->
|
||||
</CommonScrollIntoView>
|
||||
</template>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import type { AccountResult, HashTagResult, StatusResult } from './types'
|
||||
|
||||
const query = ref('')
|
||||
const { accounts, hashtags, loading, statuses } = useSearch(query)
|
||||
const index = ref(0)
|
||||
|
@ -13,9 +15,24 @@ const results = computed(() => {
|
|||
return []
|
||||
|
||||
const results = [
|
||||
...hashtags.value.slice(0, 3).map(hashtag => ({ type: 'hashtag', hashtag, to: getTagRoute(hashtag.name) })),
|
||||
...accounts.value.map(account => ({ type: 'account', account, to: getAccountRoute(account) })),
|
||||
...statuses.value.map(status => ({ type: 'status', status, to: getStatusRoute(status) })),
|
||||
...hashtags.value.slice(0, 3).map<HashTagResult>(hashtag => ({
|
||||
type: 'hashtag',
|
||||
id: hashtag.id,
|
||||
hashtag,
|
||||
to: getTagRoute(hashtag.name),
|
||||
})),
|
||||
...accounts.value.map<AccountResult>(account => ({
|
||||
type: 'account',
|
||||
id: account.id,
|
||||
account,
|
||||
to: getAccountRoute(account),
|
||||
})),
|
||||
...statuses.value.map<StatusResult>(status => ({
|
||||
type: 'status',
|
||||
id: status.id,
|
||||
status,
|
||||
to: getStatusRoute(status),
|
||||
})),
|
||||
|
||||
// Disable until search page is implemented
|
||||
// {
|
||||
|
@ -79,7 +96,12 @@ const activate = () => {
|
|||
{{ t('search.search_desc') }}
|
||||
</span>
|
||||
<template v-if="!loading">
|
||||
<SearchResult v-for="(result, i) in results" :key="result.to" :active="index === parseInt(i.toString())" :result="result" :tabindex="focused ? 0 : -1" />
|
||||
<SearchResult
|
||||
v-for="(result, i) in results" :key="result.id"
|
||||
:active="index === parseInt(i.toString())"
|
||||
:result="result"
|
||||
:tabindex="focused ? 0 : -1"
|
||||
/>
|
||||
</template>
|
||||
<div v-else>
|
||||
<SearchResultSkeleton />
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import type { Account, Status } from 'masto'
|
||||
import type { RouteLocation } from 'vue-router'
|
||||
|
||||
export interface SearchResult {
|
||||
type: 'account' | 'hashtag' | 'action' | 'status'
|
||||
to: string
|
||||
label?: string
|
||||
account?: Account
|
||||
status?: Status
|
||||
hashtag?: any
|
||||
action?: {
|
||||
label: string
|
||||
export type BuildResult<K extends keyof any, T> = {
|
||||
[P in K]: T
|
||||
} & {
|
||||
id: string
|
||||
type: K
|
||||
to: RouteLocation & {
|
||||
href: string
|
||||
}
|
||||
}
|
||||
export type HashTagResult = BuildResult<'hashtag', any>
|
||||
export type AccountResult = BuildResult<'account', Account>
|
||||
export type StatusResult = BuildResult<'status', Status>
|
||||
|
||||
export type SearchResult = HashTagResult | AccountResult | StatusResult
|
||||
|
|
|
@ -64,7 +64,7 @@ const reply = () => {
|
|||
color="text-green" hover="text-green" group-hover="bg-green/10"
|
||||
icon="i-ri:repeat-line"
|
||||
active-icon="i-ri:repeat-fill"
|
||||
:active="status.reblogged"
|
||||
:active="!!status.reblogged"
|
||||
:disabled="isLoading.reblogged"
|
||||
:command="command"
|
||||
@click="toggleReblog()"
|
||||
|
@ -88,7 +88,7 @@ const reply = () => {
|
|||
color="text-rose" hover="text-rose" group-hover="bg-rose/10"
|
||||
icon="i-ri:heart-3-line"
|
||||
active-icon="i-ri:heart-3-fill"
|
||||
:active="status.favourited"
|
||||
:active="!!status.favourited"
|
||||
:disabled="isLoading.favourited"
|
||||
:command="command"
|
||||
@click="toggleFavourite()"
|
||||
|
@ -111,7 +111,7 @@ const reply = () => {
|
|||
color="text-yellow" hover="text-yellow" group-hover="bg-yellow/10"
|
||||
icon="i-ri:bookmark-line"
|
||||
active-icon="i-ri:bookmark-fill"
|
||||
:active="status.bookmarked"
|
||||
:active="!!status.bookmarked"
|
||||
:disabled="isLoading.bookmarked"
|
||||
:command="command"
|
||||
@click="toggleBookmark()"
|
||||
|
|
|
@ -186,9 +186,8 @@ async function editStatus() {
|
|||
@click="toggleMute()"
|
||||
/>
|
||||
|
||||
<NuxtLink :to="status.url" external target="_blank">
|
||||
<NuxtLink v-if="status.url" :to="status.url" external target="_blank">
|
||||
<CommonDropdownItem
|
||||
v-if="status.url"
|
||||
:text="$t('menu.open_in_original_site')"
|
||||
icon="i-ri:arrow-right-up-line"
|
||||
:command="command"
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import type { Status } from 'masto'
|
||||
import type { Status, StatusEdit } from 'masto'
|
||||
|
||||
const { status, withAction = true } = defineProps<{
|
||||
status: Status
|
||||
status: Status | StatusEdit
|
||||
withAction?: boolean
|
||||
}>()
|
||||
|
||||
const { translation } = useTranslation(status)
|
||||
</script>
|
||||
|
||||
|
@ -15,7 +16,7 @@ const { translation } = useTranslation(status)
|
|||
class="line-compact"
|
||||
:content="status.content"
|
||||
:emojis="status.emojis"
|
||||
:lang="status.language"
|
||||
:lang="'language' in status && status.language"
|
||||
/>
|
||||
<div v-else />
|
||||
<template v-if="translation.visible">
|
||||
|
|
|
@ -113,7 +113,7 @@ const isDM = $computed(() => status.visibility === 'direct')
|
|||
</div>
|
||||
<div v-else />
|
||||
</slot>
|
||||
<StatusReplyingTo v-if="!directReply && !collapseReplyingTo" :status="status" :simplified="simplifyReplyingTo" :class="faded ? 'text-secondary-light' : ''" pt1 />
|
||||
<StatusReplyingTo v-if="!directReply && !collapseReplyingTo" :status="status" :simplified="!!simplifyReplyingTo" :class="faded ? 'text-secondary-light' : ''" pt1 />
|
||||
</div>
|
||||
<div flex gap-3 :class="{ 'text-secondary': faded }">
|
||||
<div relative>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import type { Status } from 'masto'
|
||||
import type { Status, StatusEdit } from 'masto'
|
||||
|
||||
const { status } = defineProps<{
|
||||
status: Status
|
||||
status: Status | StatusEdit
|
||||
fullSize?: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
|
|
@ -22,10 +22,7 @@ const { edit } = defineProps<{
|
|||
{{ edit.spoilerText }}
|
||||
</template>
|
||||
<StatusBody :status="edit" />
|
||||
<StatusMedia
|
||||
v-if="edit.mediaAttachments.length"
|
||||
:status="edit"
|
||||
/>
|
||||
<StatusMedia v-if="edit.mediaAttachments.length" :status="edit" />
|
||||
</StatusSpoiler>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -24,7 +24,6 @@ export const useImageGesture = (
|
|||
|
||||
const { set } = useSpring(motionProperties as Partial<PermissiveMotionProperties>)
|
||||
|
||||
// @ts-expect-error we need to fix types: just suppress it for now
|
||||
const handlers: Handlers = {
|
||||
onPinch({ offset: [d] }) {
|
||||
set({ scale: 1 + d / 200 })
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Status } from 'masto'
|
||||
import type { Status, StatusEdit } from 'masto'
|
||||
|
||||
export interface TranslationResponse {
|
||||
translatedText: string
|
||||
|
@ -24,15 +24,18 @@ export async function translateText(text: string, from?: string | null, to?: str
|
|||
return translatedText
|
||||
}
|
||||
|
||||
const translations = new WeakMap<Status, { visible: boolean; text: string }>()
|
||||
const translations = new WeakMap<Status | StatusEdit, { visible: boolean; text: string }>()
|
||||
|
||||
export function useTranslation(status: Status) {
|
||||
export function useTranslation(status: Status | StatusEdit) {
|
||||
if (!translations.has(status))
|
||||
translations.set(status, reactive({ visible: false, text: '' }))
|
||||
|
||||
const translation = translations.get(status)!
|
||||
|
||||
async function toggle() {
|
||||
if (!('language' in status))
|
||||
return
|
||||
|
||||
if (!translation.text)
|
||||
translation.text = await translateText(status.content, status.language)
|
||||
|
||||
|
|
13
html.d.ts
vendored
13
html.d.ts
vendored
|
@ -1,13 +0,0 @@
|
|||
// for UnoCSS attributify mode compact in Volar
|
||||
// refer: https://github.com/johnsoncodehk/volar/issues/1077#issuecomment-1145361472
|
||||
declare module '@vue/runtime-dom' {
|
||||
interface HTMLAttributes {
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
declare module '@vue/runtime-core' {
|
||||
interface AllowedComponentProps {
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
export {}
|
|
@ -1,5 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
import type { Status } from 'masto'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
|
||||
definePageMeta({
|
||||
|
@ -93,7 +92,7 @@ onReactivated(() => {
|
|||
</template>
|
||||
</div>
|
||||
|
||||
<StatusNotFound v-else :account="route.params.account" :status="id" />
|
||||
<StatusNotFound v-else :account="route.params.account as string" :status="id" />
|
||||
</template>
|
||||
|
||||
<StatusCardSkeleton v-else border="b base" />
|
||||
|
|
|
@ -16,7 +16,7 @@ if (account) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="account">
|
||||
<template v-if="paginator">
|
||||
<AccountPaginator :paginator="paginator" />
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -16,7 +16,7 @@ if (account) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="account">
|
||||
<template v-if="paginator">
|
||||
<AccountPaginator :paginator="paginator" />
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -32,8 +32,10 @@ onReactivated(() => {
|
|||
<span text-lg font-bold>#{{ tagName }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="typeof tag?.following === 'boolean'" #actions>
|
||||
<TagActionButton :tag="tag" @change="refresh()" />
|
||||
<template #actions>
|
||||
<template v-if="typeof tag?.following === 'boolean'">
|
||||
<TagActionButton :tag="tag" @change="refresh()" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<slot>
|
||||
|
|
Loading…
Reference in a new issue