diff --git a/index.html b/index.html
index 637dca8..635a6f1 100644
--- a/index.html
+++ b/index.html
@@ -5,6 +5,7 @@
+
Solian
diff --git a/src/components/chat/ChatEditor.vue b/src/components/chat/ChatEditor.vue
index 11dfa26..41bbf30 100644
--- a/src/components/chat/ChatEditor.vue
+++ b/src/components/chat/ChatEditor.vue
@@ -1,10 +1,10 @@
-
+
@@ -20,7 +20,7 @@
@@ -31,8 +31,8 @@
@@ -48,7 +48,7 @@
@@ -60,8 +60,8 @@
auto-grow
hide-details
class="w-full"
- variant="outlined"
- density="compact"
+ variant="solo"
+ density="comfortable"
placeholder="Enter some messages..."
:rows="1"
:max-rows="6"
diff --git a/src/components/chat/channels/ChannelAction.vue b/src/components/chat/channels/ChannelAction.vue
index 9c5a3d6..9aef434 100644
--- a/src/components/chat/channels/ChannelAction.vue
+++ b/src/components/chat/channels/ChannelAction.vue
@@ -7,8 +7,8 @@
-
+
diff --git a/src/layouts/chat.vue b/src/layouts/chat.vue
index 5fdfef2..5f71741 100644
--- a/src/layouts/chat.vue
+++ b/src/layouts/chat.vue
@@ -9,6 +9,23 @@
+
+
+
@@ -24,6 +41,7 @@ import { onMounted, ref, watch } from "vue"
import { useChannels } from "@/stores/channels"
import ChannelAction from "@/components/chat/channels/ChannelAction.vue"
import { useUI } from "@/stores/ui"
+import { getAtk } from "@/stores/userinfo"
const { showErrorSnackbar } = useUI()
@@ -31,6 +49,7 @@ const route = useRoute()
const channels = useChannels()
const loading = ref(false)
+const calling = ref(false)
async function readMetadata() {
loading.value = true
@@ -43,6 +62,30 @@ async function readMetadata() {
loading.value = false
}
+async function makeCall() {
+ calling.value = true
+ const res = await request("messaging", `/api/channels/${route.params.channel}/calls`, {
+ method: "POST",
+ headers: { Authorization: `Bearer ${await getAtk()}` }
+ })
+ if (res.status !== 200) {
+ showErrorSnackbar(await res.text())
+ }
+ calling.value = false
+}
+
+async function endsCall() {
+ calling.value = true
+ const res = await request("messaging", `/api/channels/${route.params.channel}/calls/ongoing`, {
+ method: "DELETE",
+ headers: { Authorization: `Bearer ${await getAtk()}` }
+ })
+ if (res.status !== 200) {
+ showErrorSnackbar(await res.text())
+ }
+ calling.value = false
+}
+
watch(
() => route.params.channel,
(val) => {
diff --git a/src/stores/channels.ts b/src/stores/channels.ts
index cb90fe2..7f626f2 100644
--- a/src/stores/channels.ts
+++ b/src/stores/channels.ts
@@ -35,13 +35,16 @@ export const useChannels = defineStore("channels", () => {
const available = ref([])
const current = ref(null)
+
const messages = ref([])
+ const call = ref(null)
const route = useRoute()
watch(
() => route.params.channel,
(val) => {
if (!val) {
+ call.value = null
messages.value = []
current.value = null
}
@@ -64,6 +67,22 @@ export const useChannels = defineStore("channels", () => {
const ui = useUI()
+ async function exchangeCallToken() {
+ if (!(await checkLoggedIn())) return
+ if (!current.value) return
+
+ const res = await request("messaging", `/api/channels/${current.value.alias}/calls/ongoing/token`, {
+ method: "POST",
+ headers: { Authorization: `Bearer ${await getAtk()}` }
+ })
+ if (res.status !== 200) {
+ ui.showErrorSnackbar(`unable to exchange call token: ${await res.text()}`)
+ return null
+ } else {
+ return await res.json()
+ }
+ }
+
async function connect() {
if (!(await checkLoggedIn())) return
@@ -112,6 +131,13 @@ export const useChannels = defineStore("channels", () => {
return x.id !== payload.id
})
break
+
+ case "calls.new":
+ call.value = payload
+ break
+ case "calls.end":
+ call.value = null
+ break
}
}
})
@@ -121,5 +147,5 @@ export const useChannels = defineStore("channels", () => {
socket.close()
}
- return { done, show, related, available, current, messages, list, connect, disconnect }
+ return { done, show, related, available, current, messages, call, list, exchangeCallToken, connect, disconnect }
})
\ No newline at end of file
diff --git a/src/views/chat/page.vue b/src/views/chat/page.vue
index f015c80..ed43eaf 100644
--- a/src/views/chat/page.vue
+++ b/src/views/chat/page.vue
@@ -7,11 +7,29 @@
-
+
+
+ val && mountJitsi()"
+ >
+
+
+
+
+
+
+
+
@@ -39,6 +57,20 @@ const loading = ref(false)
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
+async function readCall() {
+ loading.value = true
+ const res = await request(
+ "messaging",
+ `/api/channels/${route.params.channel}/calls/ongoing`
+ )
+ if (res.status !== 200 && res.status !== 404) {
+ showErrorSnackbar(await res.text())
+ } else {
+ channels.call = await res.json()
+ }
+ loading.value = false
+}
+
async function readHistory() {
loading.value = true
const res = await request(
@@ -86,9 +118,11 @@ watch(
() => channels.current,
(val) => {
if (val) {
+ unmountJitsi()
pagination.page = 1
pagination.total = 0
readHistory()
+ readCall()
}
},
{ immediate: true }
@@ -97,4 +131,61 @@ watch(
function scrollTop() {
window.scroll({ top: 0 })
}
-
\ No newline at end of file
+
+watch(
+ () => channels.call,
+ (val) => {
+ if (!val) {
+ unmountJitsi()
+ }
+ }
+)
+
+let mounted = false
+let jitsiInstance: any
+
+async function mountJitsi() {
+ if (mounted) return false
+ if (!channels.call) return
+ const tk = await channels.exchangeCallToken()
+ console.log(tk)
+ if (!tk) return
+ const domain = tk.endpoint.replace("http://", "").replace("https://", "")
+ const options = {
+ roomName: channels.call.external_id,
+ parentNode: document.querySelector("#call"),
+ jwt: tk.token
+ }
+ // This class imported by the script tag in index.html
+ // @ts-ignore
+ jitsiInstance = new JitsiMeetExternalAPI(domain, options)
+ mounted = true
+}
+
+function unmountJitsi() {
+ mounted = false
+ if (jitsiInstance) {
+ jitsiInstance.dispose()
+ jitsiInstance = null
+ }
+}
+
+onUnmounted(() => unmountJitsi())
+
+
+
+
+
\ No newline at end of file