Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permalinks Feature - Abel #1281

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"increment-build": "bash scripts/increment-build.sh && git add -A"
},
"dependencies": {
"@gwdevs/permalinks-hooks": "^1.1.1-beta.1",
"@material-ui/core": "^4.11.3",
"@material-ui/icons": "^4.5.1",
"@material-ui/lab": "4.0.0-alpha.60",
Expand Down
2 changes: 1 addition & 1 deletion public/build_number
Original file line number Diff line number Diff line change
@@ -1 +1 @@
157-491c7f1
159-035f023
65 changes: 18 additions & 47 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,24 @@
import React, {
useState,
useCallback,
useEffect,
} from 'react';

import { loadState, loadAuthentication } from './core/persistence';

import React from 'react';
import ConfirmContextProvider from './context/ConfirmContextProvider';
import { AppContextProvider } from './App.context';
import Layout from './Layout';
import useInitialState from './features/permalinks/useInitialState';
import PermalinksHandler from './features/permalinks/PermalinksHandler';

export default function App() {
const [resumedState, setResumedState] = useState();

const resumeState = useCallback(async () => {
// note that the authentication context manages its own
// state via provided persistence load and save closures
const authentication = await loadAuthentication('authentication');

const organization = authentication && (await loadState('organization'));
const language = authentication && (await loadState('language'));
const sourceRepository =
authentication && (await loadState('sourceRepository'));
const resourceLinks = authentication && (await loadState('resourceLinks'));
const filepath = authentication && (await loadState('filepath'));
const _resumedState = {
authentication,
language,
sourceRepository,
filepath,
organization,
resourceLinks,
};
setResumedState(_resumedState);
}, []);

useEffect(() => {
resumeState();
}, [resumeState]);

const props = { ...resumedState };

return !resumedState ? (
<></>
) : (
<ConfirmContextProvider>
<AppContextProvider {...props}>
<Layout />
</AppContextProvider>
</ConfirmContextProvider>
);
const [initialState] = useInitialState();
const props = { ...initialState };
return !initialState
? (
<></>
abelpz marked this conversation as resolved.
Show resolved Hide resolved
)
: (
<ConfirmContextProvider>
<AppContextProvider {...props}>
<PermalinksHandler>
<Layout />
</PermalinksHandler>
</AppContextProvider>
</ConfirmContextProvider>
);
};
18 changes: 11 additions & 7 deletions src/components/translatable/TranslatableTSV.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
compositeKeyIndicesFromColumnNames,
generateRowId,
} from './helpers';
import useQueryOptions from '../../features/permalinks/useQueryOptions';

const delimiters = { row: '\n', cell: '\t' };

Expand Down Expand Up @@ -101,11 +102,14 @@ export default function TranslatableTSV({
return rowId;
}, [columnNames, delimiters]);

const options = {
page: 0,
rowsPerPage: 25,
rowsPerPageOptions: [10, 25, 50, 100],
};
const { options, extraColumns } = useQueryOptions({
defaultOptions: {
page: 0,
rowsPerPage: 25,
rowsPerPageOptions: [10, 25, 50, 100],
},
columnNames
});

const rowHeader = useDeepCompareCallback((rowData, actionsMenu) => {
const _props = {
Expand All @@ -124,11 +128,11 @@ export default function TranslatableTSV({
rowHeader,
compositeKeyIndices: compositeKeyIndicesFromColumnNames({ columnNames }),
columnsFilter: columnsFilterFromColumnNames({ columnNames }),
columnsShowDefault: columnsShowDefaultFromColumnNames({ columnNames }),
columnsShowDefault: [...columnsShowDefaultFromColumnNames({ columnNames }), ...extraColumns],
};

return _config;
}, [columnNames, rowHeader]);
}, [columnNames, extraColumns, rowHeader]);

