Skip to content

Commit

Permalink
Event project and asset notification
Browse files Browse the repository at this point in the history
  • Loading branch information
EduardoOliveira committed Mar 12, 2024
1 parent 1603d70 commit 1d875e4
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 35 deletions.
8 changes: 6 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { DashboardProvider } from './dashboard/provider/DashboardProvider.tsx';
import { PrinterWidgetProvider } from './printers/providers/PrinterWidgetProvider.tsx';
import { SSEProvider } from './core/sse/SSEProvider.tsx';
import { SettingsProvider } from './core/settings/settingsProvider.tsx';
import { EventNotifications } from './system/components/event-notifications/EventNotifications.tsx';
import { DiscoveryNotifications } from './system/components/discovery-notifications/DiscoveryNotifications.tsx';
import { NewProjectNotification } from './projects/notifications/new-project-notification/NewProjectNotification.tsx';
import { Notifications } from '@mantine/notifications';

export default function App() {
const [opened, { toggle }] = useDisclosure();
Expand Down Expand Up @@ -41,8 +43,10 @@ export default function App() {
</AppShell>
<ScrollToTop />
<AxiosErrorHandler />
<EventNotifications />
<NewProjectNotification />
<DiscoveryNotifications />
<PrinterWidgetProvider />
<Notifications limit={10} />
</DashboardProvider>
</SSEProvider>
</SettingsProvider>
Expand Down
12 changes: 0 additions & 12 deletions src/assets/components/AssetCardProps.ts

This file was deleted.

10 changes: 5 additions & 5 deletions src/assets/components/asset-card/AssetCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export function AssetCard({ asset, focused, onFocused, onDelete, onChange, view3
return (
<>
{modal && asset.image_id && asset.image_id != "" && <Lightbox
medium={`${settings.localBackend}/projects/${asset.project_uuid}/assets/${asset.image_id}`}
large={`${settings.localBackend}/projects/${asset.project_uuid}/assets/${asset.image_id}`}
medium={`${settings.localBackend}/projects/${asset.project_uuid}/assets/${asset.image_id}/file`}
large={`${settings.localBackend}/projects/${asset.project_uuid}/assets/${asset.image_id}/file`}
hideDownload={true}
onClose={toggleModal}
/>}
Expand All @@ -50,15 +50,15 @@ export function AssetCard({ asset, focused, onFocused, onDelete, onChange, view3
<AspectRatio ratio={16 / 9}>
{asset?.image_id === "" ? (iconMap.get(asset.extension) ?? <IconFile />) :
<Image
src={`${settings.localBackend}/projects/${asset.project_uuid}/assets/${asset.image_id}`}
src={`${settings.localBackend}/projects/${asset.project_uuid}/assets/${asset.image_id}/file`}
alt={asset.name}
/>
}
</AspectRatio>
</Card.Section>

<Text fw={700} className={classes.title} mt="xs" onClick={() => { onFocused() }}>
{asset.name}
{asset.label != "" ? asset.label : asset.name}
</Text>

<LoadingOverlay visible={loading} zIndex={1000} overlayProps={{ blur: 2 }} />
Expand All @@ -80,7 +80,7 @@ export function AssetCard({ asset, focused, onFocused, onDelete, onChange, view3
projectUuid={asset.project_uuid}
id={asset.id}
openDetails={() => { onFocused() }}
downloadURL={`${settings.localBackend}/projects/${asset.project_uuid}/assets/${asset.id}?download=true'`}
downloadURL={`${settings.localBackend}/projects/${asset.project_uuid}/assets/${asset.id}/file?download=true'`}
onDelete={onDelete}
toggleLoad={toggleLoadingCallback}>
<SetAsMain projectUuid={asset.project_uuid} assetId={asset.image_id} onChange={onChange} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type ModelProps = {

function Model({ color, model, projectUuid }: ModelProps) {
const { settings } = useContext(SettingsContext);
const geom = useLoader(STLLoader, `${settings.localBackend}/projects/${projectUuid}/assets/${model.id}`);
const geom = useLoader(STLLoader, `${settings.localBackend}/projects/${projectUuid}/assets/${model.id}/file`);
const meshRef = useRef<THREE.Mesh>(null!)

const [active, setActive] = useState(false)
Expand Down
1 change: 1 addition & 0 deletions src/assets/entities/Assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export interface Asset {
id: string
name: string
label: string
origin: string
project_uuid: string
path: string
Expand Down
3 changes: 3 additions & 0 deletions src/core/sse/SubscriptionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ export const createSubsManager = (url: string): SubscriptionManager => {
return;
}
if (ev.event) {
if (ev.data.name) {
state.evSubs.get(`${ev.event}.${ev.data.name}`)?.forEach(sub => sub.callback(ev.data));
}
if (ev.data.state) {
state.evSubs.get(ev.event)?.forEach(sub => sub.callback(ev.data.state));
} else if (Array.isArray(ev.data)) {
Expand Down
2 changes: 0 additions & 2 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import '@mantine/core/styles/Checkbox.css';
import '@mantine/dropzone/styles.css';
import '@mantine/notifications/styles.css';
import { createTheme, MantineProvider } from '@mantine/core';
import { Notifications } from '@mantine/notifications';
import { createBrowserRouter, RouterProvider } from "react-router-dom";

import { routes as dashboardRoutes } from "./dashboard/routes.tsx";
Expand Down Expand Up @@ -71,7 +70,6 @@ console.log(router);
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<MantineProvider theme={theme} defaultColorScheme="dark">
<Notifications limit={10} />
<RouterProvider router={router} />
</MantineProvider>
</React.StrictMode>
Expand Down
4 changes: 3 additions & 1 deletion src/projects/components/project-page/ProjectPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useContext } from "react";
import { ProjectPageBody } from "./parts/project-page-body/ProjectPageBody.tsx";
import { Header } from "@/core/header/Header.tsx";
import { SettingsContext } from "@/core/settings/settingsContext.ts";
import { Refresher } from "./parts/refresher/Refresher.tsx";


export function ProjectPage() {
Expand All @@ -23,14 +24,15 @@ export function ProjectPage() {
description={project?.description}
tags={project?.tags}
link={project?.external_link}
imagePath={`${settings.localBackend}/projects/${project?.uuid}/assets/${project?.default_image_id}`}
imagePath={`${settings.localBackend}/projects/${project?.uuid}/assets/${project?.default_image_id}/file`}
onTagClick={(t) => navigate(`/projects/list?filter=${JSON.stringify({ tags: [t.value] })}`)}
/>
{error && <p>Error!</p>}
{id && <ProjectPageBody projectUuid={id} project={project} onProjectChange={() => {
console.log("onProjectChange")
refetch()
}} />}
<Refresher projectUUID={id ?? ''} refresh={refetch} />
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Alert, Container, Flex, rem, SimpleGrid, Skeleton, Tabs } from "@mantine/core";
import { Alert, Button, Container, Flex, rem, SimpleGrid, Skeleton, Tabs } from "@mantine/core";
import useAxios from "axios-hooks";
import { Asset, AssetType } from "@/assets/entities/Assets.ts";
import { useListState } from "@mantine/hooks";
import React, { useContext, useEffect, useState } from "react";
import { useContext, useEffect, useState } from "react";
import { ModelDetailPane } from "@/assets/components/model/model-detail-pane/ModelDetailPane.tsx";
import { IconSettings, IconFiles } from "@tabler/icons-react";
import { useNavigate, useSearchParams } from "react-router-dom";
Expand All @@ -11,6 +11,7 @@ import { EditProject } from "./parts/edit-project/EditProject.tsx";
import { Project } from "../../../../entities/Project.ts";
import { SettingsContext } from "@/core/settings/settingsContext.ts";
import { AssetDetails } from "@/assets/components/asset-details/AssetDetails.tsx";
import { Refresher } from "./parts/refresher/Refresher.tsx";
import { AssetCard } from "@/assets/components/asset-card/AssetCard.tsx";

const iconStyle = { width: rem(12), height: rem(12) };
Expand All @@ -25,15 +26,21 @@ export function ProjectPageBody({ projectUuid, project, onProjectChange }: Proje
const { settings } = useContext(SettingsContext);
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [assets, setAssets] = useState<Asset[]>([]);
const [selectedModels, selectedModelsHandlers] = useListState<Asset>([]);
const [selectedAsset, setSelectedAsset] = useState<Asset>();
const [typeFilter, setTypeFilter] = useState<string | null>(searchParams.get('tab'));
const [{ data: assetTypes, loading: tLoading, error: tError }] = useAxios<AssetType[]>(
`${settings.localBackend}/assettypes`
);
const [{ data: assets, loading, error }, refetch] = useAxios<Asset[]>(
const [{ data, loading, error }, refetch] = useAxios<Asset[]>(
`${settings.localBackend}/projects/${projectUuid}/assets`
);
useEffect(() => {
if (data) {
setAssets(data);
}
}, [data]);

useEffect(() => {
if (selectedModels.length == 0) {
Expand Down Expand Up @@ -127,6 +134,7 @@ export function ProjectPageBody({ projectUuid, project, onProjectChange }: Proje
}
</SimpleGrid>
</Container>
<Refresher projectUUID={projectUuid} />
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import SSEContext from "@/core/sse/SSEContext";
import { Anchor } from "@mantine/core";
import { useId } from "@mantine/hooks";
import { notifications } from "@mantine/notifications";
import { useContext, useEffect, useState } from "react";
import { Link } from "react-router-dom";

type RefresherProps = {
projectUUID: string;
}

export function Refresher({ projectUUID }: RefresherProps) {
const subscriberId = useId();
const { connected, subscribe, unsubscribe } = useContext(SSEContext)
const [assetUpdate, setAssetUpdate] = useState({} as any)
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!connected) return;
setAssetUpdate({})
const subscription = {
subscriberId,
provider: `system/events`,
}
subscribe({
...subscription,
event: `system.state.asset.event`,
callback: setAssetUpdate
}).catch(setError);
return () => {
unsubscribe(subscriberId)
}
}, [connected])

useEffect(() => {
console.log(assetUpdate)
if (!assetUpdate.state) return;
if (projectUUID == assetUpdate.state.projectUUID) {
notifications.show({
title: `Assets have changed`,
message: <Anchor component={Link} reloadDocument to={`/projects/${projectUUID}`}>Refresh</Anchor>,
})
}
}, [assetUpdate])
return (<></>)
}
40 changes: 40 additions & 0 deletions src/projects/components/project-page/parts/refresher/Refresher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import SSEContext from "@/core/sse/SSEContext";
import { useId } from "@mantine/hooks";
import { useContext, useEffect, useState } from "react";

type RefresherProps = {
projectUUID: string;
refresh: () => void;
}

export function Refresher({ projectUUID, refresh }: RefresherProps) {
const subscriberId = useId();
const { connected, subscribe, unsubscribe } = useContext(SSEContext)
const [projectUpdate, setProjectUpdate] = useState({} as any)
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!connected) return;
setProjectUpdate({})
const subscription = {
subscriberId,
provider: `system/events`,
}
subscribe({
...subscription,
event: `system.state.project.event`,
callback: setProjectUpdate
}).catch(setError);
return () => {
unsubscribe(subscriberId)
}
}, [connected])

useEffect(() => {
console.log(projectUpdate)
if (!projectUpdate.state) return;
if (projectUpdate.state.projectUUID == projectUUID && projectUpdate.state.type == "update") {
refresh();
}
}, [projectUpdate])
return (<></>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function ProjectCard({ project }: ProjectCardProps) {
<div
className={classes.image}
style={{
backgroundImage: `url(${settings.localBackend}/projects/${project.uuid}/assets/${project.default_image_id})`,
backgroundImage: `url(${settings.localBackend}/projects/${project.uuid}/assets/${project.default_image_id}/file)`,
backgroundPosition: 'center',
}}
/>}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import SSEContext from "@/core/sse/SSEContext";
import { Anchor } from "@mantine/core";
import { useId } from "@mantine/hooks";
import { notifications } from "@mantine/notifications";
import { useContext, useEffect, useState } from "react";
import { Link } from "react-router-dom";

export function EventNotifications() {
export function NewProjectNotification() {
const subscriberId = useId();
const { connected, subscribe, unsubscribe } = useContext(SSEContext)
const [message, setMessage] = useState("")
const [message, setMessage] = useState({} as any)
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!connected) return;
setMessage("")
setMessage({})
const subscription = {
subscriberId,
provider: `system/events`,
}
subscribe({
...subscription,
event: `system.state`,
event: `system.state.project.event`,
callback: setMessage
}).catch(setError);
return () => {
Expand All @@ -27,10 +29,13 @@ export function EventNotifications() {

useEffect(() => {
console.log(message)
notifications.show({
title: message,
message: message,
})
if (!message.state) return;
if (message.state.type == "new") {
notifications.show({
title: `New project found`,
message: <>Go to <Anchor component={Link} to={`/projects/${message.state.projectUUID}`}>{message.state.projectName}</Anchor></>,
})
}
}, [message])
return (<></>)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import SSEContext from "@/core/sse/SSEContext";
import { rem } from "@mantine/core";
import { useId } from "@mantine/hooks";
import { notifications } from "@mantine/notifications";
import { IconCheck } from "@tabler/icons-react";
import { useContext, useEffect, useState } from "react";

export function DiscoveryNotifications() {
const subscriberId = useId();
const { connected, subscribe, unsubscribe } = useContext(SSEContext)
const [id, setId] = useState<string | null>(null);
const [message, setMessage] = useState({} as any)
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!connected) return;
setMessage("")
const subscription = {
subscriberId,
provider: `system/events`,
}
subscribe({
...subscription,
event: `system.state.discovery.scan`,
callback: setMessage
}).catch(setError);
return () => {
unsubscribe(subscriberId)
}
}, [connected])

useEffect(() => {
console.log(message)
if (!message.state) return;
if (message.state.state == "started") {
setId(notifications.show({
loading: true,
title: "New Scan started",
message: "Let's find new projects!",
autoClose: false
}))
} else if (id) {
notifications.update({
id,
color: 'teal',
title: 'Scan finished!',
message: '',
icon: <IconCheck style={{ width: rem(18), height: rem(18) }} />,
loading: false,
autoClose: 2000,
});
}
}, [message])
return (<></>)
}

0 comments on commit 1d875e4

Please sign in to comment.