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

Add Review Order options #85

Merged
merged 9 commits into from
Dec 23, 2021
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"react-native-easy-toast": "^1.2.0",
"react-native-elements": "^3.0.0",
"react-native-modal": "^11.5.6",
"react-native-picker-select": "^8.0.4",
"react-native-safe-area-context": "3.1.9",
"react-native-web": "~0.13.12",
"react-redux": "^7.2.0",
Expand Down
9 changes: 9 additions & 0 deletions src/common/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ export const DARK_MODE = 'DARK_MODE';
export const BACK_TO_BACK_MODE = 'BACK_TO_BACK_MODE';
export const MEANING_FIRST = 'MEANING_FIRST';

// review orders
export const REVIEW_ORDER = 'REVIEW_ORDER'
export const RANDOM_ORDER = 'RANDOM_ORDER'
export const LOWEST_LEVEL_FIRST = 'LOWEST_LEVEL_FIRST'
export const CURRENT_LEVEL_FIRST = 'CURRENT_LEVEL_FIRST'
export const ASCENDING_SRS_STAGE = 'ASCENDING_SRS_STAGE'
export const DESCENDING_SRS_STAGE = 'DESCENDING_SRS_STAGE'


export const TERMINOLOGY = {

// subject types
Expand Down
10 changes: 10 additions & 0 deletions src/components/List/ListItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ListItem, Icon } from 'react-native-elements';
import device, {isWeb} from 'src/utils/device';
import useColorScheme from 'src/hooks/useColorScheme';;
import theme from 'src/common/theme';
import RNPickerSelect from 'react-native-picker-select';

const ListItemComponent = props => {
const colorScheme = useColorScheme();
Expand All @@ -27,6 +28,7 @@ const ListItemComponent = props => {
{props.rightIcon && <View style={[styles.icon, colorScheme === 'light' ? null : styles.icon_dark]}>{props.rightIcon}</View>}
{!isWeb() && props.switch && <Switch {...props.switch} />}
{isWeb() && props.switch && <View><Text>{props?.switch?.value ? "On" : "Off"}</Text></View>}
{props.picker && <RNPickerSelect style={pickerStyles} {...props.picker} />}
</ListItem>
)
};
Expand Down Expand Up @@ -63,4 +65,12 @@ const styles = StyleSheet.create({
}
})

const pickerStyles = StyleSheet.create({
inputWeb: {
borderWidth: 0,
textAlign: 'right',
fontSize: 14,
},
})

export default ListItemComponent;
16 changes: 9 additions & 7 deletions src/features/reviews/useReviewSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { MEANING, RADICAL } from 'src/common/constants';
import listToDict from 'src/utils/listToDict';
import queueReviews from 'src/features/reviews/utils/queueReviews';

import { BACK_TO_BACK_MODE, MEANING_FIRST} from 'src/common/constants';
import { BACK_TO_BACK_MODE, MEANING_FIRST, REVIEW_ORDER } from 'src/common/constants';
import { useStoreState } from 'easy-peasy';
import adjustQueue from 'src/features/reviews/utils/adjustQueue';