return (
<ResourcesContextProvider
Expand Down
3 changes: 3 additions & 0 deletions src/core/state.reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ export const stateReducer = (state, action) => {
case `set_cached_file`:
_state['cachedFile'] = value;
break;
case `set_state`:
_state = value;
break;
default:
throw new Error(`Unsupported action type: ${action.type}`);
}
Expand Down
54 changes: 54 additions & 0 deletions src/features/permalinks/PermalinksHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { useContext, useCallback, useEffect } from 'react'
import { useDeepCompareEffect, useDeepCompareCallback } from 'use-deep-compare';
import { useNavigation,PermalinksConfig } from '@gwdevs/permalinks-hooks';
import { AppContext } from '../../App.context';
import routes from './routes.json';
import {getStateKeys} from './helpers'

export default function PermalinksHandler({ children }) {
const { state, actions } = useContext(AppContext);

const { setState } = actions;

const setHistoryState = useDeepCompareCallback(() => window.history.state || window.history.replaceState(state, null) || state, [state]);

const restoreState = useCallback(() => {
console.log("restoring state from history");
const historyState = setHistoryState();
setState(historyState);
}, [setState,setHistoryState]);

useEffect(() => {
abelpz marked this conversation as resolved.
Show resolved Hide resolved
window.addEventListener('popstate', restoreState);
return () => {
window.removeEventListener('popstate', restoreState);
}
},[restoreState])

const { push } = useNavigation();

const getFormattedLink = useCallback((state) => {
const search = window.location.search;
const keys = getStateKeys(state);
const entry = 'project';
const org = keys?.organization;
const repo = keys?.resource;
const lang = keys?.language;
const filepath = keys?.filepath;

const path = [org, repo, lang, filepath].filter(Boolean).join('/');
if (!!path) {
const permalink = entry + '/' + path;
return search ? permalink + search : permalink;
}
}, []);

useDeepCompareEffect(() => {
if (!window.history.state) setHistoryState();
push(getFormattedLink(state),state);
}, [getFormattedLink, state, setHistoryState]);

return (
<PermalinksConfig routes={routes}>{children}</PermalinksConfig>
)
}
9 changes: 9 additions & 0 deletions src/features/permalinks/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function getStateKeys(state = {}) {
const { filepath, organization, sourceRepository, language } = state;
return {
organization: organization?.username,
language: language?.languageId,
resource: sourceRepository?.full_name.split('/')[1].split('_')[1],
filepath: filepath,
}
}
6 changes: 6 additions & 0 deletions src/features/permalinks/routes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"entry": "project",
"path": ["organization","resource","language","...filepath"]
}
]
93 changes: 93 additions & 0 deletions src/features/permalinks/useInitialState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
useState,
useCallback
} from 'react';
import { loadState, saveState, loadAuthentication } from '../../core/persistence';
import routes from './routes.json';
import { useLanguages } from 'uw-languages-rcl';
import { usePermalinks } from '@gwdevs/permalinks-hooks';
import { getUser, readRepo } from "gitea-react-toolkit";
import { getLanguage } from '../../components/languages/helpers';
import { useDeepCompareCallback, useDeepCompareEffect } from 'use-deep-compare';

