feat: lazy resource loading

This commit is contained in:
cha0s 2021-04-04 16:16:15 -05:00
parent 25d3df748e
commit a017c1c6a6
4 changed files with 53 additions and 54 deletions

1
TODO.md Normal file
View File

@ -0,0 +1 @@
x lazy resource loading

View File

@ -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} />

View File

@ -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: {},
});

View File

@ -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}}) => {