✅ base implementation done, now onto mobile app
This commit is contained in:
203
apps/main/src/lib/domains/mobile/mobile.vm.svelte.ts
Normal file
203
apps/main/src/lib/domains/mobile/mobile.vm.svelte.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import type {
|
||||
MobileDevice,
|
||||
MobileDeviceDetail,
|
||||
MobileMediaAsset,
|
||||
MobileSMS,
|
||||
} from "@pkg/logic/domains/mobile/data";
|
||||
import {
|
||||
getDeviceDetailSQ,
|
||||
getDeviceMediaSQ,
|
||||
getDeviceSmsSQ,
|
||||
getDevicesSQ,
|
||||
} from "./mobile.remote";
|
||||
import { toast } from "svelte-sonner";
|
||||
|
||||
class MobileViewModel {
|
||||
devices = $state([] as MobileDevice[]);
|
||||
devicesLoading = $state(false);
|
||||
devicesSearch = $state("");
|
||||
devicesPage = $state(1);
|
||||
devicesPageSize = $state(25);
|
||||
devicesTotal = $state(0);
|
||||
|
||||
selectedDeviceDetail = $state(null as MobileDeviceDetail | null);
|
||||
deviceDetailLoading = $state(false);
|
||||
|
||||
sms = $state([] as MobileSMS[]);
|
||||
smsLoading = $state(false);
|
||||
smsPage = $state(1);
|
||||
smsPageSize = $state(25);
|
||||
smsTotal = $state(0);
|
||||
|
||||
media = $state([] as MobileMediaAsset[]);
|
||||
mediaLoading = $state(false);
|
||||
mediaPage = $state(1);
|
||||
mediaPageSize = $state(25);
|
||||
mediaTotal = $state(0);
|
||||
|
||||
private devicesPollTimer: ReturnType<typeof setInterval> | null = null;
|
||||
private smsPollTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
async fetchDevices() {
|
||||
this.devicesLoading = true;
|
||||
try {
|
||||
const result = await getDevicesSQ({
|
||||
search: this.devicesSearch || undefined,
|
||||
pagination: {
|
||||
page: this.devicesPage,
|
||||
pageSize: this.devicesPageSize,
|
||||
sortBy: "lastPingAt",
|
||||
sortOrder: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
if (result?.error || !result?.data) {
|
||||
toast.error(result?.error?.message || "Failed to load devices", {
|
||||
description: result?.error?.description || "Please try again later",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.devices = result.data.data as MobileDevice[];
|
||||
this.devicesTotal = result.data.total;
|
||||
} catch (error) {
|
||||
toast.error("Failed to load devices", {
|
||||
description:
|
||||
error instanceof Error ? error.message : "Please try again later",
|
||||
});
|
||||
} finally {
|
||||
this.devicesLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchDeviceDetail(deviceId: number) {
|
||||
this.deviceDetailLoading = true;
|
||||
try {
|
||||
const result = await getDeviceDetailSQ({ deviceId });
|
||||
if (result?.error || !result?.data) {
|
||||
toast.error(result?.error?.message || "Failed to load device details", {
|
||||
description: result?.error?.description || "Please try again later",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.selectedDeviceDetail = result.data as MobileDeviceDetail;
|
||||
} catch (error) {
|
||||
toast.error("Failed to load device details", {
|
||||
description:
|
||||
error instanceof Error ? error.message : "Please try again later",
|
||||
});
|
||||
} finally {
|
||||
this.deviceDetailLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchSMS(deviceId: number) {
|
||||
this.smsLoading = true;
|
||||
try {
|
||||
const result = await getDeviceSmsSQ({
|
||||
deviceId,
|
||||
pagination: {
|
||||
page: this.smsPage,
|
||||
pageSize: this.smsPageSize,
|
||||
sortBy: "sentAt",
|
||||
sortOrder: "desc",
|
||||
},
|
||||
});
|
||||
if (result?.error || !result?.data) {
|
||||
toast.error(result?.error?.message || "Failed to load SMS", {
|
||||
description: result?.error?.description || "Please try again later",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.sms = result.data.data as MobileSMS[];
|
||||
this.smsTotal = result.data.total;
|
||||
} catch (error) {
|
||||
toast.error("Failed to load SMS", {
|
||||
description:
|
||||
error instanceof Error ? error.message : "Please try again later",
|
||||
});
|
||||
} finally {
|
||||
this.smsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchMedia(deviceId: number) {
|
||||
this.mediaLoading = true;
|
||||
try {
|
||||
const result = await getDeviceMediaSQ({
|
||||
deviceId,
|
||||
pagination: {
|
||||
page: this.mediaPage,
|
||||
pageSize: this.mediaPageSize,
|
||||
sortBy: "createdAt",
|
||||
sortOrder: "desc",
|
||||
},
|
||||
});
|
||||
if (result?.error || !result?.data) {
|
||||
toast.error(result?.error?.message || "Failed to load media assets", {
|
||||
description: result?.error?.description || "Please try again later",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.media = result.data.data as MobileMediaAsset[];
|
||||
this.mediaTotal = result.data.total;
|
||||
} catch (error) {
|
||||
toast.error("Failed to load media assets", {
|
||||
description:
|
||||
error instanceof Error ? error.message : "Please try again later",
|
||||
});
|
||||
} finally {
|
||||
this.mediaLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
startDevicesPolling(intervalMs = 5000) {
|
||||
this.stopDevicesPolling();
|
||||
this.devicesPollTimer = setInterval(() => {
|
||||
this.fetchDevices();
|
||||
}, intervalMs);
|
||||
}
|
||||
|
||||
stopDevicesPolling() {
|
||||
if (this.devicesPollTimer) {
|
||||
clearInterval(this.devicesPollTimer);
|
||||
this.devicesPollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
startSmsPolling(deviceId: number, intervalMs = 5000) {
|
||||
this.stopSmsPolling();
|
||||
this.smsPollTimer = setInterval(() => {
|
||||
this.fetchSMS(deviceId);
|
||||
}, intervalMs);
|
||||
}
|
||||
|
||||
stopSmsPolling() {
|
||||
if (this.smsPollTimer) {
|
||||
clearInterval(this.smsPollTimer);
|
||||
this.smsPollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
formatLastPing(lastPingAt: Date | null | undefined) {
|
||||
if (!lastPingAt) {
|
||||
return "Never";
|
||||
}
|
||||
const pingDate =
|
||||
lastPingAt instanceof Date ? lastPingAt : new Date(lastPingAt);
|
||||
const diffSeconds = Math.floor((Date.now() - pingDate.getTime()) / 1000);
|
||||
|
||||
if (diffSeconds < 60) {
|
||||
return `${diffSeconds}s ago`;
|
||||
}
|
||||
if (diffSeconds < 3600) {
|
||||
return `${Math.floor(diffSeconds / 60)}m ago`;
|
||||
}
|
||||
if (diffSeconds < 86400) {
|
||||
return `${Math.floor(diffSeconds / 3600)}h ago`;
|
||||
}
|
||||
return pingDate.toLocaleString();
|
||||
}
|
||||
}
|
||||
|
||||
export const mobileVM = new MobileViewModel();
|
||||
Reference in New Issue
Block a user