diff --git a/.gitignore b/.gitignore index c4c69b12..72b234fa 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ dist .idea/ .vite-inspect .netlify/ +.eslintcache public/shiki public/emojis diff --git a/.vscode/settings.json b/.vscode/settings.json index 6c710a8c..f1855fcb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,27 +1,29 @@ { - "prettier.enable": false, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - }, - "files.associations": { - "*.css": "postcss" - }, - "editor.formatOnSave": false, "cSpell.words": [ "masto", "Nuxtodon", "unmute", "unstorage" ], - "i18n-ally.localesPaths": [ - "locales" - ], - "i18n-ally.keystyle": "nested", - "i18n-ally.sourceLanguage": "en-US", - "i18n-ally.preferredDelimiter": "_", - "i18n-ally.sortKeys": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "editor.formatOnSave": false, + "files.associations": { + "*.css": "postcss" + }, "i18n-ally.keysInUse": [ "time_ago_options.*", "visibility.*" - ] + ], + "i18n-ally.keystyle": "nested", + "i18n-ally.localesPaths": [ + "locales" + ], + "i18n-ally.preferredDelimiter": "_", + "i18n-ally.sortKeys": true, + "i18n-ally.sourceLanguage": "en-US", + "prettier.enable": false, + "volar.completion.preferredTagNameCase": "pascal", + "volar.completion.preferredAttrNameCase": "kebab" } diff --git a/app.vue b/app.vue index e7e62612..838c6019 100644 --- a/app.vue +++ b/app.vue @@ -12,4 +12,13 @@ const key = computed(() => getUniqueUserId(currentUser.value)) + + + + + + + + + diff --git a/components/account/AccountAvatar.vue b/components/account/AccountAvatar.vue index 44db7eb8..e12e658e 100644 --- a/components/account/AccountAvatar.vue +++ b/components/account/AccountAvatar.vue @@ -3,6 +3,7 @@ import type { Account } from 'masto' defineProps<{ account: Account + square?: boolean }>() const loaded = $ref(false) @@ -17,8 +18,8 @@ const error = $ref(false) :src="error ? 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' : account.avatar" :alt="$t('account.avatar_description', [account.username])" loading="lazy" - rounded-full - :class="loaded ? 'bg-base' : 'bg-gray:10'" + :class="(loaded ? 'bg-base' : 'bg-gray:10') + (square ? ' ' : ' rounded-full')" + :style="{ 'clip-path': square ? `url(#avatar-mask)` : 'none' }" v-bind="$attrs" @load="loaded = true" @error="error = true" diff --git a/components/account/AccountBigAvatar.vue b/components/account/AccountBigAvatar.vue index ecc0b3b0..afe705de 100644 --- a/components/account/AccountBigAvatar.vue +++ b/components/account/AccountBigAvatar.vue @@ -6,11 +6,12 @@ import type { Account } from 'masto' defineProps<{ account: Account + square?: boolean }>() diff --git a/components/account/AccountBigCard.vue b/components/account/AccountBigCard.vue index fb7f852e..caaf3ba0 100644 --- a/components/account/AccountBigCard.vue +++ b/components/account/AccountBigCard.vue @@ -32,13 +32,7 @@ defineOptions({
-
- -
+
diff --git a/components/account/AccountDisplayName.vue b/components/account/AccountDisplayName.vue new file mode 100644 index 00000000..a8b8befc --- /dev/null +++ b/components/account/AccountDisplayName.vue @@ -0,0 +1,15 @@ + + + diff --git a/components/account/AccountHeader.vue b/components/account/AccountHeader.vue index 17c21f36..23bbd821 100644 --- a/components/account/AccountHeader.vue +++ b/components/account/AccountHeader.vue @@ -1,6 +1,5 @@ + + diff --git a/components/common/CommonCheckbox.vue b/components/common/CommonCheckbox.vue index a7ebd035..11150bbc 100644 --- a/components/common/CommonCheckbox.vue +++ b/components/common/CommonCheckbox.vue @@ -11,11 +11,13 @@ const { modelValue } = defineModel<{ diff --git a/components/common/CommonCropImage.vue b/components/common/CommonCropImage.vue index adee86d6..a1eebba4 100644 --- a/components/common/CommonCropImage.vue +++ b/components/common/CommonCropImage.vue @@ -4,8 +4,6 @@ import { Cropper } from 'vue-advanced-cropper' import 'vue-advanced-cropper/dist/style.css' export interface Props { - /** Images to be cropped */ - modelValue?: File /** 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 */ @@ -16,12 +14,11 @@ const props = withDefaults(defineProps(), { stencilSizePercentage: 0.9, }) -const emit = defineEmits<{ - (event: 'update:modelValue', value: File): void +const { modelValue: file } = defineModel<{ + /** Images to be cropped */ + modelValue: File | null }>() -const vmFile = useVModel(props, 'modelValue', emit, { passive: true }) - const cropperDialog = ref(false) const cropper = ref>() @@ -40,7 +37,7 @@ const stencilSize = ({ boundaries }: { boundaries: Boundaries }) => { } } -watch(vmFile, (file, _, onCleanup) => { +watch(file, (file, _, onCleanup) => { let expired = false onCleanup(() => expired = true) @@ -59,12 +56,12 @@ watch(vmFile, (file, _, onCleanup) => { }) const cropImage = () => { - if (cropper.value && vmFile.value) { + if (cropper.value && file.value) { cropperFlag.value = true cropperDialog.value = false const { canvas } = cropper.value.getResult() canvas?.toBlob((blob) => { - vmFile.value = new File([blob as any], `cropped${vmFile.value?.name}` as string, { type: blob?.type }) + file.value = new File([blob as any], `cropped${file.value?.name}` as string, { type: blob?.type }) }, cropperImage.type) } } diff --git a/components/common/CommonInputImage.vue b/components/common/CommonInputImage.vue index 49a51db2..595d5786 100644 --- a/components/common/CommonInputImage.vue +++ b/components/common/CommonInputImage.vue @@ -3,7 +3,6 @@ import { fileOpen } from 'browser-fs-access' import type { FileWithHandle } from 'browser-fs-access' const props = withDefaults(defineProps<{ - modelValue?: FileWithHandle /** The image src before change */ original?: string /** Allowed file types */ @@ -19,12 +18,13 @@ const props = withDefaults(defineProps<{ allowedFileSize: 1024 * 1024 * 5, // 5 MB }) const emit = defineEmits<{ - (event: 'update:modelValue', value: FileWithHandle): void (event: 'pick', value: FileWithHandle): void (event: 'error', code: number, message: string): void }>() -const file = useVModel(props, 'modelValue', emit, { passive: true }) +const { modelValue: file } = defineModel<{ + modelValue: FileWithHandle | null +}>() const { t } = useI18n() diff --git a/components/common/CommonPaginator.vue b/components/common/CommonPaginator.vue index abb0f9f4..b739a4d5 100644 --- a/components/common/CommonPaginator.vue +++ b/components/common/CommonPaginator.vue @@ -2,7 +2,7 @@ // @ts-expect-error missing types import { DynamicScroller } from 'vue-virtual-scroller' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' -import type { Account, Paginator, WsEvents } from 'masto' +import type { Paginator, WsEvents } from 'masto' const { paginator, @@ -11,7 +11,6 @@ const { virtualScroller = false, eventType = 'update', preprocess, - isAccountTimeline, } = defineProps<{ paginator: Paginator keyProp?: string @@ -19,7 +18,6 @@ const { stream?: Promise eventType?: 'notification' | 'update' preprocess?: (items: any[]) => any[] - isAccountTimeline?: boolean }>() defineSlots<{ @@ -34,18 +32,9 @@ defineSlots<{ update: () => void } loading: {} + done: {} }>() -let account: Account | null = null - -const { params } = useRoute() - -if (isAccountTimeline) { - const handle = $(computedEager(() => params.account as string)) - - account = await fetchAccountByHandle(handle) -} - const { items, prevItems, update, state, endAnchor, error } = usePaginator(paginator, stream, eventType, preprocess) @@ -84,15 +73,11 @@ const { items, prevItems, update, state, endAnchor, error } = usePaginator(pagin -
- - -
+ +
{{ $t('common.error') }}: {{ error }}
diff --git a/components/common/CommonRadio.vue b/components/common/CommonRadio.vue index 05b0dd57..5f0c175a 100644 --- a/components/common/CommonRadio.vue +++ b/components/common/CommonRadio.vue @@ -12,9 +12,10 @@ const { modelValue } = defineModel<{ diff --git a/components/common/CommonRouteTabs.vue b/components/common/CommonRouteTabs.vue index a356e364..75ca06e6 100644 --- a/components/common/CommonRouteTabs.vue +++ b/components/common/CommonRouteTabs.vue @@ -43,9 +43,9 @@ useCommands(() => command exact-active-class="children:(text-secondary !border-primary !op100 !text-base)" @click="!preventScrollTop && $scrollToTop()" > - {{ option.display }} + {{ option.display }} -
+
{{ option.display }}
diff --git a/components/common/CommonTrendingCharts.vue b/components/common/CommonTrendingCharts.vue index 43fbf05b..b579dfee 100644 --- a/components/common/CommonTrendingCharts.vue +++ b/components/common/CommonTrendingCharts.vue @@ -4,8 +4,12 @@ import sparkline from '@fnando/sparkline' const { history, + width = 60, + height = 40, } = $defineProps<{ history?: History[] + width?: number + height?: number }>() const historyNum = $computed(() => { @@ -24,5 +28,5 @@ watch([$$(historyNum), $$(sparklineEl)], ([historyNum, sparklineEl]) => { diff --git a/components/help/HelpPreview.vue b/components/help/HelpPreview.vue index a664b500..b7d31232 100644 --- a/components/help/HelpPreview.vue +++ b/components/help/HelpPreview.vue @@ -32,7 +32,7 @@ const emit = defineEmits<{

diff --git a/components/main/MainContent.vue b/components/main/MainContent.vue index 4dfedc73..f67c7c2e 100644 --- a/components/main/MainContent.vue +++ b/components/main/MainContent.vue @@ -14,11 +14,12 @@ defineProps<{ pt="[env(safe-area-inset-top,0)]" border="b base" bg="[rgba(var(--c-bg-base-rgb),0.7)]" > -
+
@@ -37,6 +38,7 @@ defineProps<{
+ diff --git a/components/modal/ModalContainer.vue b/components/modal/ModalContainer.vue index 2ef41456..31ba7cb3 100644 --- a/components/modal/ModalContainer.vue +++ b/components/modal/ModalContainer.vue @@ -53,6 +53,7 @@ const handlePublishClose = () => { > @@ -65,7 +66,7 @@ const handlePublishClose = () => { - + diff --git a/components/modal/ModalDialog.vue b/components/modal/ModalDialog.vue index c20696d2..af51f5dc 100644 --- a/components/modal/ModalDialog.vue +++ b/components/modal/ModalDialog.vue @@ -2,9 +2,6 @@ import { useFocusTrap } from '@vueuse/integrations/useFocusTrap' export interface Props { - /** v-model dislog visibility */ - modelValue: boolean - /** * level of depth * @@ -48,11 +45,13 @@ const props = withDefaults(defineProps(), { const emit = defineEmits<{ /** v-model dialog visibility */ - (event: 'update:modelValue', value: boolean): void (event: 'close',): void }>() -const visible = useVModel(props, 'modelValue', emit, { passive: true }) +const { modelValue: visible } = defineModel<{ + /** v-model dislog visibility */ + modelValue: boolean +}>() const deactivated = useDeactivated() const route = useRoute() diff --git a/components/modal/ModalMediaPreviewCarousel.vue b/components/modal/ModalMediaPreviewCarousel.vue index edad636c..b2e76a5f 100644 --- a/components/modal/ModalMediaPreviewCarousel.vue +++ b/components/modal/ModalMediaPreviewCarousel.vue @@ -3,19 +3,20 @@ import { SwipeDirection } from '@vueuse/core' import { useReducedMotion } from '@vueuse/motion' import type { Attachment } from 'masto' -const props = withDefaults(defineProps<{ media: Attachment[]; threshold?: number; modelValue: number }>(), { - media: [] as any, - threshold: 20, - modelValue: 0, -}) +const { media = [], threshold = 20 } = defineProps<{ + media?: Attachment[] + threshold?: number +}>() const emit = defineEmits<{ - (e: 'update:modelValue', v: boolean): void (event: 'close'): void }>() +const { modelValue } = defineModel<{ + modelValue: number +}>() + const target = ref() -const index = useVModel(props, 'modelValue', emit) const animateTimeout = useTimeout(10) const reduceMotion = useReducedMotion() @@ -28,15 +29,15 @@ const { isSwiping, lengthX, lengthY, direction } = useSwipe(target, { passive: false, onSwipeEnd(e, direction) { // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (direction === SwipeDirection.RIGHT && Math.abs(distanceX.value) > props.threshold) - index.value = Math.max(0, index.value - 1) + if (direction === SwipeDirection.RIGHT && Math.abs(distanceX.value) > threshold) + modelValue.value = Math.max(0, modelValue.value - 1) // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (direction === SwipeDirection.LEFT && Math.abs(distanceX.value) > props.threshold) - index.value = Math.min(props.media.length - 1, index.value + 1) + if (direction === SwipeDirection.LEFT && Math.abs(distanceX.value) > threshold) + modelValue.value = Math.min(media.length - 1, modelValue.value + 1) // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (direction === SwipeDirection.UP && Math.abs(distanceY.value) > props.threshold) + if (direction === SwipeDirection.UP && Math.abs(distanceY.value) > threshold) emit('close') }, }) @@ -46,9 +47,9 @@ const distanceX = computed(() => { return 0 if (!isSwiping.value || (direction.value !== SwipeDirection.LEFT && direction.value !== SwipeDirection.RIGHT)) - return index.value * 100 * -1 + return modelValue.value * 100 * -1 - return (lengthX.value / width.value) * 100 * -1 + (index.value * 100) * -1 + return (lengthX.value / width.value) * 100 * -1 + (modelValue.value * 100) * -1 }) const distanceY = computed(() => { diff --git a/components/nav/NavBottom.vue b/components/nav/NavBottom.vue index 24b4543a..8bf68cf5 100644 --- a/components/nav/NavBottom.vue +++ b/components/nav/NavBottom.vue @@ -38,12 +38,12 @@ const moreMenuVisible = ref(false)
- + diff --git a/components/nav/NavBottomMoreMenu.vue b/components/nav/NavBottomMoreMenu.vue index 6dd117c9..45799cbc 100644 --- a/components/nav/NavBottomMoreMenu.vue +++ b/components/nav/NavBottomMoreMenu.vue @@ -1,24 +1,20 @@ diff --git a/components/nav/NavSideItem.vue b/components/nav/NavSideItem.vue index ed6a0a93..c5eb9a03 100644 --- a/components/nav/NavSideItem.vue +++ b/components/nav/NavSideItem.vue @@ -30,13 +30,11 @@ useCommand({ let activeClass = $ref('text-primary') onMastoInit(async () => { - if (!props.userOnly) { - // TODO: force NuxtLink to reevaluate, we now we are in this route though, so we should force it to active - // we don't have currentServer defined until later - activeClass = '' - await nextTick() - activeClass = 'text-primary' - } + // TODO: force NuxtLink to reevaluate, we now we are in this route though, so we should force it to active + // we don't have currentServer defined until later + activeClass = '' + await nextTick() + activeClass = 'text-primary' }) // Optimize rendering for the common case of being logged in, only show visual feedback for disabled user-only items @@ -58,11 +56,11 @@ const noUserVisual = computed(() => isMastoInitialised.value && props.userOnly &
diff --git a/components/nav/NavTitle.vue b/components/nav/NavTitle.vue index f4f9524c..6a75f50d 100644 --- a/components/nav/NavTitle.vue +++ b/components/nav/NavTitle.vue @@ -6,19 +6,27 @@ const { env } = buildInfo diff --git a/components/nav/NavUser.vue b/components/nav/NavUser.vue index 76353153..7f31797f 100644 --- a/components/nav/NavUser.vue +++ b/components/nav/NavUser.vue @@ -8,6 +8,7 @@ h-8 w-8 :draggable="false" + square />
G @@ -18,7 +19,7 @@ - diff --git a/components/notification/NotificationCard.vue b/components/notification/NotificationCard.vue index 00f3e815..1c5d540e 100644 --- a/components/notification/NotificationCard.vue +++ b/components/notification/NotificationCard.vue @@ -18,11 +18,7 @@ const { notification } = defineProps<{ :lang="notification.status?.language ?? undefined" >
- + {{ $t('notification.followed_you') }} @@ -36,10 +32,9 @@ const { notification } = defineProps<{ diff --git a/components/notification/NotificationSubscribePushNotificationError.vue b/components/notification/NotificationSubscribePushNotificationError.vue index e7118fe3..367c2b99 100644 --- a/components/notification/NotificationSubscribePushNotificationError.vue +++ b/components/notification/NotificationSubscribePushNotificationError.vue @@ -22,13 +22,13 @@ const { modelValue } = defineModel<{
- +