feat: lazy resource loading
This commit is contained in:
parent
25d3df748e
commit
a017c1c6a6
|
@ -3,12 +3,23 @@ import './index.scss';
|
|||
import {UriContext} from '@avocado/persea';
|
||||
import {Resource} from '@avocado/resource/persea';
|
||||
import {PropTypes, React} from '@latus/react';
|
||||
import {useSelector} from '@latus/redux';
|
||||
import {
|
||||
useDispatch,
|
||||
useSelector,
|
||||
} from '@latus/redux';
|
||||
|
||||
import {resourceSelector} from '../../../state/projects';
|
||||
import {
|
||||
fetchProjectResource,
|
||||
resourceSelector,
|
||||
} from '../../../state/projects';
|
||||
|
||||
const ResourceRoute = ({match: {params: {uri, uuid}}}) => {
|
||||
const resource = useSelector((state) => resourceSelector(state, `${uuid}${uri}`));
|
||||
const dispatch = useDispatch();
|
||||
const resource = useSelector((state) => resourceSelector(state, uuid, uri));
|
||||
if (!resource) {
|
||||
dispatch(fetchProjectResource({uri, uuid}));
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<UriContext.Provider value={uri}>
|
||||
<Resource resource={resource} />
|
||||
|
|
|
@ -3,53 +3,6 @@ import {join} from 'path';
|
|||
import {promisify} from 'util';
|
||||
|
||||
const readFile = promisify(fs.readFile).bind(fs);
|
||||
const stat = promisify(fs.stat).bind(fs);
|
||||
|
||||
const projectResources = async (uuid, resourcePaths, latus) => {
|
||||
const statPaths = await Promise.all(
|
||||
resourcePaths
|
||||
.map(async ([, path]) => {
|
||||
try {
|
||||
return [
|
||||
(await stat(join(process.cwd(), 'projects', uuid, path))).isFile(),
|
||||
path,
|
||||
];
|
||||
}
|
||||
catch (error) {
|
||||
return [false];
|
||||
}
|
||||
}),
|
||||
);
|
||||
const filePaths = statPaths
|
||||
.filter(([stat]) => stat)
|
||||
.map(([, path]) => path);
|
||||
return filePaths
|
||||
.reduce(async (r, path) => {
|
||||
const {fromBuffer} = latus.get('%resource-controllers')
|
||||
.find(({matcher}) => path.match(matcher));
|
||||
try {
|
||||
const encoded = await fromBuffer(
|
||||
await readFile(join(process.cwd(), 'projects', uuid, path)),
|
||||
latus,
|
||||
);
|
||||
return ({
|
||||
...(await r),
|
||||
[join(uuid, path)]: encoded,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Encoding ${path}: ${error.stack}`);
|
||||
}
|
||||
}, {});
|
||||
};
|
||||
|
||||
const projectsResources = (projectsResourcePaths, latus) => (
|
||||
Object.entries(projectsResourcePaths)
|
||||
.reduce(async (r, [uuid, resourcePaths]) => ({
|
||||
...(await r),
|
||||
...(await projectResources(uuid, resourcePaths, latus)),
|
||||
}), {})
|
||||
);
|
||||
|
||||
const projectsStructure = async (projectsResourcePaths) => (
|
||||
Object.fromEntries(
|
||||
|
@ -77,7 +30,8 @@ const projectsStructure = async (projectsResourcePaths) => (
|
|||
)
|
||||
);
|
||||
|
||||
export default async (projectsResourcePaths, latus) => ({
|
||||
export default async (projectsResourcePaths) => ({
|
||||
pending: {},
|
||||
structure: await projectsStructure(projectsResourcePaths),
|
||||
resources: await projectsResources(projectsResourcePaths, latus),
|
||||
resources: {},
|
||||
});
|
||||
|
|
|
@ -1,13 +1,36 @@
|
|||
import {Resource} from '@avocado/resource';
|
||||
import {
|
||||
createAsyncThunk,
|
||||
createSelector,
|
||||
createSlice,
|
||||
} from '@latus/redux';
|
||||
|
||||
export const projectsSelector = (state) => state.projects;
|
||||
|
||||
export const fetchProjectResource = createAsyncThunk(
|
||||
'persea/projects/fetchResource',
|
||||
async ({uri}, {extra: latus}) => {
|
||||
const buffer = await Resource.read(uri);
|
||||
const {fromBuffer} = latus.get('%resource-controllers')
|
||||
.find(({matcher}) => uri.match(matcher));
|
||||
try {
|
||||
return fromBuffer(Buffer.from(buffer), latus);
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Encoding ${uri}: ${error.stack}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
condition: ({uri}, {getState}) => {
|
||||
const {pending} = projectsSelector(getState());
|
||||
return !pending[uri];
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export const resourceSelector = createSelector(
|
||||
[projectsSelector, (_, path) => path],
|
||||
({resources}, path) => path && resources[path],
|
||||
[projectsSelector, (_, uuid) => uuid, (_, __, uri) => uri],
|
||||
({resources}, uuid, uri) => uuid && uri && resources[`${uuid}${uri}`],
|
||||
);
|
||||
|
||||
export const structureSelector = createSelector(
|
||||
|
@ -19,10 +42,20 @@ const slice = createSlice({
|
|||
name: 'persea/projects',
|
||||
initialState: {
|
||||
structure: {},
|
||||
pending: {},
|
||||
resources: {},
|
||||
},
|
||||
/* eslint-disable no-param-reassign */
|
||||
extraReducers: {
|
||||
[fetchProjectResource.pending]: ({pending}, action) => {
|
||||
const {uri} = action.meta.arg;
|
||||
pending[uri] = true;
|
||||
},
|
||||
[fetchProjectResource.fulfilled]: ({pending, resources}, action) => {
|
||||
const {uri, uuid} = action.meta.arg;
|
||||
delete pending[uri];
|
||||
resources[`${uuid}${uri}`] = action.payload;
|
||||
},
|
||||
},
|
||||
reducers: {
|
||||
// createProject: ({projects}, {payload: {label, resourcePaths, uuid}}) => {
|
||||
|
|
Loading…
Reference in New Issue
Block a user