Expand All @@ -32,16 +32,18 @@ export default (reviews, subjects) => {
const [ completedCards, setCompletedCards ] = useState({});
const [ incorrectCards, setIncorrectCards ] = useState({});

// Sort reading-meaning card pairs back-to-back
// Sort reading-meaning card pairs back-to-back according to the review order
const userSettings = useStoreState(state => state.session.userSettings);
const backToBackMode = _.get(userSettings, BACK_TO_BACK_MODE);
const meaningFirst = _.get(userSettings, MEANING_FIRST);
const reviewOrder = _.get(userSettings, REVIEW_ORDER);
// Re-sort queue when the relevant user settings change
useEffect(() => {
setQueue(adjustQueue(queue, backToBackMode, meaningFirst))
setQueue(adjustQueue(queue, backToBackMode, meaningFirst, reviewOrder))
}, [
backToBackMode,
meaningFirst,
backToBackMode,
meaningFirst,
reviewOrder,
]);

// refresh &
Expand All @@ -63,8 +65,8 @@ export default (reviews, subjects) => {
setTotalReviews(reviews ? reviews.length : 0);

// create queue
const _queue = queueReviews(reviews);
setQueue(adjustQueue(_queue, backToBackMode, meaningFirst));
const _queue = queueReviews(reviews, subjects);
setQueue(adjustQueue(_queue, backToBackMode, meaningFirst, reviewOrder));
setTotalCards(_queue.length);

}, [
Expand Down
26 changes: 23 additions & 3 deletions src/features/reviews/utils/adjustQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,35 @@

import _ from 'lodash';

import { MEANING, RADICAL } from 'src/common/constants';
import { MEANING, RADICAL, RANDOM_ORDER, LOWEST_LEVEL_FIRST, CURRENT_LEVEL_FIRST, ASCENDING_SRS_STAGE, DESCENDING_SRS_STAGE } from 'src/common/constants';

const ALLOWED_MAX_DISTANCE = 10;
const ALLOWED_MIN_DISTANCE = 3;

export default (queue, backToBackMode = false, meaningFirst = false) => {
const newQueue = queue.slice();
export default (queue, backToBackMode = false, meaningFirst = false, reviewOrder = RANDOM_ORDER) => {

let newQueue = queue.slice();
let currentIndex = 0;

// sort new queue based on the selected review order
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a DEFAULT option (selected by default) that doesn't apply any sorting at all?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was kinda my intention with the RANDOM option here, since it is how it works today -- albeit unintentionally.

I'm afraid that if we add a DEFAULT option that doesn't apply any sorting at all, the queue would inherit the last selected option. For example, if a user switches to LOWEST_LEVEL_FIRST and then back to the DEFAULT option, since the DEFAULT does nothing, the queue would still be sorted in the LOWEST_LEVEL_FIRST order. However if we keep the RANDOM as the default selection, we will keep the default, current behavior, although with an extra step.

Does that make sense?

switch (reviewOrder) {
case RANDOM_ORDER:
newQueue = _.shuffle(newQueue);
break;
case LOWEST_LEVEL_FIRST:
newQueue = _.orderBy(newQueue, 'subjectLevel', 'asc')
break;
case CURRENT_LEVEL_FIRST:
newQueue = _.orderBy(newQueue, 'subjectLevel', 'desc')
break;
case ASCENDING_SRS_STAGE:
newQueue = _.orderBy(newQueue, 'review.data.srs_stage', 'asc')
break;
case DESCENDING_SRS_STAGE:
newQueue = _.orderBy(newQueue, 'review.data.srs_stage', 'desc')
break;
}

// avoid recursion to avoid exceeding maximum call stack size
while (currentIndex < queue.length - 1) {

Expand Down
12 changes: 8 additions & 4 deletions src/features/reviews/utils/queueReviews.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import adjustQueue from 'src/features/reviews/utils/adjustQueue';

// pick multiple reviews from the list, respecting the
// removed items from the list from the previous loop
const queueReviews = (reviews, reviewTypesHistory, queue) => {

const queueReviews = (reviews, reviewTypesHistory, queue, subjects) => {
let _reviews = reviews;
let _reviewTypesHistory = reviewTypesHistory;

Expand All @@ -22,11 +21,16 @@ const queueReviews = (reviews, reviewTypesHistory, queue) => {
newReviewTypeHistory,
] = pickReviewAndType(_reviews, _reviewTypesHistory);

// find the review's subject
const subject = subjects.find(subject => subject.id === _.get(review, 'data.subject_id'));
const subjectLevel = _.get(subject, "data.level");

// add current pick to the queue
queue.push({
id: randomId(),
review,
reviewType,
subjectLevel,
});

_reviews = newReviews;
Expand All @@ -37,7 +41,7 @@ const queueReviews = (reviews, reviewTypesHistory, queue) => {
return queue;
}

export default reviews => {
const queued = queueReviews(reviews, {}, []);
export default (reviews, subjects) => {
const queued = queueReviews(reviews, {}, [], subjects);
return adjustQueue(queued);
};
23 changes: 22 additions & 1 deletion src/screens/Review/ReviewMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import List from 'src/components/List/List';
import Modal, { DURATION_SAFE } from 'src/components/Modal/Modal';
import dialog from 'src/utils/dialog';
import { isWeb } from 'src/utils/device';
import { SKIP_MODE, QUICK_MODE, DARK_MODE, BACK_TO_BACK_MODE, MEANING_FIRST}
import { SKIP_MODE, QUICK_MODE, DARK_MODE, BACK_TO_BACK_MODE, MEANING_FIRST, REVIEW_ORDER, RANDOM_ORDER, LOWEST_LEVEL_FIRST, CURRENT_LEVEL_FIRST, ASCENDING_SRS_STAGE, DESCENDING_SRS_STAGE }
from 'src/common/constants';

const ReviewMenu = ({
Expand All @@ -33,6 +33,7 @@ const ReviewMenu = ({
const darkMode = _.get(userSettings, DARK_MODE);
const backToBackMode = _.get(userSettings, BACK_TO_BACK_MODE);
const meaningFirst = _.get(userSettings, MEANING_FIRST);
const reviewOrder = _.get(userSettings, REVIEW_ORDER, RANDOM_ORDER);

return (
<Modal
Expand Down Expand Up @@ -123,6 +124,26 @@ const ReviewMenu = ({
},
}
},
{
id: 'ses-review-order',
title: 'Review Order',
leftIcon: <SimpleLineIcons name="shuffle" size={18} color={iconcolor} />,
picker: {
onValueChange: (value) => {
saveSetting({ key: REVIEW_ORDER, value: value });
},
placeholder: {},
value: reviewOrder,
items:
[
{ value: RANDOM_ORDER, label: "Random Order" },
{ value: LOWEST_LEVEL_FIRST, label: "Lowest Level First" },
{ value: CURRENT_LEVEL_FIRST, label: "Current Level First" },
{ value: ASCENDING_SRS_STAGE, label: "Ascending SRS Stage" },
{ value: DESCENDING_SRS_STAGE, label: "Descending SRS Stage" },
],
},
},
{
id: 'ses-dark',
title: 'Dark Mode',
Expand Down
13 changes: 13 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2795,6 +2795,11 @@
resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-5.9.7.tgz#769c6b306ea6bbaa1c4a3fffdd183d0cfc30ed7e"
integrity sha512-NAkkT68oF+M9o6El2xeUqZK7magPjG/tAcEbvCbqyhlh3yElKWnI1e1vpbVvFXzTefy67FwYFWOJqBN6U7Mnkg==

"@react-native-picker/picker@^1.8.3":
version "1.16.3"
resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.16.3.tgz#22750f8d44465432845fa59ce20e69761029b6ef"
integrity sha512-Qt40DBg7IEhBN5x2DYPV3B26j5fNhVZcpCestr1IuirneMNtOaIyA9+nPYniL/OBzd9YbpUKbS7XSy/MM9bP3w==

"@reduxjs/toolkit@^1.3.5":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.4.0.tgz#ee2e2384cc3d1d76780d844b9c2da3580d32710d"
Expand Down Expand Up @@ -14201,6 +14206,14 @@ react-native-modal@^11.5.6:
prop-types "^15.6.2"
react-native-animatable "1.3.3"

react-native-picker-select@^8.0.4:
version "8.0.4"
resolved "https://registry.yarnpkg.com/react-native-picker-select/-/react-native-picker-select-8.0.4.tgz#3f7f1f42df69b06e7d2c10338288332a6c40fd10"
integrity sha512-orBjPIwBkV5oipyVw263YNMI56f6Kj3p/ejabZoCYYNSG3AiLVVhC2RqsxMgDA7IayyURAW+AlV+mDJyVqLBkg==
dependencies:
"@react-native-picker/picker" "^1.8.3"
lodash.isequal "^4.5.0"

react-native-ratings@^7.3.0:
version "7.3.0"
resolved "https://registry.yarnpkg.com/react-native-ratings/-/react-native-ratings-7.3.0.tgz#32e11e4ed944ba8adbbc995d601df3f131619b6f"
Expand Down