Compare commits
3 Commits
975766302a
...
e4111dc06e
Author | SHA1 | Date | |
---|---|---|---|
e4111dc06e | |||
3e7f259834 | |||
97449bdc1e |
@ -3,10 +3,12 @@
|
|||||||
<v-card-text>
|
<v-card-text>
|
||||||
<div class="mb-3 flex flex-row gap-4">
|
<div class="mb-3 flex flex-row gap-4">
|
||||||
<nuxt-link :to="`/users/${post.publisher?.name}`">
|
<nuxt-link :to="`/users/${post.publisher?.name}`">
|
||||||
<v-avatar :image="post.publisher?.avatar" />
|
<v-avatar :image="getAttachmentUrl(post.publisher?.avatar)" icon="mdi-account-circle" />
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span>{{ post.publisher?.nick }} <span class="text-xs">@{{ post.publisher?.name }}</span></span>
|
<span
|
||||||
|
>{{ post.publisher?.nick }} <span class="text-xs">@{{ post.publisher?.name }}</span></span
|
||||||
|
>
|
||||||
<span v-if="post.body?.title" class="text-md">{{ post.body?.title }}</span>
|
<span v-if="post.body?.title" class="text-md">{{ post.body?.title }}</span>
|
||||||
<span v-if="post.body?.description" class="text-sm">{{ post.body?.description }}</span>
|
<span v-if="post.body?.description" class="text-sm">{{ post.body?.description }}</span>
|
||||||
<span v-if="!post.body?.title && !post.body?.description" class="text-sm">
|
<span v-if="!post.body?.title && !post.body?.description" class="text-sm">
|
||||||
@ -29,7 +31,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article v-if="post.type == 'story' || props.forceShowContent" class="text-base prose max-w-none">
|
<article v-if="(post.type == 'story' || props.forceShowContent) && post.body?.content" class="text-base prose max-w-none">
|
||||||
<m-d-c :value="post.body?.content"></m-d-c>
|
<m-d-c :value="post.body?.content"></m-d-c>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
@ -41,24 +43,19 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<div class="text-sm flex flex-col">
|
<div class="text-sm flex flex-col">
|
||||||
<span class="flex flex-row gap-1">
|
<span class="flex flex-row gap-1">
|
||||||
<span>
|
<span> {{ post.metric.reply_count }} {{ post.metric.reply_count > 1 ? "replies" : "reply" }}, </span>
|
||||||
{{ post.metric.reply_count }} {{ post.metric.reply_count > 1 ? "replies" : "reply" }},
|
<span>
|
||||||
</span>
|
{{ post.metric.reaction_count }} {{ post.metric.reaction_count > 1 ? "reactions" : "reaction" }}
|
||||||
<span>
|
</span>
|
||||||
{{ post.metric.reaction_count }} {{ post.metric.reaction_count > 1 ? "reactions" : "reaction" }}
|
</span>
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span>
|
<span>
|
||||||
{{ post.type.startsWith("a") ? "An" : "A" }} {{ post.type }} posted on
|
{{ post.type.startsWith("a") ? "An" : "A" }} {{ post.type }} posted on
|
||||||
{{ new Date(post.published_at).toLocaleString() }}
|
{{ new Date(post.published_at).toLocaleString() }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-if="post.tags?.length > 0" class="text-xs text-grey flex flex-row gap-1 mt-3">
|
||||||
v-if="post.tags?.length > 0"
|
|
||||||
class="text-xs text-grey flex flex-row gap-1 mt-3"
|
|
||||||
>
|
|
||||||
<nuxt-link
|
<nuxt-link
|
||||||
v-for="tag in post.tags"
|
v-for="tag in post.tags"
|
||||||
:to="`/posts/tags/${tag.alias}`"
|
:to="`/posts/tags/${tag.alias}`"
|
||||||
@ -73,10 +70,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps<{ post: any, forceShowContent?: boolean, noClickableAttachment?: boolean }>()
|
const props = defineProps<{ post: any; forceShowContent?: boolean; noClickableAttachment?: boolean }>()
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const url = computed(() => props.post.alias ? `/posts/${props.post.area_alias}/${props.post.alias}` : `/posts/${props.post.id}`)
|
const url = computed(() =>
|
||||||
|
props.post.alias ? `/posts/${props.post.area_alias}/${props.post.alias}` : `/posts/${props.post.id}`,
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
@ -5,13 +5,11 @@
|
|||||||
</v-alert>
|
</v-alert>
|
||||||
</v-expand-transition>
|
</v-expand-transition>
|
||||||
|
|
||||||
<v-data-table-server
|
<v-data-table
|
||||||
density="compact"
|
density="compact"
|
||||||
:headers="dataDefinitions.stickers"
|
:headers="dataDefinitions.stickers"
|
||||||
:items="stickers"
|
:items="stickers"
|
||||||
:items-length="pagination.stickers.total"
|
|
||||||
:loading="reverting.stickers"
|
:loading="reverting.stickers"
|
||||||
v-model:items-per-page="pagination.stickers.pageSize"
|
|
||||||
@update:options="readStickers"
|
@update:options="readStickers"
|
||||||
item-value="id"
|
item-value="id"
|
||||||
>
|
>
|
||||||
@ -74,23 +72,24 @@
|
|||||||
<template v-slot:default="{ isActive }">
|
<template v-slot:default="{ isActive }">
|
||||||
<v-card :title="`Delete sticker #${item.id}?`">
|
<v-card :title="`Delete sticker #${item.id}?`">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
This action will delete this sticker, all content used it will no longer show your sticker.
|
This action will delete this sticker, all content used it will no longer show your sticker. But the
|
||||||
But the attachment will still exists.
|
attachment will still exists.
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
|
||||||
<v-btn
|
<v-btn text="Cancel" color="grey" @click="isActive.value = false"></v-btn>
|
||||||
text="Cancel"
|
|
||||||
color="grey"
|
|
||||||
@click="isActive.value = false"
|
|
||||||
></v-btn>
|
|
||||||
|
|
||||||
<v-btn
|
<v-btn
|
||||||
text="Delete"
|
text="Delete"
|
||||||
color="error"
|
color="error"
|
||||||
@click="() => { deleteSticker(item); isActive.value = false }"
|
@click="
|
||||||
|
() => {
|
||||||
|
deleteSticker(item)
|
||||||
|
isActive.value = false
|
||||||
|
}
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
@ -99,7 +98,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</v-data-table-server>
|
</v-data-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -108,7 +107,7 @@ import { solarFetch } from "~/utils/request"
|
|||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const props = defineProps<{ packId: number, packPrefix?: string }>()
|
const props = defineProps<{ packId: number; packPrefix?: string }>()
|
||||||
|
|
||||||
const error = ref<null | string>(null)
|
const error = ref<null | string>(null)
|
||||||
|
|
||||||
@ -125,34 +124,20 @@ const dataDefinitions: { [id: string]: any[] } = {
|
|||||||
const stickers = ref<any>([])
|
const stickers = ref<any>([])
|
||||||
|
|
||||||
const reverting = reactive({ stickers: false })
|
const reverting = reactive({ stickers: false })
|
||||||
const pagination = reactive({
|
|
||||||
stickers: { page: 1, pageSize: 5, total: 0 },
|
|
||||||
})
|
|
||||||
|
|
||||||
async function readStickers({ page, itemsPerPage }: { page?: number; itemsPerPage?: number }) {
|
|
||||||
if (itemsPerPage) pagination.stickers.pageSize = itemsPerPage
|
|
||||||
if (page) pagination.stickers.page = page
|
|
||||||
|
|
||||||
|
async function readStickers() {
|
||||||
reverting.stickers = true
|
reverting.stickers = true
|
||||||
const res = await solarFetch(
|
const res = await solarFetch("/cgi/uc/stickers/packs/" + props.packId)
|
||||||
"/cgi/uc/stickers?" +
|
|
||||||
new URLSearchParams({
|
|
||||||
pack: props.packId.toString(),
|
|
||||||
take: pagination.stickers.pageSize.toString(),
|
|
||||||
offset: ((pagination.stickers.page - 1) * pagination.stickers.pageSize).toString(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
error.value = await res.text()
|
error.value = await res.text()
|
||||||
} else {
|
} else {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
stickers.value = data["data"]
|
stickers.value = data["stickers"]
|
||||||
pagination.stickers.total = data["count"]
|
|
||||||
}
|
}
|
||||||
reverting.stickers = false
|
reverting.stickers = false
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => readStickers({}))
|
onMounted(() => readStickers())
|
||||||
|
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
|
|
||||||
@ -165,7 +150,7 @@ async function deleteSticker(item: any) {
|
|||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
error.value = await res.text()
|
error.value = await res.text()
|
||||||
} else {
|
} else {
|
||||||
await readStickers({})
|
await readStickers()
|
||||||
}
|
}
|
||||||
|
|
||||||
submitting.value = false
|
submitting.value = false
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app-bar flat color="primary" scroll-behavior="hide" scroll-threshold="800">
|
<v-app-bar flat color="primary" scroll-behavior="hide" scroll-threshold="800">
|
||||||
<v-container fluid class="mx-auto d-flex align-center justify-center px-8">
|
<v-container fluid class="mx-auto d-flex align-center justify-center px-8">
|
||||||
<v-tooltip>
|
<v-app-bar-nav-icon @click="openDrawer = !openDrawer" />
|
||||||
<template #activator="{ props }">
|
|
||||||
<div @click="openDrawer = !openDrawer" v-bind="props" class="cursor-pointer">
|
|
||||||
<v-img class="me-4 ms-1" width="32" height="32" alt="Logo" :src="Logo" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
Open / close drawer
|
|
||||||
</v-tooltip>
|
|
||||||
|
|
||||||
<nuxt-link to="/dev" exact>
|
<nuxt-link to="/creator" exact>
|
||||||
<h2 class="mt-1">Creator Hub</h2>
|
<h2>Creator Hub</h2>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
@ -45,12 +38,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Logo from "../assets/logo-w-shadow.png"
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const openDrawer = ref(false)
|
const openDrawer = ref(false)
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
titleTemplate: "%s | Solsynth Creator Hub"
|
titleTemplate: "%s | Solsynth Creator Hub",
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,41 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app-bar flat color="primary">
|
<v-app-bar app flat color="surface" class="app-bar-blur">
|
||||||
<v-container fluid class="mx-auto d-flex align-center justify-center px-8">
|
<v-container fluid class="mx-auto d-flex align-center justify-center pr-8">
|
||||||
<v-tooltip>
|
<v-app-bar-nav-icon @click="openDrawer = !openDrawer" />
|
||||||
<template #activator="{ props }">
|
|
||||||
<div @click="openDrawer = !openDrawer" v-bind="props" class="cursor-pointer">
|
|
||||||
<v-img class="me-4 ms-1" width="32" height="32" alt="Logo" :src="Logo" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
Open / close drawer
|
|
||||||
</v-tooltip>
|
|
||||||
|
|
||||||
|
|
||||||
<nuxt-link to="/" exact>
|
<nuxt-link to="/" exact>
|
||||||
<h2 class="mt-1">Solsynth LLC</h2>
|
<h2>Solsynth LLC</h2>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<v-btn to="/products" exact prepend-icon="mdi-shape">{{ t("navProducts") }}</v-btn>
|
||||||
|
<v-btn to="/posts" exact prepend-icon="mdi-note-text">{{ t("navPosts") }}</v-btn>
|
||||||
|
<v-btn to="/gallery" exact prepend-icon="mdi-image-multiple">{{ t("navGallery") }}</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
|
||||||
<locale-select />
|
<locale-select />
|
||||||
<user-menu />
|
<user-menu />
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
<v-navigation-drawer v-model="openDrawer" location="left" width="300" floating>
|
<v-navigation-drawer v-model="openDrawer" location="left" width="300" temporary order="-1">
|
||||||
<v-list density="compact" nav color="primary">
|
<v-list density="compact" nav color="primary">
|
||||||
<v-list-item :title="t('navProducts')" prepend-icon="mdi-shape" to="/products" exact />
|
<v-list-item title="Knowledge Base" prepend-icon="mdi-library" to="/docs" exact />
|
||||||
<v-list-item :title="t('navPosts')" prepend-icon="mdi-note-text" to="/posts" exact />
|
<v-list-item title="Developer Portal" prepend-icon="mdi-code-tags" to="/dev" exact />
|
||||||
<v-list-item :title="t('navActivity')" prepend-icon="mdi-newspaper-variant-multiple-outline" to="/activity" exact />
|
<v-list-item title="Creator Hub" prepend-icon="mdi-pencil" to="/creator" exact />
|
||||||
<v-list-item :title="t('navGallery')" prepend-icon="mdi-image-multiple" to="/gallery" exact />
|
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
<v-divider class="border-opacity-50 my-1" />
|
<v-divider class="border-opacity-50 my-1" />
|
||||||
|
|
||||||
<v-list density="compact" nav color="primary">
|
<v-list density="compact" nav color="primary">
|
||||||
<v-list-item title="Knowledge Base" prepend-icon="mdi-library" to="/docs" exact />
|
<v-list-item title="Code Repository" prepend-icon="mdi-git" href="https://git.solsynth.dev" target="_blank" />
|
||||||
<v-list-item title="Developer Portal" prepend-icon="mdi-code-tags" to="/dev" exact />
|
|
||||||
<v-list-item title="Creator Hub" prepend-icon="mdi-pencil" to="/creator" exact />
|
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|
||||||
<v-divider class="border-opacity-50 mb-4 mt-0.5" />
|
<v-divider class="border-opacity-50 mb-4 mt-0.5" />
|
||||||
@ -51,9 +48,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Logo from "../assets/logo-w-shadow.png"
|
const { t } = useI18n()
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const openDrawer = ref(false)
|
const openDrawer = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.app-bar-blur {
|
||||||
|
-webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 40%, rgba(0, 0, 0, 0.5) 65%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 40%, rgba(0, 0, 0, 0.5) 65%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app-bar flat color="primary" scroll-behavior="hide" scroll-threshold="800">
|
<v-app-bar flat color="primary" scroll-behavior="hide" scroll-threshold="800">
|
||||||
<v-container fluid class="mx-auto d-flex align-center justify-center px-8">
|
<v-container fluid class="mx-auto d-flex align-center justify-center pr-8">
|
||||||
<v-tooltip>
|
<v-app-bar-nav-icon @click="openDrawer = !openDrawer" />
|
||||||
<template #activator="{ props }">
|
|
||||||
<div @click="openDrawer = !openDrawer" v-bind="props" class="cursor-pointer">
|
|
||||||
<v-img class="me-4 ms-1" width="32" height="32" alt="Logo" :src="Logo" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
Open / close drawer
|
|
||||||
</v-tooltip>
|
|
||||||
|
|
||||||
<nuxt-link to="/dev" exact>
|
<nuxt-link to="/dev" exact>
|
||||||
<h2 class="mt-1">Developer Portal</h2>
|
<h2>Developer Portal</h2>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
@ -51,6 +44,6 @@ const { t } = useI18n()
|
|||||||
const openDrawer = ref(false)
|
const openDrawer = ref(false)
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
titleTemplate: "%s | Solsynth Dev Portal"
|
titleTemplate: "%s | Solsynth Dev Portal",
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -151,7 +151,7 @@ export default defineNuxtConfig({
|
|||||||
"@pinia/nuxt",
|
"@pinia/nuxt",
|
||||||
"@nuxtjs/i18n",
|
"@nuxtjs/i18n",
|
||||||
"nuxt-schema-org",
|
"nuxt-schema-org",
|
||||||
"nuxt-gtag",
|
"@vueuse/motion/nuxt",
|
||||||
(_options, nuxt) => {
|
(_options, nuxt) => {
|
||||||
nuxt.hooks.hook("vite:extendConfig", (config) => {
|
nuxt.hooks.hook("vite:extendConfig", (config) => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"@nuxtjs/i18n": "^8.5.6",
|
"@nuxtjs/i18n": "^8.5.6",
|
||||||
"@nuxtjs/sitemap": "^6.1.5",
|
"@nuxtjs/sitemap": "^6.1.5",
|
||||||
"@pinia/nuxt": "^0.5.5",
|
"@pinia/nuxt": "^0.5.5",
|
||||||
|
"@vueuse/motion": "^3.0.3",
|
||||||
"feed": "^4.2.2",
|
"feed": "^4.2.2",
|
||||||
"nuxt": "^3.16.0",
|
"nuxt": "^3.16.0",
|
||||||
"nuxt-gtag": "^2.1.0",
|
"nuxt-gtag": "^2.1.0",
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-container class="content-container mx-auto">
|
|
||||||
<div class="my-3 mx-[1.5ch]">
|
|
||||||
<div class="flex gap-1">
|
|
||||||
<h1 class="text-2xl">{{ t("navActivity") }}</h1>
|
|
||||||
<v-btn size="x-small" variant="text" icon="mdi-rss" slim to="/activity/feed" />
|
|
||||||
</div>
|
|
||||||
<span>{{ t("navActivityCaption") }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<post-list class="mx-[-2.5ch]" :realm="config.public.solarRealm" />
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: t("navActivity"),
|
|
||||||
})
|
|
||||||
|
|
||||||
useSeoMeta({
|
|
||||||
title: t("navActivity"),
|
|
||||||
ogTitle: t("navActivity"),
|
|
||||||
description: t("navActivityCaption"),
|
|
||||||
ogDescription: t("navActivityCaption"),
|
|
||||||
ogType: "website",
|
|
||||||
})
|
|
||||||
|
|
||||||
const config = useRuntimeConfig()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.content-container {
|
|
||||||
max-width: 70ch !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -6,13 +6,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<v-btn
|
<v-btn color="primary" text="New" append-icon="mdi-plus" variant="tonal" to="/creator/stickers/new" />
|
||||||
color="primary"
|
|
||||||
text="New"
|
|
||||||
append-icon="mdi-plus"
|
|
||||||
variant="tonal"
|
|
||||||
to="/creator/stickers/new"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -24,10 +18,7 @@
|
|||||||
|
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<v-expansion-panels>
|
<v-expansion-panels>
|
||||||
<v-expansion-panel
|
<v-expansion-panel v-for="item in data" :key="'sticker-pack#' + item.id">
|
||||||
v-for="item in data"
|
|
||||||
:key="'sticker-pack#'+item.id"
|
|
||||||
>
|
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<p>{{ item.name }}</p>
|
<p>{{ item.name }}</p>
|
||||||
@ -87,16 +78,17 @@
|
|||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
|
||||||
<v-btn
|
<v-btn text="Cancel" color="grey" @click="isActive.value = false"></v-btn>
|
||||||
text="Cancel"
|
|
||||||
color="grey"
|
|
||||||
@click="isActive.value = false"
|
|
||||||
></v-btn>
|
|
||||||
|
|
||||||
<v-btn
|
<v-btn
|
||||||
text="Delete"
|
text="Delete"
|
||||||
color="error"
|
color="error"
|
||||||
@click="() => { deletePack(item); isActive.value = false }"
|
@click="
|
||||||
|
() => {
|
||||||
|
deletePack(item)
|
||||||
|
isActive.value = false
|
||||||
|
}
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
@ -131,6 +123,7 @@ useHead({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
const ua = useUserinfo()
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref<null | string>(null)
|
const error = ref<null | string>(null)
|
||||||
@ -140,7 +133,7 @@ const data = ref<any[]>([])
|
|||||||
async function readPacks() {
|
async function readPacks() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
const res = await solarFetch(`/cgi/uc/stickers/packs?take=10&offset=${data.value.length}`)
|
const res = await solarFetch(`/cgi/uc/stickers/packs?take=10&author=${ua.userinfo?.id}&offset=${data.value.length}`)
|
||||||
if (res.status != 200) {
|
if (res.status != 200) {
|
||||||
error.value = await res.text()
|
error.value = await res.text()
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,39 +1,71 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container class="flex flex-col my-2 px-12 gap-[4rem]">
|
<v-container class="flex flex-col my-2 px-12 gap-[4rem]">
|
||||||
<v-row class="content-section">
|
<section class="content-section flex flex-col items-center justify-center text-center px-4">
|
||||||
<v-col cols="12" md="4" class="flex justify-start">
|
<img
|
||||||
<div class="flex flex-col items-start">
|
v-motion="{
|
||||||
<h1 class="text-4xl font-bold">{{ t("brandName") }}</h1>
|
initial: {
|
||||||
<p class="text-lg mt-3 max-w-2/3">
|
y: 100,
|
||||||
{{ t("indexIntroduce") }}
|
opacity: 0,
|
||||||
</p>
|
},
|
||||||
<p class="text-grey mt-2">
|
enter: {
|
||||||
{{ t("indexProductListHint") }}
|
y: 0,
|
||||||
<v-icon icon="mdi-arrow-right" size="16" class="mb-0.5" />
|
opacity: 1,
|
||||||
</p>
|
},
|
||||||
</div>
|
}"
|
||||||
</v-col>
|
:src="Logo"
|
||||||
<v-col cols="12" md="8">
|
alt="Company Logo"
|
||||||
<v-card>
|
class="w-32 h-32 mb-4"
|
||||||
|
/>
|
||||||
|
<h1 class="text-4xl font-bold">Welcome to {{ t("brandName") }}</h1>
|
||||||
|
<p class="mt-2 text-lg">Building cool, open-source, and elegant apps for human.</p>
|
||||||
|
<v-btn class="mt-4" color="primary" prepend-icon="mdi-arrow-down" href="#products">{{ t("learnMore") }}</v-btn>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="content-section py-16" id="products">
|
||||||
|
<div class="container mx-auto text-center">
|
||||||
|
<h2 class="text-3xl font-bold">Our Projects</h2>
|
||||||
|
<p>Take a peek of our works.</p>
|
||||||
|
<v-card class="mt-12">
|
||||||
<product-carousel class="carousel-section" :products="products as any[]" />
|
<product-carousel class="carousel-section" :products="products as any[]" />
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</div>
|
||||||
</v-row>
|
</section>
|
||||||
|
|
||||||
<v-row class="content-section">
|
<v-row class="content-section">
|
||||||
<v-col cols="12" md="8">
|
<v-col cols="12" md="6">
|
||||||
<v-card class="h-[500px]">
|
<v-card>
|
||||||
<activity-list class="carousel-section" />
|
<v-list>
|
||||||
|
<v-list-item
|
||||||
|
title="GitHub"
|
||||||
|
subtitle="The place hosts most of our public projects' code"
|
||||||
|
prepend-icon="mdi-github"
|
||||||
|
href="https://github.com/Solsynth"
|
||||||
|
target="_blank"
|
||||||
|
/>
|
||||||
|
<v-list-item
|
||||||
|
lines="two"
|
||||||
|
title="Solsynth Code Repository"
|
||||||
|
subtitle="Our self-hosted git server, may contains some unpublished projects' code"
|
||||||
|
prepend-icon="mdi-git"
|
||||||
|
href="https://git.solsynth.dev/explore"
|
||||||
|
target="_blank"
|
||||||
|
/>
|
||||||
|
</v-list>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" md="4" class="flex justify-end" order="first" order-md="last">
|
<v-col cols="12" md="6" class="flex justify-end" order="first" order-md="last">
|
||||||
<div class="text-right flex flex-col items-end">
|
<div class="text-right flex flex-col items-end">
|
||||||
<h2 class="text-4xl font-bold">{{ t("indexActivities") }}</h2>
|
<h2 class="text-4xl font-bold">
|
||||||
<p class="text-lg mt-3 max-w-2/3">
|
We<br />
|
||||||
{{ t("indexActivitiesCaption") }}
|
❤️ Open-source
|
||||||
|
</h2>
|
||||||
|
<p class="text-md mt-3 max-w-2/3">
|
||||||
|
No software can run without the support of open source software, and our software is no exception.
|
||||||
|
Therefore, we feel it is important to contribute to open source as well.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-grey mt-2">
|
<p class="text-grey mt-2">
|
||||||
<v-icon icon="mdi-arrow-left" size="16" class="mb-0.5" />
|
<v-icon icon="mdi-arrow-left" size="16" class="mb-0.5" />
|
||||||
{{ t("indexActivitiesHint") }}
|
Check out our GitHub
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
@ -42,6 +74,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import Logo from "../assets/logo-w-shadow.png"
|
||||||
|
|
||||||
import { getLocale } from "~/utils/locale"
|
import { getLocale } from "~/utils/locale"
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@ -61,13 +95,16 @@ useSeoMeta({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const { data: products } = await useAsyncData("products", () => {
|
const { data: products } = await useAsyncData("products", () => {
|
||||||
return queryContent("/products").where({ _locale: getLocale(), archived: { $ne: true } }).limit(5).find()
|
return queryContent("/products")
|
||||||
|
.where({ _locale: getLocale(), archived: { $ne: true } })
|
||||||
|
.limit(5)
|
||||||
|
.find()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.carousel-section {
|
.carousel-section {
|
||||||
height: 96rem;
|
height: 120rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-section {
|
.content-section {
|
||||||
@ -76,3 +113,10 @@ const { data: products } = await useAsyncData("products", () => {
|
|||||||
place-items: center;
|
place-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container class="content-container mx-auto">
|
<v-container class="content-container mx-auto">
|
||||||
<div class="my-3 flex flex-row gap-4">
|
<div class="my-3 flex flex-row gap-4">
|
||||||
<nuxt-link :to="`/users/${post.publisher?.name}`">
|
<nuxt-link :to="`/publishers/${post.publisher?.name}`">
|
||||||
<v-avatar :image="post.publisher?.avatar" />
|
<v-avatar :image="getAttachmentUrl(post.publisher?.avatar)" />
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span>{{ post.publisher?.nick }} <span class="text-xs">@{{ post.publisher?.name }}</span></span>
|
<span>
|
||||||
|
{{ post.publisher?.nick }}
|
||||||
|
<span class="text-xs">@{{ post.publisher?.name }}</span>
|
||||||
|
</span>
|
||||||
<span v-if="post.body?.title" class="text-md">{{ post.body?.title }}</span>
|
<span v-if="post.body?.title" class="text-md">{{ post.body?.title }}</span>
|
||||||
<span v-if="post.body?.description" class="text-sm">{{ post.body?.description }}</span>
|
<span v-if="post.body?.description" class="text-sm">{{ post.body?.description }}</span>
|
||||||
<span v-if="!post.body?.title && !post.body?.description" class="text-sm">{{ post.publisher?.description }}</span>
|
<span v-if="!post.body?.title && !post.body?.description" class="text-sm">{{
|
||||||
|
post.publisher?.description
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -20,22 +25,18 @@
|
|||||||
/>
|
/>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<article class="text-base prose xl:text-lg mx-auto">
|
<article v-if="post.body?.content" class="text-base prose xl:text-lg mx-auto">
|
||||||
<m-d-c :value="post.body?.content"></m-d-c>
|
<m-d-c :value="post.body?.content"></m-d-c>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<v-card v-if="post.body?.attachments?.length > 0" class="mb-5">
|
<v-card v-if="post.body?.attachments?.length > 0" class="mb-5">
|
||||||
<attachment-carousel :attachments="post.body?.attachments" @update:metadata="args => attachments = args" />
|
<attachment-carousel :attachments="post.body?.attachments" @update:metadata="(args) => (attachments = args)" />
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<div class="mb-3 text-sm flex flex-col">
|
<div class="mb-3 text-sm flex flex-col">
|
||||||
<span class="flex flex-row gap-1">
|
<span class="flex flex-row gap-1">
|
||||||
<span>
|
<span> {{ post.metric.reply_count }} {{ post.metric.reply_count > 1 ? "replies" : "reply" }}, </span>
|
||||||
{{ post.metric.reply_count }} {{ post.metric.reply_count > 1 ? "replies" : "reply" }},
|
<span> {{ post.metric.reaction_count }} {{ post.metric.reaction_count > 1 ? "reactions" : "reaction" }} </span>
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{{ post.metric.reaction_count }} {{ post.metric.reaction_count > 1 ? "reactions" : "reaction" }}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{{ post.type.startsWith("a") ? "An" : "A" }} {{ post.type }} posted on
|
{{ post.type.startsWith("a") ? "An" : "A" }} {{ post.type }} posted on
|
||||||
@ -43,10 +44,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-if="post.tags?.length > 0" class="text-xs text-grey flex flex-row gap-1 mb-3">
|
||||||
v-if="post.tags?.length > 0"
|
|
||||||
class="text-xs text-grey flex flex-row gap-1 mb-3"
|
|
||||||
>
|
|
||||||
<nuxt-link
|
<nuxt-link
|
||||||
v-for="tag in post.tags"
|
v-for="tag in post.tags"
|
||||||
:to="`/posts/tags/${tag.alias}`"
|
:to="`/posts/tags/${tag.alias}`"
|
||||||
@ -108,21 +106,29 @@ if (!post.value) {
|
|||||||
navigateTo(`/posts/${post.value.area_alias}/${post.value.alias}`)
|
navigateTo(`/posts/${post.value.area_alias}/${post.value.alias}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = computed(() => post.value.body?.title ? `${post.value.body?.title} by @${post.value.publisher.name}` : `Post by @${post.value.publisher.name}`)
|
const title = computed(() =>
|
||||||
|
post.value.body?.title
|
||||||
|
? `${post.value.body?.title} by @${post.value.publisher.name}`
|
||||||
|
: `Post by @${post.value.publisher.name}`,
|
||||||
|
)
|
||||||
const description = computed(() => post.value.body?.description ?? post.value.body?.content.substring(0, 280).trim())
|
const description = computed(() => post.value.body?.description ?? post.value.body?.content.substring(0, 280).trim())
|
||||||
|
|
||||||
watch(attachments, (value) => {
|
watch(
|
||||||
if (post.value.body?.thumbnail) {
|
attachments,
|
||||||
firstImage.value = `${config.public.solarNetworkApi}/cgi/uc/attachments/${post.value.body?.thumbnail}`
|
(value) => {
|
||||||
}
|
if (post.value.body?.thumbnail) {
|
||||||
if (value.length > 0 && value[0].mimetype.split("/")[0] == "image") {
|
firstImage.value = `${config.public.solarNetworkApi}/cgi/uc/attachments/${post.value.body?.thumbnail}`
|
||||||
firstImage.value = `${config.public.solarNetworkApi}/cgi/uc/attachments/${attachments.value[0].rid}`
|
}
|
||||||
}
|
if (value.length > 0 && value[0].mimetype.split("/")[0] == "image") {
|
||||||
|
firstImage.value = `${config.public.solarNetworkApi}/cgi/uc/attachments/${attachments.value[0].rid}`
|
||||||
|
}
|
||||||
|
|
||||||
if (value.length > 0 && value[0].mimetype.split("/")[0] == "video") {
|
if (value.length > 0 && value[0].mimetype.split("/")[0] == "video") {
|
||||||
firstVideo.value = `${config.public.solarNetworkApi}/cgi/uc/attachments/${attachments.value[0].rid}`
|
firstVideo.value = `${config.public.solarNetworkApi}/cgi/uc/attachments/${attachments.value[0].rid}`
|
||||||
}
|
}
|
||||||
}, { immediate: true, deep: true })
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
)
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: title.value,
|
title: title.value,
|
||||||
|
73
pages/publishers/[name].vue
Normal file
73
pages/publishers/[name].vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<v-container class="mx-auto">
|
||||||
|
<v-img v-if="urlOfBanner" :src="urlOfBanner" :aspect-ratio="16 / 5" class="rounded-md mb-3" cover />
|
||||||
|
|
||||||
|
<div class="mx-[2.5ch]">
|
||||||
|
<div class="my-5 mx-4 flex flex-row gap-4">
|
||||||
|
<v-avatar :image="urlOfAvatar" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span
|
||||||
|
>{{ account?.nick }} <span class="text-xs">@{{ account?.name }}</span></span
|
||||||
|
>
|
||||||
|
<span class="text-sm">{{ account?.description }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-7">
|
||||||
|
<v-card rounded="xl" class="mx-[-5px]">
|
||||||
|
<v-tabs v-model="tab" align-tabs="start" color="primary" hide-slider>
|
||||||
|
<v-tab :value="1">{{ t("userActivity") }}</v-tab>
|
||||||
|
</v-tabs>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col row="12" lg="8">
|
||||||
|
<post-list class="mx-[-2.5ch] mt-[-16px]" v-if="account" :author="account.name" />
|
||||||
|
</v-col>
|
||||||
|
<v-col row="12" lg="4" order="first" order-lg="last">
|
||||||
|
<div class="sticky top-0 h-fit">
|
||||||
|
<v-card prepend-icon="mdi-identifier" title="About">
|
||||||
|
<v-card-text>
|
||||||
|
<p><b>Description</b></p>
|
||||||
|
<p>{{ account.description }}</p>
|
||||||
|
<p class="mt-3"><b>Joined At</b></p>
|
||||||
|
<p>{{ new Date(account.created_at).toLocaleString() }}</p>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</div>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
alias: ["/@:name(.*)*"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const route = useRoute()
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
|
||||||
|
const tab = ref(1)
|
||||||
|
|
||||||
|
const { data: account } = await useFetch<any>(`${config.public.solarNetworkApi}/cgi/co/publisher/${route.params.name}`)
|
||||||
|
|
||||||
|
if (account.value == null) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 404,
|
||||||
|
statusMessage: "User Not Found",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlOfAvatar = computed(() =>
|
||||||
|
account.value?.avatar ? `${config.public.solarNetworkApi}/cgi/uc/attachments/${account.value.avatar}` : void 0,
|
||||||
|
)
|
||||||
|
const urlOfBanner = computed(() =>
|
||||||
|
account.value?.banner ? `${config.public.solarNetworkApi}/cgi/uc/attachments/${account.value.banner}` : void 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
const externalOpenLink = computed(() => `${config.public.solianUrl}/accounts/view/${route.params.name}`)
|
||||||
|
</script>
|
@ -14,3 +14,13 @@ export async function solarFetch(input: string, init?: RequestInit) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAttachmentUrl(identifier: string | undefined): string | undefined {
|
||||||
|
if (identifier == null || identifier.length == 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
if (identifier.startsWith("http")) {
|
||||||
|
return identifier
|
||||||
|
}
|
||||||
|
return `${useRuntimeConfig().public.solarNetworkApi}/cgi/uc/attachments/${identifier}`
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user