Move communication workbench to use resource store
Because it's getting better all the time, including by adding the ability to get new resources when they get created over SSE.
This commit is contained in:
parent
72eef554ea
commit
b4ae9e5a95
6 changed files with 50 additions and 116 deletions
|
|
@ -174,7 +174,7 @@ interface Emits {
|
||||||
}
|
}
|
||||||
interface Props {
|
interface Props {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
selectedCommunication: Communication | null;
|
selectedCommunication: Communication | undefined;
|
||||||
selectedReport: PublicReport | undefined;
|
selectedReport: PublicReport | undefined;
|
||||||
}
|
}
|
||||||
const emit = defineEmits<Emits>();
|
const emit = defineEmits<Emits>();
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ interface Props {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
mapBounds?: LngLatBounds;
|
mapBounds?: LngLatBounds;
|
||||||
mapMarkers: Marker[];
|
mapMarkers: Marker[];
|
||||||
selectedCommunication: Communication | null;
|
selectedCommunication: Communication | undefined;
|
||||||
selectedReport: PublicReport | undefined;
|
selectedReport: PublicReport | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ import ListCardCommunication from "@/components/ListCardCommunication.vue";
|
||||||
import { Communication, LogEntry, PublicReport } from "@/type/api";
|
import { Communication, LogEntry, PublicReport } from "@/type/api";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
all: Communication[] | null;
|
all: Communication[] | undefined;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
selectedID?: string;
|
selectedID?: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
import { defineStore } from "pinia";
|
|
||||||
import { ref } from "vue";
|
|
||||||
|
|
||||||
import { apiClient } from "@/client";
|
|
||||||
import { SSEManager, SSEMessageResource } from "@/SSEManager";
|
|
||||||
import { useSessionStore } from "@/store/session";
|
|
||||||
import { Communication, CommunicationDTO } from "@/type/api";
|
|
||||||
|
|
||||||
export const useCommunicationStore = defineStore("communication", () => {
|
|
||||||
// State
|
|
||||||
const all = ref<Communication[] | null>(null);
|
|
||||||
const loading = ref(false);
|
|
||||||
const error = ref(null);
|
|
||||||
|
|
||||||
// Subscription
|
|
||||||
SSEManager.subscribe((msg: SSEMessageResource) => {
|
|
||||||
if (
|
|
||||||
msg.resource.startsWith("sync:communication") &&
|
|
||||||
msg.type == "updated"
|
|
||||||
) {
|
|
||||||
fetchOne(msg.uri);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Actions
|
|
||||||
async function fetchAll(): Promise<Communication[]> {
|
|
||||||
const session = useSessionStore();
|
|
||||||
if (session.urls == null) {
|
|
||||||
throw new Error("can't fetch without user URL data");
|
|
||||||
}
|
|
||||||
|
|
||||||
loading.value = true;
|
|
||||||
error.value = null;
|
|
||||||
try {
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
params.append("sort", "-created");
|
|
||||||
//if (typeFilter.value) params.append("type", typeFilter.value);
|
|
||||||
|
|
||||||
const url = `${session.urls.api.communication}?${params}`;
|
|
||||||
const data = (await apiClient.JSONGet(url)) as CommunicationDTO[];
|
|
||||||
|
|
||||||
all.value = data.map((c: CommunicationDTO) => Communication.fromJSON(c));
|
|
||||||
return all.value;
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error loading communications:", err);
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function fetchOne(uri: string) {
|
|
||||||
const data = (await apiClient.JSONGet(uri)) as CommunicationDTO;
|
|
||||||
if (!all.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < all.value.length; i++) {
|
|
||||||
const c = all.value[i];
|
|
||||||
if (c.uri == data.uri) {
|
|
||||||
all.value[i] = Communication.fromJSON(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
// State
|
|
||||||
all,
|
|
||||||
loading,
|
|
||||||
// Actions
|
|
||||||
fetchAll,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
@ -31,8 +31,12 @@ function createResourceStore<dto, full extends uriHaver>(
|
||||||
|
|
||||||
// Subscription
|
// Subscription
|
||||||
SSEManager.subscribe((msg: SSEMessageResource) => {
|
SSEManager.subscribe((msg: SSEMessageResource) => {
|
||||||
if (msg.resource.startsWith(resource_name) && msg.type == "updated") {
|
if (msg.resource.startsWith(resource_name)) {
|
||||||
fetchByURI(msg.uri);
|
if (msg.type == "created") {
|
||||||
|
fetchByURI(msg.uri);
|
||||||
|
} else if (msg.type == "updated") {
|
||||||
|
fetchByURI(msg.uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -92,10 +96,22 @@ function createResourceStore<dto, full extends uriHaver>(
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function getAll(): full[] | null {
|
||||||
|
if (_resourceFetchAll) {
|
||||||
|
return Array.from(_resourceByURI.value.values());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function hasAll(): boolean {
|
||||||
|
return !!_resourceFetchAll.value;
|
||||||
|
}
|
||||||
function loadingAll(): boolean {
|
function loadingAll(): boolean {
|
||||||
return !!_resourceFetchAll.value;
|
return !!_resourceFetchAll.value;
|
||||||
}
|
}
|
||||||
function loadingURI(uri: string): boolean {
|
function loadingURI(uri: string | undefined): boolean {
|
||||||
|
if (uri === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return !!_resourceFetchByURI.value.get(uri);
|
return !!_resourceFetchByURI.value.get(uri);
|
||||||
}
|
}
|
||||||
function uriFromID(id: string): string {
|
function uriFromID(id: string): string {
|
||||||
|
|
@ -108,6 +124,8 @@ function createResourceStore<dto, full extends uriHaver>(
|
||||||
fetchAll,
|
fetchAll,
|
||||||
fetchByID,
|
fetchByID,
|
||||||
fetchByURI,
|
fetchByURI,
|
||||||
|
getAll,
|
||||||
|
hasAll,
|
||||||
loadingAll,
|
loadingAll,
|
||||||
loadingURI,
|
loadingURI,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,17 @@
|
||||||
<CommunicationColumnList
|
<CommunicationColumnList
|
||||||
:all="visibleCommunications"
|
:all="visibleCommunications"
|
||||||
@deselect="handleDeselect"
|
@deselect="handleDeselect"
|
||||||
:loading="storeCommunication.loading"
|
:loading="storeResource.communication.loadingAll()"
|
||||||
:selectedID="selectedId"
|
:selectedID="selectedId"
|
||||||
@select="handleSelect"
|
@select="handleSelect"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #center>
|
<template #center>
|
||||||
<CommunicationColumnDetail
|
<CommunicationColumnDetail
|
||||||
:loading="storePublicReport.loading || storeCommunication.loading"
|
:loading="
|
||||||
|
storePublicReport.loading ||
|
||||||
|
storeResource.communication.loadingURI(selectedCommunication?.uri)
|
||||||
|
"
|
||||||
:mapBounds="mapBounds || undefined"
|
:mapBounds="mapBounds || undefined"
|
||||||
:mapMarkers="mapMarkers"
|
:mapMarkers="mapMarkers"
|
||||||
:selectedCommunication="selectedCommunication"
|
:selectedCommunication="selectedCommunication"
|
||||||
|
|
@ -33,7 +36,10 @@
|
||||||
</template>
|
</template>
|
||||||
<template #right>
|
<template #right>
|
||||||
<CommunicationColumnAction
|
<CommunicationColumnAction
|
||||||
:isLoading="storePublicReport.loading || storeCommunication.loading"
|
:isLoading="
|
||||||
|
storePublicReport.loading ||
|
||||||
|
storeResource.communication.loadingURI(selectedCommunication?.uri)
|
||||||
|
"
|
||||||
@markInvalid="markInvalid"
|
@markInvalid="markInvalid"
|
||||||
@markPendingResponse="markPendingResponse"
|
@markPendingResponse="markPendingResponse"
|
||||||
@markPossibleIssue="markPossibleIssue"
|
@markPossibleIssue="markPossibleIssue"
|
||||||
|
|
@ -73,7 +79,7 @@ import ThreeColumn from "@/components/layout/ThreeColumn.vue";
|
||||||
import ToastNotification from "@/components/ToastNotification.vue";
|
import ToastNotification from "@/components/ToastNotification.vue";
|
||||||
import { useQueryParam } from "@/composable/use-query-param";
|
import { useQueryParam } from "@/composable/use-query-param";
|
||||||
import { SSEManager } from "@/SSEManager";
|
import { SSEManager } from "@/SSEManager";
|
||||||
import { useCommunicationStore } from "@/store/communication";
|
import { useStoreResource } from "@/store/resource";
|
||||||
import { useSessionStore } from "@/store/session";
|
import { useSessionStore } from "@/store/session";
|
||||||
import type { Marker } from "@/types";
|
import type { Marker } from "@/types";
|
||||||
import { Bounds, type Communication, PublicReport } from "@/type/api";
|
import { Bounds, type Communication, PublicReport } from "@/type/api";
|
||||||
|
|
@ -88,8 +94,8 @@ const currentImageIndex = ref<number>(0);
|
||||||
const paramCommunication = useQueryParam("communication");
|
const paramCommunication = useQueryParam("communication");
|
||||||
const selectedId = ref<string | undefined>(undefined);
|
const selectedId = ref<string | undefined>(undefined);
|
||||||
const showImageModal = ref(false);
|
const showImageModal = ref(false);
|
||||||
const storeCommunication = useCommunicationStore();
|
|
||||||
const storePublicReport = useStorePublicReport();
|
const storePublicReport = useStorePublicReport();
|
||||||
|
const storeResource = useStoreResource();
|
||||||
const toastMessage = ref("");
|
const toastMessage = ref("");
|
||||||
const toastShow = ref(false);
|
const toastShow = ref(false);
|
||||||
const toastTitle = ref("");
|
const toastTitle = ref("");
|
||||||
|
|
@ -168,16 +174,13 @@ const mapMarkers = computed<Marker[]>((): Marker[] => {
|
||||||
}
|
}
|
||||||
return markers;
|
return markers;
|
||||||
});
|
});
|
||||||
const selectedCommunication = computed<Communication | null>(
|
const selectedCommunication = computedAsync(
|
||||||
(): Communication | null => {
|
async (): Promise<Communication | undefined> => {
|
||||||
if (selectedId.value == undefined) {
|
if (selectedId.value == undefined) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (storeCommunication.all == null) {
|
const all = await storeResource.communication.byAll();
|
||||||
return null;
|
return all.find((c: Communication) => c.id == selectedId.value);
|
||||||
}
|
|
||||||
const result = storeCommunication.all.find((c) => c.id == selectedId.value);
|
|
||||||
return result || null;
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const selectedReport = computedAsync(
|
const selectedReport = computedAsync(
|
||||||
|
|
@ -192,14 +195,14 @@ const selectedReport = computedAsync(
|
||||||
return await storePublicReport.byURI(selectedCommunication.value.source);
|
return await storePublicReport.byURI(selectedCommunication.value.source);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const visibleCommunications = computed((): Communication[] => {
|
const visibleCommunications = computedAsync(
|
||||||
if (!storeCommunication.all) {
|
async (): Promise<Communication[]> => {
|
||||||
return [];
|
const all = await storeResource.communication.byAll();
|
||||||
}
|
return all.filter((c: Communication) => {
|
||||||
return storeCommunication.all.filter((c: Communication) => {
|
return c.status == "new" || c.status == "opened";
|
||||||
return c.status == "new" || c.status == "opened";
|
});
|
||||||
});
|
},
|
||||||
});
|
);
|
||||||
const handleDeselect = (id: string) => {
|
const handleDeselect = (id: string) => {
|
||||||
selectedId.value = undefined;
|
selectedId.value = undefined;
|
||||||
};
|
};
|
||||||
|
|
@ -245,27 +248,9 @@ async function markReport(title: string, status: string) {
|
||||||
`Report Marked ${title}`,
|
`Report Marked ${title}`,
|
||||||
`Report #${selectedCommunication.value.id} has been updated`,
|
`Report #${selectedCommunication.value.id} has been updated`,
|
||||||
);
|
);
|
||||||
removeCurrentFromList();
|
await storeResource.communication.fetchAll();
|
||||||
await storeCommunication.fetchAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCurrentFromList() {
|
|
||||||
if (storeCommunication.all == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const index = storeCommunication.all.findIndex(
|
|
||||||
(c) => c.id === selectedId.value,
|
|
||||||
);
|
|
||||||
if (index > -1) {
|
|
||||||
storeCommunication.all.splice(index, 1);
|
|
||||||
}
|
|
||||||
if (storeCommunication.all.length > 0) {
|
|
||||||
const nextIndex = Math.min(index, storeCommunication.all.length - 1);
|
|
||||||
selectedId.value = storeCommunication.all[nextIndex].id;
|
|
||||||
} else {
|
|
||||||
selectedId.value = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function sendMessage(message: string) {
|
async function sendMessage(message: string) {
|
||||||
if (!message.trim()) return;
|
if (!message.trim()) return;
|
||||||
if (selectedCommunication.value == null) return;
|
if (selectedCommunication.value == null) return;
|
||||||
|
|
@ -306,7 +291,7 @@ function showNotification(title: string, message: string) {
|
||||||
|
|
||||||
// Lifecycle hooks
|
// Lifecycle hooks
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await storeCommunication.fetchAll();
|
await storeResource.communication.fetchAll();
|
||||||
});
|
});
|
||||||
watch(
|
watch(
|
||||||
paramCommunication.value,
|
paramCommunication.value,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue