Frontend: Basic notification receiving via SSE
ci/woodpecker/push/ociImagePush Pipeline is running
Details
ci/woodpecker/push/ociImagePush Pipeline is running
Details
This commit is contained in:
parent
7b02f84271
commit
f441de806f
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import XNotification from "@/components/MkNotification.vue";
|
import XNotification from "@/components/MagNotification.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
|
@ -44,10 +44,10 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref } from "vue";
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
import XNotification from "@/components/MkNotification.vue";
|
import XNotification from "@/components/MagNotification.vue";
|
||||||
import XList from "@/components/MkDateSeparatedList.vue";
|
import XList from "@/components/MkDateSeparatedList.vue";
|
||||||
import XNote from "@/components/MagNote.vue";
|
import XNote from "@/components/MagNote.vue";
|
||||||
import { stream } from "@/stream";
|
import { magStream, stream } from "@/stream";
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
import MagPagination, { Paging } from "@/components/MagPagination.vue";
|
import MagPagination, { Paging } from "@/components/MagPagination.vue";
|
||||||
import { endpoints, types } from "magnetar-common";
|
import { endpoints, types } from "magnetar-common";
|
||||||
|
@ -89,18 +89,20 @@ const onNotification = (notification) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let notifStream;
|
||||||
let connection;
|
let connection;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
notifStream = magStream.useFiltered("Notification", onNotification);
|
||||||
|
|
||||||
connection = stream.useChannel("main");
|
connection = stream.useChannel("main");
|
||||||
connection.on("notification", onNotification);
|
|
||||||
connection.on("readAllNotifications", () => {
|
connection.on("readAllNotifications", () => {
|
||||||
if (pagingComponent.value) {
|
if (pagingComponent.value) {
|
||||||
for (const item of pagingComponent.value.queue) {
|
for (const item of pagingComponent.value.queue) {
|
||||||
item.isRead = true;
|
item.is_read = true;
|
||||||
}
|
}
|
||||||
for (const item of pagingComponent.value.items) {
|
for (const item of pagingComponent.value.items) {
|
||||||
item.isRead = true;
|
item.is_read = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -110,7 +112,7 @@ onMounted(() => {
|
||||||
if (
|
if (
|
||||||
notificationIds.includes(pagingComponent.value.queue[i].id)
|
notificationIds.includes(pagingComponent.value.queue[i].id)
|
||||||
) {
|
) {
|
||||||
pagingComponent.value.queue[i].isRead = true;
|
pagingComponent.value.queue[i].is_read = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (
|
for (
|
||||||
|
@ -121,7 +123,7 @@ onMounted(() => {
|
||||||
if (
|
if (
|
||||||
notificationIds.includes(pagingComponent.value.items[i].id)
|
notificationIds.includes(pagingComponent.value.items[i].id)
|
||||||
) {
|
) {
|
||||||
pagingComponent.value.items[i].isRead = true;
|
pagingComponent.value.items[i].is_read = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ export const host = _HOST || address.host;
|
||||||
export const hostname = address.hostname;
|
export const hostname = address.hostname;
|
||||||
export const url = _REMOTE_URL || address.origin;
|
export const url = _REMOTE_URL || address.origin;
|
||||||
export const apiUrl = `${url}/api`;
|
export const apiUrl = `${url}/api`;
|
||||||
|
export const magStreamingUrl = `${url}/mag/v1`;
|
||||||
export const feApiUrl = `${url}/fe-api`;
|
export const feApiUrl = `${url}/fe-api`;
|
||||||
export const wsUrl = `${url
|
export const wsUrl = `${url
|
||||||
.replace("http://", "ws://")
|
.replace("http://", "ws://")
|
||||||
|
|
|
@ -62,7 +62,7 @@ import { computed, ref, watch } from "vue";
|
||||||
import { Virtual } from "swiper";
|
import { Virtual } from "swiper";
|
||||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||||
import { notificationTypes } from "calckey-js";
|
import { notificationTypes } from "calckey-js";
|
||||||
import XNotifications from "@/components/MkNotifications.vue";
|
import XNotifications from "@/components/MagNotifications.vue";
|
||||||
import XNotes from "@/components/MkNotes.vue";
|
import XNotes from "@/components/MkNotes.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import * as Misskey from "calckey-js";
|
import * as Misskey from "calckey-js";
|
||||||
import { markRaw } from "vue";
|
import { markRaw } from "vue";
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
import { url } from "@/config";
|
import { magStreamingUrl, url } from "@/config";
|
||||||
|
import { MagEventChannel } from "magnetar-common";
|
||||||
|
|
||||||
export const stream = markRaw(
|
export const stream = markRaw(
|
||||||
new Misskey.Stream(
|
new Misskey.Stream(
|
||||||
|
@ -22,3 +23,7 @@ function heartbeat(): void {
|
||||||
}
|
}
|
||||||
window.setTimeout(heartbeat, 1000 * 60);
|
window.setTimeout(heartbeat, 1000 * 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const magStream = markRaw(
|
||||||
|
new MagEventChannel(magStreamingUrl, $i ? $i.token : null)
|
||||||
|
);
|
||||||
|
|
|
@ -19,11 +19,12 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent } from "vue";
|
||||||
import { swInject } from "./sw-inject";
|
import { swInject } from "./sw-inject";
|
||||||
import { popup, popups, pendingApiRequestsCount } from "@/os";
|
import { popup, popups } from "@/os";
|
||||||
import { uploads } from "@/scripts/upload";
|
import { uploads } from "@/scripts/upload";
|
||||||
import * as sound from "@/scripts/sound";
|
import * as sound from "@/scripts/sound";
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
import { stream } from "@/stream";
|
import { magStream, stream } from "@/stream";
|
||||||
|
import { PackNotification } from "magnetar-common/built/types/PackNotification";
|
||||||
|
|
||||||
const XStreamIndicator = defineAsyncComponent(
|
const XStreamIndicator = defineAsyncComponent(
|
||||||
() => import("./stream-indicator.vue")
|
() => import("./stream-indicator.vue")
|
||||||
|
@ -32,7 +33,7 @@ const XUpload = defineAsyncComponent(() => import("./upload.vue"));
|
||||||
|
|
||||||
const dev = _DEV_;
|
const dev = _DEV_;
|
||||||
|
|
||||||
const onNotification = (notification) => {
|
const onNotification = (notification: PackNotification) => {
|
||||||
if ($i.mutingNotificationTypes.includes(notification.type)) return;
|
if ($i.mutingNotificationTypes.includes(notification.type)) return;
|
||||||
|
|
||||||
if (document.visibilityState === "visible") {
|
if (document.visibilityState === "visible") {
|
||||||
|
@ -42,7 +43,7 @@ const onNotification = (notification) => {
|
||||||
|
|
||||||
popup(
|
popup(
|
||||||
defineAsyncComponent(
|
defineAsyncComponent(
|
||||||
() => import("@/components/MkNotificationToast.vue")
|
() => import("@/components/MagNotificationToast.vue")
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
notification,
|
notification,
|
||||||
|
@ -56,8 +57,7 @@ const onNotification = (notification) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($i) {
|
if ($i) {
|
||||||
const connection = stream.useChannel("main", null, "UI");
|
const connection = magStream.useFiltered("Notification", onNotification);
|
||||||
connection.on("notification", onNotification);
|
|
||||||
|
|
||||||
//#region Listen message from SW
|
//#region Listen message from SW
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent } from "vue";
|
||||||
import XColumn from "./column.vue";
|
import XColumn from "./column.vue";
|
||||||
import { updateColumn } from "./deck-store";
|
|
||||||
import type { Column } from "./deck-store";
|
import type { Column } from "./deck-store";
|
||||||
import XNotifications from "@/components/MkNotifications.vue";
|
import { updateColumn } from "./deck-store";
|
||||||
|
import XNotifications from "@/components/MagNotifications.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
|
|
|
@ -26,16 +26,10 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent } from "vue";
|
||||||
import {
|
import { useWidgetPropsManager, Widget, WidgetComponentExpose } from "./widget";
|
||||||
useWidgetPropsManager,
|
|
||||||
Widget,
|
|
||||||
WidgetComponentEmits,
|
|
||||||
WidgetComponentExpose,
|
|
||||||
WidgetComponentProps,
|
|
||||||
} from "./widget";
|
|
||||||
import { GetFormResultType } from "@/scripts/form";
|
import { GetFormResultType } from "@/scripts/form";
|
||||||
import MkContainer from "@/components/MkContainer.vue";
|
import MkContainer from "@/components/MkContainer.vue";
|
||||||
import XNotifications from "@/components/MkNotifications.vue";
|
import XNotifications from "@/components/MagNotifications.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
|
|
|
@ -11,5 +11,8 @@
|
||||||
"description": "A library with common utilities for Magnetar application development",
|
"description": "A library with common utilities for Magnetar application development",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"eventemitter3": "^5.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,11 @@ import {
|
||||||
FrontendApiEndpoints,
|
FrontendApiEndpoints,
|
||||||
} from "./fe-api";
|
} from "./fe-api";
|
||||||
|
|
||||||
export * as types from "./types";
|
import { MagEventChannel } from "./sse-listener";
|
||||||
export * as packed from "./packed";
|
|
||||||
export * as endpoints from "./endpoints";
|
import * as types from "./types";
|
||||||
|
import * as packed from "./packed";
|
||||||
|
import * as endpoints from "./endpoints";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Method,
|
Method,
|
||||||
|
@ -27,4 +29,8 @@ export {
|
||||||
feEndpoints,
|
feEndpoints,
|
||||||
FrontendApiEndpoint,
|
FrontendApiEndpoint,
|
||||||
FrontendApiEndpoints,
|
FrontendApiEndpoints,
|
||||||
|
MagEventChannel,
|
||||||
|
types,
|
||||||
|
packed,
|
||||||
|
endpoints,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
import { EventEmitter } from "eventemitter3";
|
||||||
|
import { ChannelEvent } from "./types/ChannelEvent";
|
||||||
|
|
||||||
|
export type MagChannelState = "connected" | "exponentialBackoff" | "failed";
|
||||||
|
|
||||||
|
export class MagEventChannel extends EventEmitter<{
|
||||||
|
stateChange: MagChannelState;
|
||||||
|
message: ChannelEvent;
|
||||||
|
close: "cancelled";
|
||||||
|
}> {
|
||||||
|
private readonly baseUrl: string;
|
||||||
|
private attempts = 0;
|
||||||
|
private readonly maxAttempts: number;
|
||||||
|
private readonly token: string | null;
|
||||||
|
private readonly backoffFactor: number;
|
||||||
|
private readonly backoffBase: number;
|
||||||
|
private readonly closePromise: Promise<"cancelled">;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
baseUrl: string,
|
||||||
|
token: string | null,
|
||||||
|
maxReconnectAttempts: number = 12,
|
||||||
|
backoffFactor: number = 1.618,
|
||||||
|
backoffBase: number = 500.0
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
this.token = token;
|
||||||
|
this.maxAttempts = maxReconnectAttempts;
|
||||||
|
this.backoffFactor = backoffFactor;
|
||||||
|
this.backoffBase = backoffBase;
|
||||||
|
this.closePromise = new Promise((resolve) => {
|
||||||
|
this.on("close", resolve);
|
||||||
|
});
|
||||||
|
this.connect().then();
|
||||||
|
}
|
||||||
|
|
||||||
|
public useFiltered<T extends ChannelEvent["type"]>(
|
||||||
|
messageType: T,
|
||||||
|
listener: (val: (ChannelEvent & { type: T })["body"]) => void
|
||||||
|
) {
|
||||||
|
const cb = (val: ChannelEvent) => {
|
||||||
|
if (val.type != messageType) return;
|
||||||
|
|
||||||
|
listener((val as ChannelEvent & { type: T }).body);
|
||||||
|
};
|
||||||
|
this.on("message", cb);
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async connect() {
|
||||||
|
if (this.attempts >= this.maxAttempts) {
|
||||||
|
this.emit("stateChange", "failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authorization = this.token ? `Bearer ${this.token}` : undefined;
|
||||||
|
const baseUrl = this.baseUrl.replace(/\/+$/, "");
|
||||||
|
|
||||||
|
const response = await fetch(`${baseUrl}/streaming`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: authorization ? { authorization } : {},
|
||||||
|
credentials: "omit",
|
||||||
|
cache: "no-cache",
|
||||||
|
}).catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
response === null ||
|
||||||
|
response.status >= 500 ||
|
||||||
|
response.body === null
|
||||||
|
) {
|
||||||
|
this.emit("stateChange", "exponentialBackoff");
|
||||||
|
setTimeout(
|
||||||
|
() => this.connect(),
|
||||||
|
this.backoffBase * Math.pow(this.backoffFactor, this.attempts)
|
||||||
|
);
|
||||||
|
this.attempts++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status >= 400 && response.status < 500) {
|
||||||
|
this.emit("stateChange", "failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.attempts = 0;
|
||||||
|
|
||||||
|
const decoderStream = new TextDecoderStream();
|
||||||
|
const reader = response.body.pipeThrough(decoderStream).getReader();
|
||||||
|
this.emit("stateChange", "connected");
|
||||||
|
|
||||||
|
let buf = "";
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const res = await Promise.race([reader.read(), this.closePromise]);
|
||||||
|
|
||||||
|
if (res === "cancelled") break;
|
||||||
|
|
||||||
|
if (res.done) {
|
||||||
|
this.emit("stateChange", "exponentialBackoff");
|
||||||
|
setTimeout(
|
||||||
|
() => this.connect(),
|
||||||
|
this.backoffBase *
|
||||||
|
Math.pow(this.backoffFactor, this.attempts)
|
||||||
|
);
|
||||||
|
this.attempts++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf += res.value;
|
||||||
|
|
||||||
|
const splitIndex = buf.indexOf("\n\n");
|
||||||
|
if (splitIndex === -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawValue = buf.substring(0, splitIndex);
|
||||||
|
buf = buf.substring(splitIndex + 2);
|
||||||
|
|
||||||
|
if (rawValue.startsWith(":")) continue;
|
||||||
|
|
||||||
|
const text = rawValue
|
||||||
|
.split("\n")
|
||||||
|
.filter((l) => l.startsWith("data: "))
|
||||||
|
.map((l) => l.substring("data: ".length))
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
if (!text) continue;
|
||||||
|
|
||||||
|
const data = JSON.parse(text) as ChannelEvent;
|
||||||
|
this.emit("message", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async close() {
|
||||||
|
this.emit("close", "cancelled");
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,3 +55,4 @@ export { NotificationNoteExt } from "./types/NotificationNoteExt";
|
||||||
export { NotificationReactionExt } from "./types/NotificationReactionExt";
|
export { NotificationReactionExt } from "./types/NotificationReactionExt";
|
||||||
export { NotificationUserExt } from "./types/NotificationUserExt";
|
export { NotificationUserExt } from "./types/NotificationUserExt";
|
||||||
export { NotificationsReq } from "./types/NotificationsReq";
|
export { NotificationsReq } from "./types/NotificationsReq";
|
||||||
|
export { ChannelEvent } from "./types/ChannelEvent";
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { PackNotification } from "./PackNotification";
|
||||||
|
|
||||||
|
export type ChannelEvent = { "type": "Notification", "body": PackNotification };
|
|
@ -358,6 +358,10 @@ importers:
|
||||||
version: 4.1.0(vue@3.3.4)
|
version: 4.1.0(vue@3.3.4)
|
||||||
|
|
||||||
magnetar-common:
|
magnetar-common:
|
||||||
|
dependencies:
|
||||||
|
eventemitter3:
|
||||||
|
specifier: ^5.0.1
|
||||||
|
version: 5.0.1
|
||||||
devDependencies:
|
devDependencies:
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.1.6
|
specifier: ^5.1.6
|
||||||
|
@ -3269,6 +3273,10 @@ packages:
|
||||||
/eventemitter3@4.0.7:
|
/eventemitter3@4.0.7:
|
||||||
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||||
|
|
||||||
|
/eventemitter3@5.0.1:
|
||||||
|
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/events@3.3.0:
|
/events@3.3.0:
|
||||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||||
engines: {node: '>=0.8.x'}
|
engines: {node: '>=0.8.x'}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use futures_util::StreamExt as _;
|
||||||
use magnetar_calckey_model::model_ext::IdShape;
|
use magnetar_calckey_model::model_ext::IdShape;
|
||||||
use magnetar_calckey_model::{CalckeySub, MainStreamMessage, SubMessage};
|
use magnetar_calckey_model::{CalckeySub, MainStreamMessage, SubMessage};
|
||||||
use magnetar_sdk::types::streaming::ChannelEvent;
|
use magnetar_sdk::types::streaming::ChannelEvent;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
@ -47,6 +48,11 @@ pub async fn handle_streaming(
|
||||||
};
|
};
|
||||||
|
|
||||||
if id != user_id {
|
if id != user_id {
|
||||||
|
trace!(
|
||||||
|
"Skipping message intended for {} in channel {}",
|
||||||
|
id,
|
||||||
|
user_id
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,11 +65,15 @@ pub async fn handle_streaming(
|
||||||
|
|
||||||
drop_on_close(sub, tx);
|
drop_on_close(sub, tx);
|
||||||
|
|
||||||
|
let event_counter = Arc::new(AtomicU64::default());
|
||||||
let stream = ReceiverStream::new(rx).filter_map(move |m| {
|
let stream = ReceiverStream::new(rx).filter_map(move |m| {
|
||||||
|
trace!("Processing raw message: {:?}", m);
|
||||||
|
|
||||||
let service = service.clone();
|
let service = service.clone();
|
||||||
let self_user = self_user.clone();
|
let self_user = self_user.clone();
|
||||||
|
let event_counter = event_counter.clone();
|
||||||
async move {
|
async move {
|
||||||
match m {
|
let message = match m {
|
||||||
MainStreamMessage::Notification(IdShape { id }) => {
|
MainStreamMessage::Notification(IdShape { id }) => {
|
||||||
let ctx = PackingContext::new(service, Some(self_user.clone()))
|
let ctx = PackingContext::new(service, Some(self_user.clone()))
|
||||||
.await
|
.await
|
||||||
|
@ -75,7 +85,10 @@ pub async fn handle_streaming(
|
||||||
let notification_model = NotificationModel;
|
let notification_model = NotificationModel;
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
Event::default().json_data(ChannelEvent::Notification(
|
Event::default()
|
||||||
|
.id(event_counter.fetch_add(1, Ordering::Relaxed).to_string())
|
||||||
|
.event("message")
|
||||||
|
.json_data(ChannelEvent::Notification(
|
||||||
notification_model
|
notification_model
|
||||||
.get_notification(&ctx, &id, &self_user.id)
|
.get_notification(&ctx, &id, &self_user.id)
|
||||||
.await
|
.await
|
||||||
|
@ -88,7 +101,11 @@ pub async fn handle_streaming(
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
trace!("Sending message: {:?}", message);
|
||||||
|
|
||||||
|
message
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue