= T extends T ? keyof T : never;
export type Exact = P extends Builtin ? P
: P & { [K in keyof P]: Exact
} & { [K in Exclude>]: never };
+function longToNumber(int64: { toString(): string }): number {
+ const num = globalThis.Number(int64.toString());
+ if (num > globalThis.Number.MAX_SAFE_INTEGER) {
+ throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
+ }
+ if (num < globalThis.Number.MIN_SAFE_INTEGER) {
+ throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER");
+ }
+ return num;
+}
+
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
diff --git a/src/server/api/proto/app/v1/common.ts b/src/server/api/proto/app/v1/common.ts
index 38576ba..884125c 100644
--- a/src/server/api/proto/app/v1/common.ts
+++ b/src/server/api/proto/app/v1/common.ts
@@ -83,6 +83,17 @@ export interface AdTemplate {
updatedAt?: string | undefined;
}
+export interface PopupAd {
+ id?: string | undefined;
+ type?: string | undefined;
+ label?: string | undefined;
+ value?: string | undefined;
+ isActive?: boolean | undefined;
+ maxTriggersPerSession?: number | undefined;
+ createdAt?: string | undefined;
+ updatedAt?: string | undefined;
+}
+
export interface PlayerConfig {
id?: string | undefined;
name?: string | undefined;
@@ -335,6 +346,19 @@ export interface AdminAdTemplate {
updatedAt?: string | undefined;
}
+export interface AdminPopupAd {
+ id?: string | undefined;
+ userId?: string | undefined;
+ type?: string | undefined;
+ label?: string | undefined;
+ value?: string | undefined;
+ isActive?: boolean | undefined;
+ maxTriggersPerSession?: number | undefined;
+ ownerEmail?: string | undefined;
+ createdAt?: string | undefined;
+ updatedAt?: string | undefined;
+}
+
export interface AdminJob {
id?: string | undefined;
status?: string | undefined;
@@ -1681,6 +1705,203 @@ export const AdTemplate: MessageFns = {
},
};
+function createBasePopupAd(): PopupAd {
+ return {
+ id: "",
+ type: "",
+ label: "",
+ value: "",
+ isActive: false,
+ maxTriggersPerSession: 0,
+ createdAt: undefined,
+ updatedAt: undefined,
+ };
+}
+
+export const PopupAd: MessageFns = {
+ encode(message: PopupAd, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
+ if (message.id !== undefined && message.id !== "") {
+ writer.uint32(10).string(message.id);
+ }
+ if (message.type !== undefined && message.type !== "") {
+ writer.uint32(18).string(message.type);
+ }
+ if (message.label !== undefined && message.label !== "") {
+ writer.uint32(26).string(message.label);
+ }
+ if (message.value !== undefined && message.value !== "") {
+ writer.uint32(34).string(message.value);
+ }
+ if (message.isActive !== undefined && message.isActive !== false) {
+ writer.uint32(40).bool(message.isActive);
+ }
+ if (message.maxTriggersPerSession !== undefined && message.maxTriggersPerSession !== 0) {
+ writer.uint32(48).int32(message.maxTriggersPerSession);
+ }
+ if (message.createdAt !== undefined) {
+ Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(58).fork()).join();
+ }
+ if (message.updatedAt !== undefined) {
+ Timestamp.encode(toTimestamp(message.updatedAt), writer.uint32(66).fork()).join();
+ }
+ return writer;
+ },
+
+ decode(input: BinaryReader | Uint8Array, length?: number): PopupAd {
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
+ const end = length === undefined ? reader.len : reader.pos + length;
+ const message = createBasePopupAd();
+ while (reader.pos < end) {
+ const tag = reader.uint32();
+ switch (tag >>> 3) {
+ case 1: {
+ if (tag !== 10) {
+ break;
+ }
+
+ message.id = reader.string();
+ continue;
+ }
+ case 2: {
+ if (tag !== 18) {
+ break;
+ }
+
+ message.type = reader.string();
+ continue;
+ }
+ case 3: {
+ if (tag !== 26) {
+ break;
+ }
+
+ message.label = reader.string();
+ continue;
+ }
+ case 4: {
+ if (tag !== 34) {
+ break;
+ }
+
+ message.value = reader.string();
+ continue;
+ }
+ case 5: {
+ if (tag !== 40) {
+ break;
+ }
+
+ message.isActive = reader.bool();
+ continue;
+ }
+ case 6: {
+ if (tag !== 48) {
+ break;
+ }
+
+ message.maxTriggersPerSession = reader.int32();
+ continue;
+ }
+ case 7: {
+ if (tag !== 58) {
+ break;
+ }
+
+ message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
+ continue;
+ }
+ case 8: {
+ if (tag !== 66) {
+ break;
+ }
+
+ message.updatedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
+ continue;
+ }
+ }
+ if ((tag & 7) === 4 || tag === 0) {
+ break;
+ }
+ reader.skip(tag & 7);
+ }
+ return message;
+ },
+
+ fromJSON(object: any): PopupAd {
+ return {
+ id: isSet(object.id) ? globalThis.String(object.id) : "",
+ type: isSet(object.type) ? globalThis.String(object.type) : "",
+ label: isSet(object.label) ? globalThis.String(object.label) : "",
+ value: isSet(object.value) ? globalThis.String(object.value) : "",
+ isActive: isSet(object.isActive)
+ ? globalThis.Boolean(object.isActive)
+ : isSet(object.is_active)
+ ? globalThis.Boolean(object.is_active)
+ : false,
+ maxTriggersPerSession: isSet(object.maxTriggersPerSession)
+ ? globalThis.Number(object.maxTriggersPerSession)
+ : isSet(object.max_triggers_per_session)
+ ? globalThis.Number(object.max_triggers_per_session)
+ : 0,
+ createdAt: isSet(object.createdAt)
+ ? globalThis.String(object.createdAt)
+ : isSet(object.created_at)
+ ? globalThis.String(object.created_at)
+ : undefined,
+ updatedAt: isSet(object.updatedAt)
+ ? globalThis.String(object.updatedAt)
+ : isSet(object.updated_at)
+ ? globalThis.String(object.updated_at)
+ : undefined,
+ };
+ },
+
+ toJSON(message: PopupAd): unknown {
+ const obj: any = {};
+ if (message.id !== undefined && message.id !== "") {
+ obj.id = message.id;
+ }
+ if (message.type !== undefined && message.type !== "") {
+ obj.type = message.type;
+ }
+ if (message.label !== undefined && message.label !== "") {
+ obj.label = message.label;
+ }
+ if (message.value !== undefined && message.value !== "") {
+ obj.value = message.value;
+ }
+ if (message.isActive !== undefined && message.isActive !== false) {
+ obj.isActive = message.isActive;
+ }
+ if (message.maxTriggersPerSession !== undefined && message.maxTriggersPerSession !== 0) {
+ obj.maxTriggersPerSession = Math.round(message.maxTriggersPerSession);
+ }
+ if (message.createdAt !== undefined) {
+ obj.createdAt = message.createdAt;
+ }
+ if (message.updatedAt !== undefined) {
+ obj.updatedAt = message.updatedAt;
+ }
+ return obj;
+ },
+
+ create, I>>(base?: I): PopupAd {
+ return PopupAd.fromPartial(base ?? ({} as any));
+ },
+ fromPartial, I>>(object: I): PopupAd {
+ const message = createBasePopupAd();
+ message.id = object.id ?? "";
+ message.type = object.type ?? "";
+ message.label = object.label ?? "";
+ message.value = object.value ?? "";
+ message.isActive = object.isActive ?? false;
+ message.maxTriggersPerSession = object.maxTriggersPerSession ?? 0;
+ message.createdAt = object.createdAt ?? undefined;
+ message.updatedAt = object.updatedAt ?? undefined;
+ return message;
+ },
+};
+
function createBasePlayerConfig(): PlayerConfig {
return {
id: "",
@@ -6341,6 +6562,245 @@ export const AdminAdTemplate: MessageFns = {
},
};
+function createBaseAdminPopupAd(): AdminPopupAd {
+ return {
+ id: "",
+ userId: "",
+ type: "",
+ label: "",
+ value: "",
+ isActive: false,
+ maxTriggersPerSession: 0,
+ ownerEmail: undefined,
+ createdAt: undefined,
+ updatedAt: undefined,
+ };
+}
+
+export const AdminPopupAd: MessageFns = {
+ encode(message: AdminPopupAd, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
+ if (message.id !== undefined && message.id !== "") {
+ writer.uint32(10).string(message.id);
+ }
+ if (message.userId !== undefined && message.userId !== "") {
+ writer.uint32(18).string(message.userId);
+ }
+ if (message.type !== undefined && message.type !== "") {
+ writer.uint32(26).string(message.type);
+ }
+ if (message.label !== undefined && message.label !== "") {
+ writer.uint32(34).string(message.label);
+ }
+ if (message.value !== undefined && message.value !== "") {
+ writer.uint32(42).string(message.value);
+ }
+ if (message.isActive !== undefined && message.isActive !== false) {
+ writer.uint32(48).bool(message.isActive);
+ }
+ if (message.maxTriggersPerSession !== undefined && message.maxTriggersPerSession !== 0) {
+ writer.uint32(56).int32(message.maxTriggersPerSession);
+ }
+ if (message.ownerEmail !== undefined) {
+ writer.uint32(66).string(message.ownerEmail);
+ }
+ if (message.createdAt !== undefined) {
+ Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(74).fork()).join();
+ }
+ if (message.updatedAt !== undefined) {
+ Timestamp.encode(toTimestamp(message.updatedAt), writer.uint32(82).fork()).join();
+ }
+ return writer;
+ },
+
+ decode(input: BinaryReader | Uint8Array, length?: number): AdminPopupAd {
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
+ const end = length === undefined ? reader.len : reader.pos + length;
+ const message = createBaseAdminPopupAd();
+ while (reader.pos < end) {
+ const tag = reader.uint32();
+ switch (tag >>> 3) {
+ case 1: {
+ if (tag !== 10) {
+ break;
+ }
+
+ message.id = reader.string();
+ continue;
+ }
+ case 2: {
+ if (tag !== 18) {
+ break;
+ }
+
+ message.userId = reader.string();
+ continue;
+ }
+ case 3: {
+ if (tag !== 26) {
+ break;
+ }
+
+ message.type = reader.string();
+ continue;
+ }
+ case 4: {
+ if (tag !== 34) {
+ break;
+ }
+
+ message.label = reader.string();
+ continue;
+ }
+ case 5: {
+ if (tag !== 42) {
+ break;
+ }
+
+ message.value = reader.string();
+ continue;
+ }
+ case 6: {
+ if (tag !== 48) {
+ break;
+ }
+
+ message.isActive = reader.bool();
+ continue;
+ }
+ case 7: {
+ if (tag !== 56) {
+ break;
+ }
+
+ message.maxTriggersPerSession = reader.int32();
+ continue;
+ }
+ case 8: {
+ if (tag !== 66) {
+ break;
+ }
+
+ message.ownerEmail = reader.string();
+ continue;
+ }
+ case 9: {
+ if (tag !== 74) {
+ break;
+ }
+
+ message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
+ continue;
+ }
+ case 10: {
+ if (tag !== 82) {
+ break;
+ }
+
+ message.updatedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
+ continue;
+ }
+ }
+ if ((tag & 7) === 4 || tag === 0) {
+ break;
+ }
+ reader.skip(tag & 7);
+ }
+ return message;
+ },
+
+ fromJSON(object: any): AdminPopupAd {
+ return {
+ id: isSet(object.id) ? globalThis.String(object.id) : "",
+ userId: isSet(object.userId)
+ ? globalThis.String(object.userId)
+ : isSet(object.user_id)
+ ? globalThis.String(object.user_id)
+ : "",
+ type: isSet(object.type) ? globalThis.String(object.type) : "",
+ label: isSet(object.label) ? globalThis.String(object.label) : "",
+ value: isSet(object.value) ? globalThis.String(object.value) : "",
+ isActive: isSet(object.isActive)
+ ? globalThis.Boolean(object.isActive)
+ : isSet(object.is_active)
+ ? globalThis.Boolean(object.is_active)
+ : false,
+ maxTriggersPerSession: isSet(object.maxTriggersPerSession)
+ ? globalThis.Number(object.maxTriggersPerSession)
+ : isSet(object.max_triggers_per_session)
+ ? globalThis.Number(object.max_triggers_per_session)
+ : 0,
+ ownerEmail: isSet(object.ownerEmail)
+ ? globalThis.String(object.ownerEmail)
+ : isSet(object.owner_email)
+ ? globalThis.String(object.owner_email)
+ : undefined,
+ createdAt: isSet(object.createdAt)
+ ? globalThis.String(object.createdAt)
+ : isSet(object.created_at)
+ ? globalThis.String(object.created_at)
+ : undefined,
+ updatedAt: isSet(object.updatedAt)
+ ? globalThis.String(object.updatedAt)
+ : isSet(object.updated_at)
+ ? globalThis.String(object.updated_at)
+ : undefined,
+ };
+ },
+
+ toJSON(message: AdminPopupAd): unknown {
+ const obj: any = {};
+ if (message.id !== undefined && message.id !== "") {
+ obj.id = message.id;
+ }
+ if (message.userId !== undefined && message.userId !== "") {
+ obj.userId = message.userId;
+ }
+ if (message.type !== undefined && message.type !== "") {
+ obj.type = message.type;
+ }
+ if (message.label !== undefined && message.label !== "") {
+ obj.label = message.label;
+ }
+ if (message.value !== undefined && message.value !== "") {
+ obj.value = message.value;
+ }
+ if (message.isActive !== undefined && message.isActive !== false) {
+ obj.isActive = message.isActive;
+ }
+ if (message.maxTriggersPerSession !== undefined && message.maxTriggersPerSession !== 0) {
+ obj.maxTriggersPerSession = Math.round(message.maxTriggersPerSession);
+ }
+ if (message.ownerEmail !== undefined) {
+ obj.ownerEmail = message.ownerEmail;
+ }
+ if (message.createdAt !== undefined) {
+ obj.createdAt = message.createdAt;
+ }
+ if (message.updatedAt !== undefined) {
+ obj.updatedAt = message.updatedAt;
+ }
+ return obj;
+ },
+
+ create, I>>(base?: I): AdminPopupAd {
+ return AdminPopupAd.fromPartial(base ?? ({} as any));
+ },
+ fromPartial, I>>(object: I): AdminPopupAd {
+ const message = createBaseAdminPopupAd();
+ message.id = object.id ?? "";
+ message.userId = object.userId ?? "";
+ message.type = object.type ?? "";
+ message.label = object.label ?? "";
+ message.value = object.value ?? "";
+ message.isActive = object.isActive ?? false;
+ message.maxTriggersPerSession = object.maxTriggersPerSession ?? 0;
+ message.ownerEmail = object.ownerEmail ?? undefined;
+ message.createdAt = object.createdAt ?? undefined;
+ message.updatedAt = object.updatedAt ?? undefined;
+ return message;
+ },
+};
+
function createBaseAdminJob(): AdminJob {
return {
id: "",
diff --git a/src/server/routes/rpc/admin.ts b/src/server/routes/rpc/admin.ts
index 1a97940..ea3fb44 100644
--- a/src/server/routes/rpc/admin.ts
+++ b/src/server/routes/rpc/admin.ts
@@ -307,6 +307,58 @@ export const adminMethods = {
const metadata = context.get("grpcMetadata");
return await adminClient.deleteAdminAdTemplate(data, metadata);
}),
+ listAdminPopupAds: validateFn(
+ z.object({
+ page: z.number().int().min(1).optional(),
+ limit: z.number().int().min(1).max(100).optional(),
+ userId: optionalTrimmed(),
+ search: optionalTrimmed(),
+ }).optional().default({}),
+ )(async (data) => {
+ const context = getContext();
+ const adminClient = context.get("adminClient");
+ const metadata = context.get("grpcMetadata");
+ return await adminClient.listAdminPopupAds(data, metadata);
+ }),
+ createAdminPopupAd: validateFn(
+ z.object({
+ userId: z.string().trim().min(1),
+ type: z.enum(['url', 'script']),
+ label: z.string().trim().min(1),
+ value: z.string().trim().min(1),
+ isActive: z.boolean().optional(),
+ maxTriggersPerSession: z.number().int().min(1).optional(),
+ }),
+ )(async (data) => {
+ const context = getContext();
+ const adminClient = context.get("adminClient");
+ const metadata = context.get("grpcMetadata");
+ return await adminClient.createAdminPopupAd(data, metadata);
+ }),
+ updateAdminPopupAd: validateFn(
+ z.object({
+ id: z.string().trim().min(1),
+ userId: z.string().trim().min(1),
+ type: z.enum(['url', 'script']),
+ label: z.string().trim().min(1),
+ value: z.string().trim().min(1),
+ isActive: z.boolean().optional(),
+ maxTriggersPerSession: z.number().int().min(1).optional(),
+ }),
+ )(async (data) => {
+ const context = getContext();
+ const adminClient = context.get("adminClient");
+ const metadata = context.get("grpcMetadata");
+ return await adminClient.updateAdminPopupAd(data, metadata);
+ }),
+ deleteAdminPopupAd: validateFn(
+ z.object({ id: z.string().trim().min(1) }),
+ )(async (data) => {
+ const context = getContext();
+ const adminClient = context.get("adminClient");
+ const metadata = context.get("grpcMetadata");
+ return await adminClient.deleteAdminPopupAd(data, metadata);
+ }),
listAdminPlayerConfigs: validateFn(
z.object({
page: z.number().int().min(1).optional(),
diff --git a/src/server/routes/rpc/me.ts b/src/server/routes/rpc/me.ts
index dc756ad..3a72e70 100644
--- a/src/server/routes/rpc/me.ts
+++ b/src/server/routes/rpc/me.ts
@@ -139,6 +139,60 @@ export const meMethods = {
const metadata = context.get("grpcMetadata");
return await adTemplatesClient.deleteAdTemplate(data, metadata);
}),
+ listPopupAds: validateFn(
+ z.object({
+ page: z.number().int().min(1).optional(),
+ limit: z.number().int().min(1).max(100).optional(),
+ }).optional().default({}),
+ )(async (data) => {
+ const context = getContext();
+ const popupAdsClient = context.get("popupAdsClient");
+ const metadata = context.get("grpcMetadata");
+ return await popupAdsClient.listPopupAds(data, metadata);
+ }),
+ createPopupAd: validateFn(
+ z.object({
+ type: z.enum(['url', 'script']),
+ label: z.string().trim().min(1),
+ value: z.string().trim().min(1),
+ isActive: z.boolean().optional(),
+ maxTriggersPerSession: z.number().int().min(1).optional(),
+ }),
+ )(async (data) => {
+ const context = getContext();
+ const popupAdsClient = context.get("popupAdsClient");
+ const metadata = context.get("grpcMetadata");
+ return await popupAdsClient.createPopupAd(data, metadata);
+ }),
+ updatePopupAd: validateFn(
+ z.object({
+ id: z.string().trim().min(1),
+ type: z.enum(['url', 'script']),
+ label: z.string().trim().min(1),
+ value: z.string().trim().min(1),
+ isActive: z.boolean().optional(),
+ maxTriggersPerSession: z.number().int().min(1).optional(),
+ }),
+ )(async (data) => {
+ const context = getContext();
+ const popupAdsClient = context.get("popupAdsClient");
+ const metadata = context.get("grpcMetadata");
+ return await popupAdsClient.updatePopupAd(data, metadata);
+ }),
+ deletePopupAd: validateFn(
+ z.object({ id: z.string().trim().min(1) }),
+ )(async (data) => {
+ const context = getContext();
+ const popupAdsClient = context.get("popupAdsClient");
+ const metadata = context.get("grpcMetadata");
+ return await popupAdsClient.deletePopupAd(data, metadata);
+ }),
+ getActivePopupAd: async () => {
+ const context = getContext();
+ const popupAdsClient = context.get("popupAdsClient");
+ const metadata = context.get("grpcMetadata");
+ return await popupAdsClient.getActivePopupAd({}, metadata);
+ },
listPlayerConfigs: async () => {
const context = getContext();
const playerConfigsClient = context.get("playerConfigsClient");
diff --git a/src/server/services/grpcClient.ts b/src/server/services/grpcClient.ts
index f2d92b6..afef6bb 100644
--- a/src/server/services/grpcClient.ts
+++ b/src/server/services/grpcClient.ts
@@ -17,10 +17,12 @@ import {
DomainsClient,
PlayerConfigsClient,
PlansClient,
+ PopupAdsClient,
type AdTemplatesClient as AdTemplatesClientType,
type DomainsClient as DomainsClientType,
type PlayerConfigsClient as PlayerConfigsClientType,
type PlansClient as PlansClientType,
+ type PopupAdsClient as PopupAdsClientType,
} from "@/server/api/proto/app/v1/catalog";
import {
PaymentsClient,
@@ -41,6 +43,7 @@ declare module "hono" {
authClient: PromisifiedClient;
adminClient: PromisifiedClient;
adTemplatesClient: PromisifiedClient;
+ popupAdsClient: PromisifiedClient;
videosClient: PromisifiedClient;
domainsClient: PromisifiedClient;
playerConfigsClient: PromisifiedClient;
@@ -145,6 +148,14 @@ export const getAdTemplatesClient = () => {
return context.get("adTemplatesClient");
};
+export const getPopupAdsClient = () => {
+ const context = tryGetContext();
+ if (!context) {
+ throw new Error("No context available to get PopupAdsClient");
+ }
+ return context.get("popupAdsClient");
+};
+
export const getVideosClient = () => {
const context = tryGetContext();
if (!context) {
@@ -205,6 +216,7 @@ export const setupServices = (app: Hono) => {
const authClient = new AuthClient(grpcAddress(), creds);
const adminClient = new AdminClient(grpcAddress(), creds);
const adTemplatesClient = new AdTemplatesClient(grpcAddress(), creds);
+ const popupAdsClient = new PopupAdsClient(grpcAddress(), creds);
const videosClient = new VideosClient(grpcAddress(), creds);
const domainsClient = new DomainsClient(grpcAddress(), creds);
const playerConfigsClient = new PlayerConfigsClient(grpcAddress(), creds);
@@ -216,6 +228,7 @@ export const setupServices = (app: Hono) => {
c.set("authClient", promisifyClient(authClient));
c.set("adminClient", promisifyClient(adminClient));
c.set("adTemplatesClient", promisifyClient(adTemplatesClient));
+ c.set("popupAdsClient", promisifyClient(popupAdsClient));
c.set("videosClient", promisifyClient(videosClient));
c.set("domainsClient", promisifyClient(domainsClient));
c.set("playerConfigsClient", promisifyClient(playerConfigsClient));
diff --git a/src/stores/auth.ts b/src/stores/auth.ts
index 3649f04..1848f8d 100644
--- a/src/stores/auth.ts
+++ b/src/stores/auth.ts
@@ -5,6 +5,7 @@ import { useTranslation } from "i18next-vue";
import { defineStore } from "pinia";
import { computed, ref, watch } from "vue";
import { useRouter } from "vue-router";
+import { useNotifications } from "@/composables/useNotifications";
type ProfileUpdatePayload = {
username?: string;
@@ -21,6 +22,7 @@ type AuthUserPayload = User & {
};
const mqttBrokerUrl = "wss://mqtt-dashboard.com:8884/mqtt";
+const userNotificationTopic = (userId: string) => ["picpic", "notifications", userId].join("/");
const normalizeUser = (user: User | null): AuthUserPayload | null => {
if (!user) return null;
@@ -38,6 +40,7 @@ export const useAuthStore = defineStore("auth", () => {
const user = ref(null);
const router = useRouter();
const { t, i18next } = useTranslation();
+ const notificationStore = useNotifications();
const loading = ref(false);
const error = ref(null);
const initialized = ref(false);
@@ -83,13 +86,14 @@ export const useAuthStore = defineStore("auth", () => {
mqttClient = new TinyMqttClient(
mqttBrokerUrl,
- [["ecos1231231", userId, "#"].join("/")],
+ [userNotificationTopic(userId)],
(topic, message) => {
- console.log(`Tín hiệu nhận được [${topic}]:`, message);
+ notificationStore.ingestRealtimeNotification(message);
},
);
mqttClient.connect();
},
+ { immediate: true },
);
watch(() => user.value?.language, (lng) => i18next.changeLanguage(lng));
diff --git a/tsconfig.json b/tsconfig.json
index 9b085be..d2fd5f3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -21,5 +21,8 @@
"include": [
"src/**/*.ts",
"src/**/*.tsx",
- "src/**/*.vue" ]
+ "src/**/*.vue",
+ "auto-imports.d.ts",
+ "components.d.ts"
+ ]
}
\ No newline at end of file