From 5cf503c48e26e83f7d0ea9e9e746678c9405c824 Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Fri, 11 Nov 2022 14:28:27 +0100 Subject: [PATCH 1/4] Adding new file system syncer --- src/store/files.ts | 90 ++++++++++++++++++++++++++++++++++++++++++++++ src/store/model.ts | 5 ++- 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/store/files.ts diff --git a/src/store/files.ts b/src/store/files.ts new file mode 100644 index 00000000..ecb408c9 --- /dev/null +++ b/src/store/files.ts @@ -0,0 +1,90 @@ +import { action, Action, thunk, Thunk } from 'easy-peasy'; +import localforage from 'localforage' + +localforage.config({ + driver : localforage.INDEXEDDB, + name : 'JupyterLite Storage', + storeName : 'files', // Should be alphanumeric, with underscores. + description : 'some description' +}); + +export interface AtomifyFile { + created: Date + format: string + last_modified: Date + mimetype: string + name: string + path: string + type: string + writeable: boolean + text: () => Promise + writeText: (content: string) => Promise +} + +export interface FilesModel { + files: {[key: string]: AtomifyFile} + setFiles: Action + syncFilesJupyterLite: Thunk +} + +export const filesModel: FilesModel = { + files: {}, + setFiles: action((state, render: {[key: string]: AtomifyFile}) => { + state.files = render + }), + syncFilesJupyterLite: thunk(async (actions, dummy: undefined, {getStoreState}) => { + // @ts-ignore + const state = getStoreState().files as FilesModel + + const newFiles = {...state.files} + let anyChanges = false + + // First find all files from JupyterLite and sync them in + const keys = await localforage.keys() + for (let key of keys) { + const file = await localforage.getItem(key) as any + if (!state.files[key]) { + const dateModified = new Date(file.date_modified) + // Create AtomifyFile object + const newFile: AtomifyFile = { + created: new Date(file.created), + format: file.format, + last_modified: new Date(file.last_modified), + mimetype: file.mimetype, + name: file.name, + path: file.path, + type: file.type, + writeable: file.writeable, + text: async () => { + const file = await localforage.getItem(key) as any + return file.content + }, + writeText: async (content: string) => { + const file = await localforage.getItem(key) as any + + await localforage.setItem(key, { + ...file, + content + }) + } + } + + newFiles[key] = newFile + anyChanges = true + } + } + + // Then remove any files that no longer are there + Object.keys(state.files).forEach(key => { + if (keys.indexOf(key) === -1) { + // This file no longer exists in JupyterLite + delete newFiles[key] + anyChanges = true + } + }) + + if (anyChanges) { + actions.setFiles(newFiles) + } + }) +}; diff --git a/src/store/model.ts b/src/store/model.ts index 5f74c1c0..95ddabaf 100644 --- a/src/store/model.ts +++ b/src/store/model.ts @@ -3,6 +3,7 @@ import { SettingsModel, settingsModel } from './settings' import { SimulationStatusModel, simulationStatusModel } from './simulationstatus' import { ProcessingModel, processingModel } from './processing' import { RenderModel, renderModel } from './render' +import { FilesModel, filesModel } from './files' import { AppModel, appModel } from './app' import { persist } from 'easy-peasy'; @@ -13,6 +14,7 @@ export interface StoreModel { processing: ProcessingModel, app: AppModel, render: RenderModel + files: FilesModel } export const storeModel: StoreModel = { @@ -21,5 +23,6 @@ export const storeModel: StoreModel = { simulationStatus: simulationStatusModel, processing: processingModel, app: appModel, - render: renderModel + render: renderModel, + files: filesModel }; From 512cca724babc745f451ee596e3d5c16ca5e62a9 Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Sat, 12 Nov 2022 15:20:26 +0100 Subject: [PATCH 2/4] WIP: adding simulations --- src/App.tsx | 1 + src/containers/Main.tsx | 4 ++ src/containers/Simulations.tsx | 86 ++++++++++++++++++++++++++++++++++ src/store/app.ts | 2 +- 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/containers/Simulations.tsx diff --git a/src/App.tsx b/src/App.tsx index c5c6f84d..24e7278e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -101,6 +101,7 @@ const App: React.FC = () => { }): [], undefined, selectedFile==null), {type: 'divider'}, newSimulationButton, + getItem('Simulations', 'simulations', ), getItem('Examples', 'examples', ), {type: 'divider'}, runStopButton, diff --git a/src/containers/Main.tsx b/src/containers/Main.tsx index 2b0573ee..71f624e5 100644 --- a/src/containers/Main.tsx +++ b/src/containers/Main.tsx @@ -5,6 +5,7 @@ import Edit from './Edit' import Console from './Console' import Examples from './Examples' import {useStoreActions, useStoreState} from '../hooks' +import Simulations from './Simulations'; const { Content } = Layout; const Main = () => { @@ -32,6 +33,9 @@ const Main = () => { + + + diff --git a/src/containers/Simulations.tsx b/src/containers/Simulations.tsx new file mode 100644 index 00000000..079a890c --- /dev/null +++ b/src/containers/Simulations.tsx @@ -0,0 +1,86 @@ +import {useCallback, useState, useEffect} from 'react' +import { useMeasure } from 'react-use'; +import { Select, Button, Divider } from 'antd'; +import {Simulation} from '../store/simulation' +import {SimulationFile} from '../store/app' +import {useStoreActions, useStoreState} from '../hooks' +import { CaretRightOutlined, EditOutlined } from '@ant-design/icons'; +import { Card, Layout, Skeleton, Row, Col, notification } from 'antd'; +import {track} from '../utils/metrics' +import { AtomifyFile } from '../store/files'; + +const {Option} = Select + +const { Header } = Layout; +const { Meta } = Card; + +const Simulations = () => { + const files = useStoreState(state => state.files.files) + const syncFilesJupyterLite = useStoreActions(actions => actions.files.syncFilesJupyterLite) + const [myRef, { width }] = useMeasure(); + + useEffect(() => { + setInterval(() => { + syncFilesJupyterLite() + }, 1000) + }, []) + + useEffect(() => { + (async () => { + const folders = Object.values(files).filter(file => file.type === "directory") + folders.forEach(folder => { + const filesInFolder = Object.values(files).filter(file => file.type !== "directory" && file.path.startsWith(folder.name)) + let image: AtomifyFile + filesInFolder.forEach(file => { + if (file.mimetype === "image/png") { + image = file + } + }) + }) + + + })() + }, [files]) + + const renderCard = (simulation: AtomifyFile) => { + return ( + , + , + ]} + > + + + )} + + const chunkIt = (array: Simulation[], chunkSize: number) => { + const chunks: Simulation[][] = [] + + for (let i = 0; i < array.length; i += chunkSize) { + const chunk = array.slice(i, i + chunkSize); + chunks.push(chunk) + } + return chunks + } + + + + return ( + <> +
+ Simulations +
+
+

Your simulations

+ + {folders.map(renderCard)} +
+ ) +} +export default Simulations \ No newline at end of file diff --git a/src/store/app.ts b/src/store/app.ts index d58cc28e..1acb5e84 100644 --- a/src/store/app.ts +++ b/src/store/app.ts @@ -25,7 +25,7 @@ export interface AppModel { } export const appModel: AppModel = { - selectedMenu: 'examples', + selectedMenu: 'simulations', setSelectedMenu: action((state, selectedMenu: string) => { state.selectedMenu = selectedMenu }), From 4f42750d3f9a030b32818c2e0bc64e1cde933459 Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Sat, 4 Mar 2023 21:19:21 +0100 Subject: [PATCH 3/4] Added filesystem file --- src/utils/filesystem.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/utils/filesystem.ts diff --git a/src/utils/filesystem.ts b/src/utils/filesystem.ts new file mode 100644 index 00000000..e69de29b From fedf2f7e8003ab7dddcf0a80ac36ff58551ab8a2 Mon Sep 17 00:00:00 2001 From: Anders Hafreager Date: Sat, 4 Mar 2023 22:55:25 +0100 Subject: [PATCH 4/4] WIP --- src/hooks/files.ts | 29 +++++++++++++++++++++++++++++ src/utils/filesystem.ts | 0 2 files changed, 29 insertions(+) create mode 100644 src/hooks/files.ts delete mode 100644 src/utils/filesystem.ts diff --git a/src/hooks/files.ts b/src/hooks/files.ts new file mode 100644 index 00000000..f88f75c5 --- /dev/null +++ b/src/hooks/files.ts @@ -0,0 +1,29 @@ +import {useState, useEffect} from 'react' +import localforage from 'localforage' + +localforage.config({ + driver : localforage.INDEXEDDB, + name : 'JupyterLite Storage', + storeName : 'files', // Should be alphanumeric, with underscores. + description : 'some description' +}); + +export const useListSimulations = async () => { + const [loading, setLoading] = useState(true) + const [error, setError] = useState(false) + + useEffect(() => { + (async () => { + const keys = await localforage.keys() + const simulations = [] + + for (let key of keys) { + const file = await localforage.getItem(key) as any + if (file.type === 'directory') { + + } + } + })() + + }) +} \ No newline at end of file diff --git a/src/utils/filesystem.ts b/src/utils/filesystem.ts deleted file mode 100644 index e69de29b..00000000