Skip to content
This repository has been archived by the owner on Apr 29, 2022. It is now read-only.

Add history page #177

Merged
merged 1 commit into from
Jun 6, 2020
Merged
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
2 changes: 1 addition & 1 deletion public/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ if (!gotTheLock) {
alwaysOnTop: getPreference('alwaysOnTop'),
width: 400,
height: 500,
minWidth: 400,
minWidth: 460,
minHeight: 500,
webPreferences: {
nodeIntegration: true,
Expand Down
9 changes: 4 additions & 5 deletions public/libs/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"quit": "Quit",
"home": "Home",
"phrasebook": "Phrasebook",
"phrasebookDesc": "Tap the star icon to save a translation.",
"searchSavedTranslations": "Search saved translations...",
"search": "Search",
"noMatchingResults": "No Matching Results",
Expand Down Expand Up @@ -140,11 +141,9 @@
"trialExpireIn": "Your trial period will expire in $TIME.",
"visitStore": "Visit Store...",
"register": "Register",
"definitions": "Definitions",
"synonyms": "Synonyms",
"examples": "Examples",
"seeAlso": "See also",
"translations": "Translations",
"history": "History",
"historyDesc": "View previously translated words and phrases here.",
"clearHistory": "Clear history",
"auto": "Auto detect",
"af": "Afrikaans",
"sq": "Albanian",
Expand Down
1 change: 1 addition & 0 deletions public/libs/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const defaultPreferences = {
themeSource: 'system',
translateClipboardOnShortcut: false,
translateWhenPressingEnter: true,
useHardwareAcceleration: false,
};

let cachedPreferences = null;
Expand Down
40 changes: 25 additions & 15 deletions src/components/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';

import CircularProgress from '@material-ui/core/CircularProgress';
import ActionHome from '@material-ui/icons/Home';
import ActionSettings from '@material-ui/icons/Settings';
import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';
import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
import Snackbar from '@material-ui/core/Snackbar';
import ToggleStar from '@material-ui/icons/Star';

import HomeIcon from '@material-ui/icons/Home';
import SettingsIcon from '@material-ui/icons/Settings';
import StarIcon from '@material-ui/icons/Star';
import HistoryIcon from '@material-ui/icons/History';

import connectComponent from '../helpers/connect-component';
import getLocale from '../helpers/get-locale';
Expand All @@ -25,13 +27,15 @@ import DialogAbout from './root/dialog-about';
import DialogLicenseRegistration from './root/dialog-license-registration';

import Home from './pages/home';
import History from './pages/history';
import Phrasebook from './pages/phrasebook';
import Preferences from './pages/preferences';
import LanguageList from './pages/language-list';
import Ocr from './pages/ocr';

import {
ROUTE_HOME,
ROUTE_HISTORY,
ROUTE_PHRASEBOOK,
ROUTE_PREFERENCES,
ROUTE_LANGUAGE_LIST,
Expand Down Expand Up @@ -80,15 +84,8 @@ const styles = (theme) => ({
flexDirection: 'column',
overflow: 'hidden',
},
bottomNavigation: {
height: 40,
},
bottomNavigationActionWrapper: {
flexDirection: 'row',
},
bottomNavigationActionLabel: {
fontSize: '0.8rem !important',
paddingLeft: 4,
},
hidden: {
display: 'none !important',
Expand Down Expand Up @@ -131,8 +128,10 @@ class App extends React.Component {
switch (route) {
case ROUTE_PREFERENCES:
return <Preferences key="preferences" />;
case ROUTE_HISTORY:
return <History key="history" />;
case ROUTE_PHRASEBOOK:
return <Phrasebook key="installed" />;
return <Phrasebook key="phrasebook" />;
case ROUTE_LANGUAGE_LIST:
return null; // already preloaded
case ROUTE_OCR:
Expand All @@ -145,8 +144,10 @@ class App extends React.Component {
const bottomNavigationSelectedIndex = (() => {
switch (route) {
case ROUTE_PREFERENCES:
return 2;
return 3;
case ROUTE_PHRASEBOOK:
return 2;
case ROUTE_HISTORY:
return 1;
case ROUTE_HOME:
return 0;
Expand Down Expand Up @@ -218,16 +219,25 @@ class App extends React.Component {
>
<BottomNavigationAction
label={getLocale('home')}
icon={<ActionHome className={classes.icon} />}
icon={<HomeIcon className={classes.icon} />}
onClick={() => onChangeRoute(ROUTE_HOME)}
classes={{
wrapper: classes.bottomNavigationActionWrapper,
label: classes.bottomNavigationActionLabel,
}}
/>
<BottomNavigationAction
label={getLocale('history')}
icon={<HistoryIcon className={classes.icon} />}
onClick={() => onChangeRoute(ROUTE_HISTORY)}
classes={{
wrapper: classes.bottomNavigationActionWrapper,
label: classes.bottomNavigationActionLabel,
}}
/>
<BottomNavigationAction
label={getLocale('phrasebook')}
icon={<ToggleStar className={classes.icon} />}
icon={<StarIcon className={classes.icon} />}
onClick={() => onChangeRoute(ROUTE_PHRASEBOOK)}
classes={{
wrapper: classes.bottomNavigationActionWrapper,
Expand All @@ -236,7 +246,7 @@ class App extends React.Component {
/>
<BottomNavigationAction
label={getLocale('preferences')}
icon={<ActionSettings className={classes.icon} />}
icon={<SettingsIcon className={classes.icon} />}
onClick={() => onChangeRoute(ROUTE_PREFERENCES)}
classes={{
wrapper: classes.bottomNavigationActionWrapper,
Expand Down
236 changes: 236 additions & 0 deletions src/components/pages/history/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import React from 'react';
import PropTypes from 'prop-types';

import AppBar from '@material-ui/core/AppBar';
import Divider from '@material-ui/core/Divider';
import IconButton from '@material-ui/core/IconButton';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
import Toolbar from '@material-ui/core/Toolbar';
import Tooltip from '@material-ui/core/Tooltip';

import Typography from '@material-ui/core/Typography';
import DeleteIcon from '@material-ui/icons/Delete';
import HistoryIcon from '@material-ui/icons/History';
import SearchIcon from '@material-ui/icons/Search';
import ClearAllIcon from '@material-ui/icons/ClearAll';

import connectComponent from '../../../helpers/connect-component';
import getLocale from '../../../helpers/get-locale';

import { deleteHistoryItem, loadHistory, clearAllHistory } from '../../../state/pages/history/actions';
import { loadOutput } from '../../../state/pages/home/actions';
import { changeRoute } from '../../../state/root/router/actions';

import { ROUTE_HOME } from '../../../constants/routes';

import SearchBox from './search-box';

const styles = (theme) => ({
emptyContainer: {
flex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
emptyInnerContainer: {
textAlign: 'center',
},
bigIcon: {
height: 96,
width: 96,
color: theme.palette.text.primary,
},
container: {
flex: 1,
display: 'flex',
flexDirection: 'column',
position: 'relative',
overflow: 'hidden',
},
listContainer: {
flex: 1,
overflowY: 'auto',
WebkitOverflowScrolling: 'touch',
padding: 0,
boxSizing: 'border-box',
},
progress: {
marginTop: 12,
},
appBarColorDefault: {
background: theme.palette.type === 'dark' ? theme.palette.grey[900] : theme.palette.primary.main,
color: theme.palette.type === 'dark' ? theme.palette.getContrastText(theme.palette.grey[900]) : theme.palette.primary.contrastText,
},
title: {
flex: 1,
textAlign: 'center',
},
toolbar: {
minHeight: 40,
paddingRight: theme.spacing(1.5),
paddingLeft: theme.spacing(1.5),
},
appBarMenu: {
position: 'absolute',
right: theme.spacing(1.5),
},
toolbarIconButton: {
padding: theme.spacing(1),
},
});

class History extends React.Component {
componentDidMount() {
const { onLoadHistory } = this.props;

onLoadHistory(true);

if (this.listView) {
this.listView.onscroll = () => {
const { scrollTop, clientHeight, scrollHeight } = this.listView;
if (scrollTop + clientHeight > scrollHeight - 200) {
const { canLoadMore, historyLoading } = this.props;
if (canLoadMore === true && historyLoading === false) {
onLoadHistory();
}
}
};
}
}

componentWillUnmount() {
if (this.listView) this.listView.onscroll = null;
}

render() {
const {
classes,
historyItems,
historyLoading,
onChangeRoute,
onClearAllHistory,
onDeleteHistoryItem,
onLoadOutput,
query,
} = this.props;

return (
<div className={classes.container}>
<AppBar position="static" color="default" elevation={0} classes={{ colorDefault: classes.appBarColorDefault }}>
<Toolbar variant="dense" className={classes.toolbar}>
<Typography variant="subtitle1" color="inherit" className={classes.title}>{getLocale('history')}</Typography>
<div className={classes.appBarMenu}>
<Tooltip title={getLocale('clearHistory')} placement="left">
<IconButton
aria-label={getLocale('clearHistory')}
className={classes.toolbarIconButton}
onClick={onClearAllHistory}
>
<ClearAllIcon fontSize="small" />
</IconButton>
</Tooltip>
</div>
</Toolbar>
</AppBar>
<SearchBox />
{(() => {
if (historyItems.length < 1 && historyLoading === false) {
return (
<div className={classes.emptyContainer}>
<div className={classes.emptyInnerContainer}>
{query ? (
<SearchIcon className={classes.bigIcon} />
) : (
<HistoryIcon className={classes.bigIcon} />
)}
<Typography variant="h5" color="textSecondary">
{query ? getLocale('noMatchingResults') : getLocale('history')}
</Typography>
{!query && (
<Typography variant="body2" color="textSecondary">
{getLocale('historyDesc')}
</Typography>
)}
</div>
</div>
);
}

return (
<div className={classes.listContainer} ref={(c) => { this.listView = c; }}>
<List disablePadding>
{historyItems.map((item) => [(
<ListItem
button
key={`historyItem_${item.historyId}`}
onClick={() => {
onLoadOutput(item);
onChangeRoute(ROUTE_HOME);
}}
>
<ListItemText
primary={item.outputText}
secondary={item.inputText}
primaryTypographyProps={{ color: 'textPrimary' }}
/>
<ListItemSecondaryAction>
<Tooltip title={getLocale('remove')} placement="left">
<IconButton
aria-label={getLocale('remove')}
onClick={() => onDeleteHistoryItem(
item.historyId,
item.rev,
)}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
), <Divider key={`historyDivider_${item.historyId}`} />])}
</List>
</div>
);
})()}
</div>
);
}
}

History.propTypes = {
canLoadMore: PropTypes.bool.isRequired,
classes: PropTypes.object.isRequired,
historyItems: PropTypes.arrayOf(PropTypes.object).isRequired,
historyLoading: PropTypes.bool.isRequired,
onChangeRoute: PropTypes.func.isRequired,
onClearAllHistory: PropTypes.func.isRequired,
onDeleteHistoryItem: PropTypes.func.isRequired,
onLoadHistory: PropTypes.func.isRequired,
onLoadOutput: PropTypes.func.isRequired,
query: PropTypes.string.isRequired,
};

const mapStateToProps = (state) => ({
canLoadMore: state.pages.history.canLoadMore,
historyItems: state.pages.history.items,
historyLoading: state.pages.history.loading,
query: state.pages.history.query,
});

const actionCreators = {
changeRoute,
clearAllHistory,
deleteHistoryItem,
loadHistory,
loadOutput,
};

export default connectComponent(
History,
mapStateToProps,
actionCreators,
styles,
);
Loading