fix: type errors (#802)

This commit is contained in:
三咲智子 Kevin Deng 2023-01-06 00:48:20 +08:00 committed by GitHub
parent 272fb4a13d
commit acdb94de62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 113 additions and 74 deletions

View file

@ -1,4 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
// type used in <template>
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import type { Account } from 'masto' import type { Account } from 'masto'
defineProps<{ defineProps<{
@ -14,8 +16,9 @@ defineProps<{
</div> </div>
<div flex> <div flex>
<NuxtLink :to="getAccountRoute(account.moved as any)"> <!-- type error of masto.js -->
<AccountInfo :account="account.moved" /> <NuxtLink :to="getAccountRoute(account.moved as unknown as Account)">
<AccountInfo :account="account.moved as unknown as Account" />
</NuxtLink> </NuxtLink>
<div flex-auto /> <div flex-auto />
<div flex items-center> <div flex items-center>

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <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' import type { CommandScope, QueryResult, QueryResultItem } from '@/composables/command'
const emit = defineEmits<{ const emit = defineEmits<{
@ -37,11 +37,23 @@ const searchResult = $computed<QueryResult>(() => {
if (query.length === 0 || loading.value) if (query.length === 0 || loading.value)
return { length: 0, items: [], grouped: {} as any } return { length: 0, items: [], grouped: {} as any }
// TODO extract this scope
// duplicate in SearchWidget.vue
const hashtagList = hashtags.value.slice(0, 3) 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) .map(toSearchQueryResultItem)
const accountList = accounts.value 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) .map(toSearchQueryResultItem)
const grouped: QueryResult['grouped'] = new Map() const grouped: QueryResult['grouped'] = new Map()

View file

@ -2,7 +2,7 @@
import { decode } from 'blurhash' import { decode } from 'blurhash'
const { blurhash, src, srcset } = defineProps<{ const { blurhash, src, srcset } = defineProps<{
blurhash: string blurhash?: string | null | undefined
src: string src: string
srcset?: string srcset?: string
}>() }>()
@ -11,8 +11,13 @@ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}) })
const placeholderSrc = ref<string>()
const isLoaded = ref(false) const isLoaded = ref(false)
const placeholderSrc = $computed(() => {
if (!blurhash)
return ''
const pixels = decode(blurhash, 32, 32)
return getDataUrlFromArr(pixels, 32, 32)
})
onMounted(() => { onMounted(() => {
const img = document.createElement('img') const img = document.createElement('img')
@ -29,11 +34,6 @@ onMounted(() => {
setTimeout(() => { setTimeout(() => {
isLoaded.value = true isLoaded.value = true
}, 3_000) }, 3_000)
if (blurhash) {
const pixels = decode(blurhash, 32, 32)
placeholderSrc.value = getDataUrlFromArr(pixels, 32, 32)
}
}) })
</script> </script>

View file

@ -5,7 +5,7 @@ import 'vue-advanced-cropper/dist/style.css'
export interface Props { export interface Props {
/** Images to be cropped */ /** Images to be cropped */
modelValue?: File modelValue?: File | null
/** Crop frame aspect ratio (width/height), default 1/1 */ /** Crop frame aspect ratio (width/height), default 1/1 */
stencilAspectRatio?: number 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 */ /** The ratio of the longest edge of the cut box to the length of the cut screen, default 0.9, not more than 1 */

View file

@ -3,7 +3,7 @@ import { fileOpen } from 'browser-fs-access'
import type { FileWithHandle } from 'browser-fs-access' import type { FileWithHandle } from 'browser-fs-access'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
modelValue?: FileWithHandle modelValue?: FileWithHandle | null
/** The image src before change */ /** The image src before change */
original?: string original?: string
/** Allowed file types */ /** Allowed file types */

View file

@ -53,6 +53,7 @@ const handlePublishClose = () => {
> >
<!-- This `w-0` style is used to avoid overflow problems in flex layoutsso don't remove it unless you know what you're doing --> <!-- This `w-0` style is used to avoid overflow problems in flex layoutsso don't remove it unless you know what you're doing -->
<PublishWidget <PublishWidget
v-if="dialogDraftKey"
:draft-key="dialogDraftKey" expanded flex-1 w-0 :draft-key="dialogDraftKey" expanded flex-1 w-0
@published="handlePublished" @published="handlePublished"
/> />
@ -65,7 +66,7 @@ const handlePublishClose = () => {
<ModalMediaPreview v-if="isMediaPreviewOpen" @close="closeMediaPreview()" /> <ModalMediaPreview v-if="isMediaPreviewOpen" @close="closeMediaPreview()" />
</ModalDialog> </ModalDialog>
<ModalDialog v-model="isEditHistoryDialogOpen" max-w-125> <ModalDialog v-model="isEditHistoryDialogOpen" max-w-125>
<StatusEditPreview :edit="statusEdit" /> <StatusEditPreview v-if="statusEdit" :edit="statusEdit" />
</ModalDialog> </ModalDialog>
<ModalDialog v-model="isCommandPanelOpen" max-w-fit flex> <ModalDialog v-model="isCommandPanelOpen" max-w-fit flex>
<CommandPanel @close="closeCommandPanel()" /> <CommandPanel @close="closeCommandPanel()" />

View file

@ -1,5 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
// type used in <template>
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import type { Notification, Paginator, WsEvents } from 'masto' 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' import type { GroupedAccountLike, NotificationSlot } from '~/types'
const { paginator, stream } = defineProps<{ const { paginator, stream } = defineProps<{
@ -118,12 +124,12 @@ const { formatNumber } = useHumanReadableNumber()
/> />
<NotificationGroupedLikes <NotificationGroupedLikes
v-else-if="item.type === 'grouped-reblogs-and-favourites'" v-else-if="item.type === 'grouped-reblogs-and-favourites'"
:group="item" :group="item as GroupedLikeNotifications"
border="b base" border="b base"
/> />
<NotificationCard <NotificationCard
v-else v-else
:notification="item" :notification="item as Notification"
hover:bg-active hover:bg-active
border="b base" border="b base"
/> />

View file

@ -269,7 +269,7 @@ defineExpose({
<PublishAttachment <PublishAttachment
v-for="(att, idx) in draft.attachments" :key="att.id" v-for="(att, idx) in draft.attachments" :key="att.id"
:attachment="att" :attachment="att"
:dialog-labelled-by="dialogLabelledBy ?? (draft.editingStatus ? 'state-editing' : null)" :dialog-labelled-by="dialogLabelledBy ?? (draft.editingStatus ? 'state-editing' : undefined)"
@remove="removeAttachment(idx)" @remove="removeAttachment(idx)"
@set-description="setDescription(att, $event)" @set-description="setDescription(att, $event)"
/> />

View file

@ -1,6 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SearchResult } from './types' import type { SearchResult } from './types'
defineProps<{ result: SearchResult; active: boolean }>()
defineProps<{
result: SearchResult
active: boolean
}>()
const onActivate = () => { const onActivate = () => {
(document.activeElement as HTMLElement).blur() (document.activeElement as HTMLElement).blur()
@ -10,10 +14,10 @@ const onActivate = () => {
<template> <template>
<CommonScrollIntoView as="RouterLink" :active="active" :to="result.to" py2 block px2 :aria-selected="active" :class="{ 'bg-active': active }" hover:bg-active @click="() => onActivate()"> <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" /> <SearchHashtagInfo v-if="result.type === 'hashtag'" :hashtag="result.hashtag" />
<AccountInfo v-else-if="result.type === 'account'" :account="result.account" /> <AccountInfo v-else-if="result.type === 'account' && result.account" :account="result.account" />
<StatusCard v-else-if="result.type === 'status'" :status="result.status" :actions="false" :show-reply-to="false" /> <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> <!-- <div v-else-if="result.type === 'action'" text-center>
{{ result.action!.label }} {{ result.action!.label }}
</div> </div> -->
</CommonScrollIntoView> </CommonScrollIntoView>
</template> </template>

View file

@ -1,4 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { AccountResult, HashTagResult, StatusResult } from './types'
const query = ref('') const query = ref('')
const { accounts, hashtags, loading, statuses } = useSearch(query) const { accounts, hashtags, loading, statuses } = useSearch(query)
const index = ref(0) const index = ref(0)
@ -13,9 +15,24 @@ const results = computed(() => {
return [] return []
const results = [ const results = [
...hashtags.value.slice(0, 3).map(hashtag => ({ type: 'hashtag', hashtag, to: getTagRoute(hashtag.name) })), ...hashtags.value.slice(0, 3).map<HashTagResult>(hashtag => ({
...accounts.value.map(account => ({ type: 'account', account, to: getAccountRoute(account) })), type: 'hashtag',
...statuses.value.map(status => ({ type: 'status', status, to: getStatusRoute(status) })), 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 // Disable until search page is implemented
// { // {
@ -79,7 +96,12 @@ const activate = () => {
{{ t('search.search_desc') }} {{ t('search.search_desc') }}
</span> </span>
<template v-if="!loading"> <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> </template>
<div v-else> <div v-else>
<SearchResultSkeleton /> <SearchResultSkeleton />

View file

@ -1,13 +1,17 @@
import type { Account, Status } from 'masto' import type { Account, Status } from 'masto'
import type { RouteLocation } from 'vue-router'
export interface SearchResult { export type BuildResult<K extends keyof any, T> = {
type: 'account' | 'hashtag' | 'action' | 'status' [P in K]: T
to: string } & {
label?: string id: string
account?: Account type: K
status?: Status to: RouteLocation & {
hashtag?: any href: string
action?: {
label: 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

View file

@ -64,7 +64,7 @@ const reply = () => {
color="text-green" hover="text-green" group-hover="bg-green/10" color="text-green" hover="text-green" group-hover="bg-green/10"
icon="i-ri:repeat-line" icon="i-ri:repeat-line"
active-icon="i-ri:repeat-fill" active-icon="i-ri:repeat-fill"
:active="status.reblogged" :active="!!status.reblogged"
:disabled="isLoading.reblogged" :disabled="isLoading.reblogged"
:command="command" :command="command"
@click="toggleReblog()" @click="toggleReblog()"
@ -88,7 +88,7 @@ const reply = () => {
color="text-rose" hover="text-rose" group-hover="bg-rose/10" color="text-rose" hover="text-rose" group-hover="bg-rose/10"
icon="i-ri:heart-3-line" icon="i-ri:heart-3-line"
active-icon="i-ri:heart-3-fill" active-icon="i-ri:heart-3-fill"
:active="status.favourited" :active="!!status.favourited"
:disabled="isLoading.favourited" :disabled="isLoading.favourited"
:command="command" :command="command"
@click="toggleFavourite()" @click="toggleFavourite()"
@ -111,7 +111,7 @@ const reply = () => {
color="text-yellow" hover="text-yellow" group-hover="bg-yellow/10" color="text-yellow" hover="text-yellow" group-hover="bg-yellow/10"
icon="i-ri:bookmark-line" icon="i-ri:bookmark-line"
active-icon="i-ri:bookmark-fill" active-icon="i-ri:bookmark-fill"
:active="status.bookmarked" :active="!!status.bookmarked"
:disabled="isLoading.bookmarked" :disabled="isLoading.bookmarked"
:command="command" :command="command"
@click="toggleBookmark()" @click="toggleBookmark()"

View file

@ -186,9 +186,8 @@ async function editStatus() {
@click="toggleMute()" @click="toggleMute()"
/> />
<NuxtLink :to="status.url" external target="_blank"> <NuxtLink v-if="status.url" :to="status.url" external target="_blank">
<CommonDropdownItem <CommonDropdownItem
v-if="status.url"
:text="$t('menu.open_in_original_site')" :text="$t('menu.open_in_original_site')"
icon="i-ri:arrow-right-up-line" icon="i-ri:arrow-right-up-line"
:command="command" :command="command"

View file

@ -1,10 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Status } from 'masto' import type { Status, StatusEdit } from 'masto'
const { status, withAction = true } = defineProps<{ const { status, withAction = true } = defineProps<{
status: Status status: Status | StatusEdit
withAction?: boolean withAction?: boolean
}>() }>()
const { translation } = useTranslation(status) const { translation } = useTranslation(status)
</script> </script>
@ -15,7 +16,7 @@ const { translation } = useTranslation(status)
class="line-compact" class="line-compact"
:content="status.content" :content="status.content"
:emojis="status.emojis" :emojis="status.emojis"
:lang="status.language" :lang="'language' in status && status.language"
/> />
<div v-else /> <div v-else />
<template v-if="translation.visible"> <template v-if="translation.visible">

View file

@ -113,7 +113,7 @@ const isDM = $computed(() => status.visibility === 'direct')
</div> </div>
<div v-else /> <div v-else />
</slot> </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>
<div flex gap-3 :class="{ 'text-secondary': faded }"> <div flex gap-3 :class="{ 'text-secondary': faded }">
<div relative> <div relative>

View file

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Status } from 'masto' import type { Status, StatusEdit } from 'masto'
const { status } = defineProps<{ const { status } = defineProps<{
status: Status status: Status | StatusEdit
fullSize?: boolean fullSize?: boolean
}>() }>()
</script> </script>

View file

@ -22,10 +22,7 @@ const { edit } = defineProps<{
{{ edit.spoilerText }} {{ edit.spoilerText }}
</template> </template>
<StatusBody :status="edit" /> <StatusBody :status="edit" />
<StatusMedia <StatusMedia v-if="edit.mediaAttachments.length" :status="edit" />
v-if="edit.mediaAttachments.length"
:status="edit"
/>
</StatusSpoiler> </StatusSpoiler>
</div> </div>
</template> </template>

View file

@ -24,7 +24,6 @@ export const useImageGesture = (
const { set } = useSpring(motionProperties as Partial<PermissiveMotionProperties>) const { set } = useSpring(motionProperties as Partial<PermissiveMotionProperties>)
// @ts-expect-error we need to fix types: just suppress it for now
const handlers: Handlers = { const handlers: Handlers = {
onPinch({ offset: [d] }) { onPinch({ offset: [d] }) {
set({ scale: 1 + d / 200 }) set({ scale: 1 + d / 200 })

View file

@ -1,4 +1,4 @@
import type { Status } from 'masto' import type { Status, StatusEdit } from 'masto'
export interface TranslationResponse { export interface TranslationResponse {
translatedText: string translatedText: string
@ -24,15 +24,18 @@ export async function translateText(text: string, from?: string | null, to?: str
return translatedText 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)) if (!translations.has(status))
translations.set(status, reactive({ visible: false, text: '' })) translations.set(status, reactive({ visible: false, text: '' }))
const translation = translations.get(status)! const translation = translations.get(status)!
async function toggle() { async function toggle() {
if (!('language' in status))
return
if (!translation.text) if (!translation.text)
translation.text = await translateText(status.content, status.language) translation.text = await translateText(status.content, status.language)

13
html.d.ts vendored
View file

@ -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 {}

View file

@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Status } from 'masto'
import type { ComponentPublicInstance } from 'vue' import type { ComponentPublicInstance } from 'vue'
definePageMeta({ definePageMeta({
@ -93,7 +92,7 @@ onReactivated(() => {
</template> </template>
</div> </div>
<StatusNotFound v-else :account="route.params.account" :status="id" /> <StatusNotFound v-else :account="route.params.account as string" :status="id" />
</template> </template>
<StatusCardSkeleton v-else border="b base" /> <StatusCardSkeleton v-else border="b base" />

View file

@ -16,7 +16,7 @@ if (account) {
</script> </script>
<template> <template>
<template v-if="account"> <template v-if="paginator">
<AccountPaginator :paginator="paginator" /> <AccountPaginator :paginator="paginator" />
</template> </template>
</template> </template>

View file

@ -16,7 +16,7 @@ if (account) {
</script> </script>
<template> <template>
<template v-if="account"> <template v-if="paginator">
<AccountPaginator :paginator="paginator" /> <AccountPaginator :paginator="paginator" />
</template> </template>
</template> </template>

View file

@ -32,9 +32,11 @@ onReactivated(() => {
<span text-lg font-bold>#{{ tagName }}</span> <span text-lg font-bold>#{{ tagName }}</span>
</template> </template>
<template v-if="typeof tag?.following === 'boolean'" #actions> <template #actions>
<template v-if="typeof tag?.following === 'boolean'">
<TagActionButton :tag="tag" @change="refresh()" /> <TagActionButton :tag="tag" @change="refresh()" />
</template> </template>
</template>
<slot> <slot>
<TimelinePaginator v-bind="{ paginator, stream }" context="public" /> <TimelinePaginator v-bind="{ paginator, stream }" context="public" />