Compare commits
2 Commits
c616214c3b
...
05e8782557
Author | SHA1 | Date | |
---|---|---|---|
05e8782557 | |||
e986ff8c5f |
@ -3,12 +3,25 @@
|
|||||||
Attached {{ props.attachments.length }} attachment(s)
|
Attached {{ props.attachments.length }} attachment(s)
|
||||||
</v-chip>
|
</v-chip>
|
||||||
|
|
||||||
<v-card v-else variant="outlined" class="max-w-[540px] max-h-[720px]">
|
<v-responsive v-else :aspect-ratio="16 / 9" max-height="720">
|
||||||
<v-carousel hide-delimiter-background height="100%" :show-arrows="false">
|
<v-card variant="outlined" class="w-full h-full">
|
||||||
<v-carousel-item v-for="item in attachments">
|
<v-carousel
|
||||||
<img v-if="item.type === 1" :src="getUrl(item)" :alt="item.filename" class="cursor-zoom-in"
|
hide-delimiter-background
|
||||||
@click="openLightbox" />
|
height="100%"
|
||||||
<video v-if="item.type === 2" controls class="w-full">
|
:hide-delimiters="props.attachments.length <= 1"
|
||||||
|
:show-arrows="false"
|
||||||
|
>
|
||||||
|
<v-carousel-item v-for="(item, idx) in attachments">
|
||||||
|
<img
|
||||||
|
v-if="item.type === 1"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
class="cursor-zoom-in content-visibility-auto"
|
||||||
|
:src="getUrl(item)"
|
||||||
|
:alt="item.filename"
|
||||||
|
@click="openLightbox(item, idx)"
|
||||||
|
/>
|
||||||
|
<video v-if="item.type === 2" controls class="w-full content-visibility-auto">
|
||||||
<source :src="getUrl(item)" />
|
<source :src="getUrl(item)" />
|
||||||
</video>
|
</video>
|
||||||
<div v-if="item.type === 3" class="w-full px-7 py-12">
|
<div v-if="item.type === 3" class="w-full px-7 py-12">
|
||||||
@ -17,7 +30,13 @@
|
|||||||
</v-carousel-item>
|
</v-carousel-item>
|
||||||
</v-carousel>
|
</v-carousel>
|
||||||
|
|
||||||
<vue-easy-lightbox teleport="#app" :visible="lightbox" :imgs="[getUrl(current)]" @hide="lightbox = false">
|
<vue-easy-lightbox
|
||||||
|
teleport="#app"
|
||||||
|
:visible="lightbox"
|
||||||
|
:imgs="props.attachments.map((x) => getUrl(x))"
|
||||||
|
v-model:index="currentIndex"
|
||||||
|
@hide="lightbox = false"
|
||||||
|
>
|
||||||
<template v-slot:close-btn="{ close }">
|
<template v-slot:close-btn="{ close }">
|
||||||
<v-btn
|
<v-btn
|
||||||
class="fixed left-2 top-2"
|
class="fixed left-2 top-2"
|
||||||
@ -30,6 +49,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</vue-easy-lightbox>
|
</vue-easy-lightbox>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
</v-responsive>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -43,23 +63,24 @@ const props = defineProps<{ attachments: any[]; overview?: boolean }>()
|
|||||||
const ui = useUI()
|
const ui = useUI()
|
||||||
|
|
||||||
const lightbox = ref(false)
|
const lightbox = ref(false)
|
||||||
const focus = ref(0)
|
|
||||||
|
|
||||||
const current = computed(() => props.attachments[focus.value])
|
const current = ref<any>(null)
|
||||||
const canLightbox = computed(() => current.value.type === 1)
|
const currentIndex = ref(0)
|
||||||
|
|
||||||
const safeAreaTop = computed(() => {
|
const safeAreaTop = computed(() => {
|
||||||
return `${ui.safeArea.top}px`
|
return `${ui.safeArea.top}px`
|
||||||
})
|
})
|
||||||
|
|
||||||
function getUrl(item: any) {
|
function getUrl(item: any) {
|
||||||
return item.external_url ? item.external_url : buildRequestUrl("interactive", `/api/attachments/o/${item.file_id}`)
|
return item.external_url
|
||||||
|
? item.external_url
|
||||||
|
: buildRequestUrl("interactive", `/api/attachments/o/${item.file_id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openLightbox() {
|
function openLightbox(item: any, idx: number) {
|
||||||
if (canLightbox.value) {
|
current.value = item
|
||||||
|
currentIndex.value = idx
|
||||||
lightbox.value = true
|
lightbox.value = true
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -11,13 +11,13 @@
|
|||||||
>
|
>
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<v-toolbar
|
<v-toolbar
|
||||||
class="flex items-center justify-between px-[12px] border-opacity-15"
|
class="flex items-center justify-between px-[14px] border-opacity-15"
|
||||||
color="primary"
|
color="primary"
|
||||||
height="64"
|
height="64"
|
||||||
:style="`padding-top: ${safeAreaTop}`"
|
:style="`padding-top: ${safeAreaTop}`"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<img src="/favicon.png" alt="Logo" width="36" height="36" class="block" />
|
<img src="/favicon.png" alt="Logo" width="32" height="32" class="block" />
|
||||||
<div v-show="!drawerMini" class="ms-6 font-medium">Solar Network</div>
|
<div v-show="!drawerMini" class="ms-6 font-medium">Solar Network</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -83,22 +83,6 @@
|
|||||||
<v-main id="main">
|
<v-main id="main">
|
||||||
<router-view />
|
<router-view />
|
||||||
</v-main>
|
</v-main>
|
||||||
|
|
||||||
<v-menu open-on-hover open-on-click :open-delay="0" :close-delay="0" location="top"
|
|
||||||
transition="scroll-y-reverse-transition">
|
|
||||||
<template v-slot:activator="{ props }">
|
|
||||||
<v-fab v-bind="props" appear class="editor-fab" icon="mdi-pencil" color="primary" size="64"
|
|
||||||
:active="id.userinfo.isLoggedIn" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="flex flex-col items-center gap-4 mb-4">
|
|
||||||
<v-btn variant="elevated" color="secondary" icon="mdi-newspaper-variant" @click="editor.show.article = true" />
|
|
||||||
<v-btn variant="elevated" color="accent" icon="mdi-camera-iris" @click="editor.show.moment = true" />
|
|
||||||
</div>
|
|
||||||
</v-menu>
|
|
||||||
|
|
||||||
<post-tools />
|
|
||||||
<realm-tools />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -123,7 +107,6 @@ const safeAreaBottom = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const id = useUserinfo()
|
const id = useUserinfo()
|
||||||
const editor = useEditor()
|
|
||||||
|
|
||||||
const username = computed(() => {
|
const username = computed(() => {
|
||||||
if (id.userinfo.isLoggedIn) {
|
if (id.userinfo.isLoggedIn) {
|
||||||
@ -154,10 +137,3 @@ const drawerOpen = ref(true)
|
|||||||
const drawerMini = ref(false)
|
const drawerMini = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.editor-fab {
|
|
||||||
position: fixed !important;
|
|
||||||
bottom: 16px;
|
|
||||||
right: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
65
src/layouts/plaza.vue
Normal file
65
src/layouts/plaza.vue
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
|
||||||
|
<v-fab
|
||||||
|
appear
|
||||||
|
class="editor-fab"
|
||||||
|
color="primary"
|
||||||
|
size="64"
|
||||||
|
:active="id.userinfo.isLoggedIn"
|
||||||
|
>
|
||||||
|
<v-icon icon="mdi-pencil" />
|
||||||
|
|
||||||
|
<v-speed-dial
|
||||||
|
target=".editor-fab"
|
||||||
|
activator="parent"
|
||||||
|
location="top center"
|
||||||
|
class="editor-speed-dial"
|
||||||
|
transition="slide-y-reverse-transition"
|
||||||
|
open-on-hover
|
||||||
|
open-on-click
|
||||||
|
>
|
||||||
|
<v-btn
|
||||||
|
key="article"
|
||||||
|
variant="elevated"
|
||||||
|
color="secondary"
|
||||||
|
icon="mdi-newspaper-variant"
|
||||||
|
@click="editor.show.article = true"
|
||||||
|
/>
|
||||||
|
<v-btn
|
||||||
|
key="moment"
|
||||||
|
variant="elevated"
|
||||||
|
color="accent"
|
||||||
|
icon="mdi-camera-iris"
|
||||||
|
@click="editor.show.moment = true"
|
||||||
|
/>
|
||||||
|
</v-speed-dial>
|
||||||
|
</v-fab>
|
||||||
|
|
||||||
|
<post-tools />
|
||||||
|
<realm-tools />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PostTools from "@/components/publish/PostTools.vue"
|
||||||
|
import RealmTools from "@/components/realms/RealmTools.vue"
|
||||||
|
import { useEditor } from "@/stores/editor"
|
||||||
|
import { useUserinfo } from "@/stores/userinfo"
|
||||||
|
|
||||||
|
const id = useUserinfo()
|
||||||
|
const editor = useEditor()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.editor-fab {
|
||||||
|
position: fixed !important;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-speed-dial {
|
||||||
|
position: fixed !important;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -7,6 +7,10 @@ const router = createRouter({
|
|||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
component: MasterLayout,
|
component: MasterLayout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
component: () => import("@/layouts/plaza.vue"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
@ -29,6 +33,19 @@ const router = createRouter({
|
|||||||
path: "/realms/:realmId",
|
path: "/realms/:realmId",
|
||||||
name: "realms.page",
|
name: "realms.page",
|
||||||
component: () => import("@/views/realms/page.vue")
|
component: () => import("@/views/realms/page.vue")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: "/chat",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: ":channel",
|
||||||
|
name: "chat.channel",
|
||||||
|
component: () => import("@/views/chat/channel.vue")
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,8 @@ import { Preferences } from "@capacitor/preferences"
|
|||||||
|
|
||||||
const serviceMap: { [id: string]: string } = {
|
const serviceMap: { [id: string]: string } = {
|
||||||
interactive: "https://co.solsynth.dev",
|
interactive: "https://co.solsynth.dev",
|
||||||
identity: "https://id.solsynth.dev"
|
identity: "https://id.solsynth.dev",
|
||||||
|
messaging: "http://localhost:8447",
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function request(service: string, input: string, init?: RequestInit, noRetry?: boolean) {
|
export async function request(service: string, input: string, init?: RequestInit, noRetry?: boolean) {
|
||||||
|
41
src/views/chat/channel.vue
Normal file
41
src/views/chat/channel.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<v-app-bar :order="5" color="grey-lighten-3">
|
||||||
|
<div class="max-md:px-5 md:px-12 flex flex-grow-1 items-center">
|
||||||
|
<v-app-bar-nav-icon icon="mdi-chat" :loading="loading" />
|
||||||
|
|
||||||
|
<h2 class="ml-2 text-lg font-500">{{ metadata?.name }}</h2>
|
||||||
|
|
||||||
|
<p class="ml-3 text-xs opacity-80">{{ metadata?.description }}</p>
|
||||||
|
</div>
|
||||||
|
</v-app-bar>
|
||||||
|
|
||||||
|
<!-- @vue-ignore -->
|
||||||
|
<v-snackbar v-model="error" :timeout="5000">Something went wrong... {{ error }}</v-snackbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { request } from "@/scripts/request"
|
||||||
|
import { useRoute } from "vue-router"
|
||||||
|
import { ref } from "vue"
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const error = ref<string | null>(null)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const metadata = ref<any>(null)
|
||||||
|
|
||||||
|
async function readMetadata() {
|
||||||
|
loading.value = true
|
||||||
|
const res = await request("messaging", `/api/channels/${route.params.channel}`)
|
||||||
|
if (res.status !== 200) {
|
||||||
|
error.value = await res.text()
|
||||||
|
} else {
|
||||||
|
error.value = null
|
||||||
|
metadata.value = await res.json()
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
readMetadata()
|
||||||
|
</script>
|
Reference in New Issue
Block a user