export default function useInitialState() {
const [initialState, setInitialState] = useState();
const { state: languages } = useLanguages();
const { permalink, isLoading: isLoadingPermalink } = usePermalinks({ routes });

const getLocalState = useCallback(async (authenticated=false) => {
const authentication = await loadAuthentication('authentication');
const organization = (!authenticated || authentication) && await loadState('organization');
const language = (!authenticated || authentication) && await loadState('language');
const sourceRepository = (!authenticated || authentication) && await loadState('sourceRepository');
const resourceLinks = (!authenticated || authentication) && await loadState('resourceLinks');
const filepath = (!authenticated || authentication) && await loadState('filepath');
return {
authentication,
language,
sourceRepository,
filepath,
organization,
resourceLinks,
};
},[]);

const setState = useDeepCompareCallback(async () => {

if (!permalink && isLoadingPermalink) return;

if (!permalink && !isLoadingPermalink) {
setInitialState(await getLocalState(true));
return;
}

const localLanguage = await loadState('language');

const fetchedLanguage = !!languages.length && getLanguage({ languageId: permalink.language, languagesJSON: languages });

const language = localLanguage?.languageId === permalink?.language
? localLanguage
: fetchedLanguage?.languageId && fetchedLanguage

if (permalink?.language && !language && !languages.length) return;

if(language?.languageId) saveState('language', language);

const localState = await getLocalState();
const authentication = localState.authentication;

const organization = localState.organization?.username === permalink.organization
? localState.organization
: permalink.organization && await getUser({username: permalink.organization}).then(({ data }) => {
saveState('organization', data);
return data
}).catch(err => console.warn(err))

const sourceRepoName = 'en_' + permalink.resource;
const sourceRepository = localState.sourceRepository?.full_name.split('/')[1].split('_')[1] === permalink.resource
? localState.sourceRepository
: permalink.resource && await readRepo({owner:'unfoldingWord', repo:sourceRepoName}).then(({ data }) => {
data.tree_url = `api/v1/repos/unfoldingWord/${sourceRepoName}/git/trees/master`;
saveState('sourceRepository', data);
return data;
}).catch(err => console.warn(err));

const filepath = permalink.filepath;

const permalinkState = {
authentication,
language,
sourceRepository,
filepath,
organization,
resourceLinks: null,
};
setInitialState(permalinkState);
}, [languages,permalink,isLoadingPermalink]);

useDeepCompareEffect(() => {
if (!initialState) setState();
}, [setState, initialState]);

return [initialState];
}
39 changes: 39 additions & 0 deletions src/features/permalinks/useQueryOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useState } from 'react';
import { useDeepCompareEffect } from 'use-deep-compare';
import { usePermalinks } from '@gwdevs/permalinks-hooks';

export default function useQueryOptions({defaultOptions,columnNames}) {
const { query } = usePermalinks({});
const [extraColumns, setExtraColumns] = useState([]);
const [options, setOptions] = useState(defaultOptions);

useDeepCompareEffect(() => {
const exludedFromSearch = ['columns', 'check'];

if (!query) return;

Object.keys(query).forEach((key) => {
if (!exludedFromSearch.includes(key)) {
setOptions((options) => ({ ...options, searchText: query[key] }));
}
});

},[query]);

useDeepCompareEffect(() => {
if (query && columnNames) {
const queryColumns = query.columns?.split(',');
const validColumns = columnNames.map(column => queryColumns && queryColumns.includes(column) && column) || [];

const columnsShowDefault = columnNames.map(column =>
query[column] && column
);
setExtraColumns([
...columnsShowDefault,
...validColumns,
]);
}
}, [query, columnNames]);

return {options,extraColumns}
}
10 changes: 10 additions & 0 deletions src/hooks/useStateReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ export const useStateReducer = ({
setFilepath();
}, [setFilepath, setLanguage, setOrganization, setSourceRepository]);

const setState = useCallback((value) => {
const { organization, sourceRepository, language, filePath } = value;
dispatch({ type: 'set_state', value });
saveState('organization', organization);
saveState('sourceRepository', sourceRepository);
saveState('language', language);
saveState('filePath', filePath);
}, []);

const setAuthentication = useCallback((value) => {
if (JSON.stringify(value) !== JSON.stringify(state.authentication)) {
dispatch({ type: 'set_authentication', value });
Expand Down Expand Up @@ -205,6 +214,7 @@ export const useStateReducer = ({
setCacheWarningMessage,
setCachedFile,
clearCachedFile,
setState,
};
return { state, actions };
};
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { disableBackButton } from './utils';
disableBackButton();
// import { disableBackButton } from './utils';
// disableBackButton();
ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,11 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==

"@gwdevs/permalinks-hooks@^1.1.1-beta.1":
version "1.1.1-beta.1"
resolved "https://registry.yarnpkg.com/@gwdevs/permalinks-hooks/-/permalinks-hooks-1.1.1-beta.1.tgz#a03d394f662875c536aac824db02477dbdadb636"
integrity sha512-l5Vz2QlVaz8jxyf4S9q9S9vET07rY03hMeoi7IFIB283cLLvMUmY9vSMcJwLd3weAKv1SG1uUd3IY4pK+R4sKQ==

"@hapi/[email protected]":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
Expand Down