diff --git a/src/components/SelectMenu/component.js b/src/components/SelectMenu/component.js
index 0c1cddd01..8c4656c31 100644
--- a/src/components/SelectMenu/component.js
+++ b/src/components/SelectMenu/component.js
@@ -1,10 +1,10 @@
-import React, { PureComponent } from 'react';
+import * as React from 'react';
import { TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';
import ActionSheet from 'react-native-actionsheet';
import * as colors from 'kitsu/constants/colors';
-export class SelectMenu extends PureComponent {
+export class SelectMenu extends React.PureComponent {
static propTypes = {
cancelButtonIndex: PropTypes.number,
children: PropTypes.element,
@@ -26,18 +26,6 @@ export class SelectMenu extends PureComponent {
tintColor: colors.black,
};
- constructor(props) {
- super(props);
-
- this.displayOptions = props.options.map((option) => {
- if (typeof option === 'string') {
- return option.charAt(0).toUpperCase() + option.slice(1);
- }
-
- return option.text;
- });
- }
-
getCancelButtonIndex() {
return this.props.cancelButtonIndex > -1
? this.props.cancelButtonIndex
@@ -48,6 +36,14 @@ export class SelectMenu extends PureComponent {
this.ActionSheet = component;
};
+ displayOptions = () => this.props.options.map((option) => {
+ if (typeof option === 'string') {
+ return option.charAt(0).toUpperCase() + option.slice(1);
+ }
+
+ return option.text;
+ });
+
handleFilterChange = (selectedIndex) => {
const cancelButtonIndex = this.getCancelButtonIndex();
@@ -78,7 +74,7 @@ export class SelectMenu extends PureComponent {
diff --git a/src/screens/Auth/LoginScreen.js b/src/screens/Auth/LoginScreen.js
index 80670366b..43cddfa16 100644
--- a/src/screens/Auth/LoginScreen.js
+++ b/src/screens/Auth/LoginScreen.js
@@ -21,14 +21,13 @@ class LoginScreen extends Component {
loading: false,
};
- onSubmit = (isFb = false) => {
+ onSubmit = () => {
const { username, password } = this.state;
const { navigation } = this.props;
- console.log('isFB', isFb, username, password);
- if (isFb) {
- this.props.loginUser(null, navigation);
- } else if (username.length > 0 && password.length > 0) {
+ if (username.length > 0 && password.length > 0) {
this.props.loginUser({ username, password }, navigation);
+ } else {
+ this.props.loginUser(null, navigation);
}
};
diff --git a/src/screens/Profiles/UserLibrary/components/UserLibraryListCard/component.js b/src/screens/Profiles/UserLibrary/components/UserLibraryListCard/component.js
index e7076a8be..bdd9f6586 100644
--- a/src/screens/Profiles/UserLibrary/components/UserLibraryListCard/component.js
+++ b/src/screens/Profiles/UserLibrary/components/UserLibraryListCard/component.js
@@ -16,17 +16,25 @@ const USER_LIBRARY_EDIT_SCREEN = 'UserLibraryEdit';
export const STATUS_SELECT_OPTIONS = [
{ value: 'current', anime: 'Currently Watching', manga: 'Currently Reading' },
{ value: 'planned', anime: 'Want To Watch', manga: 'Want To Read' },
- { value: 'onHold', anime: 'On Hold', manga: 'On Hold' },
+ { value: 'on_hold', anime: 'On Hold', manga: 'On Hold' },
{ value: 'dropped', anime: 'Dropped', manga: 'Dropped' },
{ value: 'completed', anime: 'Completed', manga: 'Completed' },
{ value: 'remove', anime: 'Remove From Library', manga: 'Remove From Library' },
{ value: 'cancel', anime: 'Nevermind', manga: 'Nevermind' },
];
+const HEADER_TEXT_MAPPING = {
+ current: { anime: 'Watching', manga: 'Reading' },
+ planned: { anime: 'Want To Watch', manga: 'Want to Read' },
+ completed: { anime: 'Complete', manga: 'Complete' },
+ on_hold: { anime: 'On Hold', manga: 'On Hold' },
+ dropped: { anime: 'Dropped', manga: 'Dropped' },
+};
+
export class UserLibraryListCard extends React.Component {
static propTypes = {
currentUser: PropTypes.object.isRequired,
- data: PropTypes.object.isRequired,
+ libraryEntry: PropTypes.object.isRequired,
libraryStatus: PropTypes.string.isRequired,
libraryType: PropTypes.string.isRequired,
navigate: PropTypes.func.isRequired,
@@ -37,10 +45,12 @@ export class UserLibraryListCard extends React.Component {
state = {
isUpdating: false,
- libraryStatus: this.props.data.status,
- progress: this.props.data.progress,
- progressPercentage: Math.floor((this.props.data.progress / this.getMaxProgress()) * 100),
- ratingTwenty: this.props.data.ratingTwenty,
+ libraryStatus: this.props.libraryEntry.status,
+ progress: this.props.libraryEntry.progress,
+ progressPercentage: Math.floor(
+ (this.props.libraryEntry.progress / this.getMaxProgress()) * 100,
+ ),
+ ratingTwenty: this.props.libraryEntry.ratingTwenty,
isSliderActive: false,
}
@@ -74,8 +84,8 @@ export class UserLibraryListCard extends React.Component {
onSwipeRelease = () => this.props.onSwipingItem(false)
getMaxProgress() {
- const { data, libraryType } = this.props;
- const mediaData = data[libraryType];
+ const { libraryEntry, libraryType } = this.props;
+ const mediaData = libraryEntry[libraryType];
if (mediaData.type === 'anime') {
return mediaData.episodeCount;
@@ -111,11 +121,11 @@ export class UserLibraryListCard extends React.Component {
// send the status from props because that is the list we're looking
// at not the status from state because that is what the value of the
// card may have just been changed to
- const { libraryStatus, libraryType } = this.props;
+ const { libraryEntry, libraryType } = this.props;
const { libraryStatus: newStatus, progress, ratingTwenty } = this.state;
- await this.props.updateUserLibraryEntry(libraryType, libraryStatus, {
- id: this.props.data.id,
+ this.props.updateUserLibraryEntry(libraryType, libraryEntry.status, {
+ id: this.props.libraryEntry.id,
progress,
ratingTwenty,
status: newStatus,
@@ -124,15 +134,15 @@ export class UserLibraryListCard extends React.Component {
debounceSave = debounce(this.saveEntry, 200);
- selectOptions = STATUS_SELECT_OPTIONS.map(option => ({
+ selectOptions = () => STATUS_SELECT_OPTIONS.map(option => ({
value: option.value,
text: option[this.props.libraryType],
- })).filter(option => option.value !== this.props.libraryStatus);
+ })).filter(option => option.value !== this.props.libraryEntry.status);
render() {
- const { data, libraryType, currentUser } = this.props;
+ const { libraryEntry, libraryType, currentUser } = this.props;
const { progressPercentage, isSliderActive } = this.state;
- const mediaData = data[libraryType];
+ const mediaData = libraryEntry[libraryType];
const canEdit = this.props.profile.id === this.props.currentUser.id;
const maxProgress = this.getMaxProgress();
@@ -157,59 +167,72 @@ export class UserLibraryListCard extends React.Component {
]}
>
-
-
-
-
-
- {mediaData.canonicalTitle}
+ { libraryEntry.status !== this.props.libraryStatus &&
+
+
+
+ Moved to
+ {HEADER_TEXT_MAPPING[libraryEntry.status][libraryType]}
+
- {canEdit && (
-
-
-
- )}
+
-
-
-
-
-
-
+ }
+
+
+
+
+
+
+ {mediaData.canonicalTitle}
+
+ {canEdit && (
+
+
+
+ )}
+
+
+
+
+
+
+
+
diff --git a/src/screens/Profiles/UserLibrary/components/UserLibraryListCard/styles.js b/src/screens/Profiles/UserLibrary/components/UserLibraryListCard/styles.js
index f92a53bff..a4108d6c0 100644
--- a/src/screens/Profiles/UserLibrary/components/UserLibraryListCard/styles.js
+++ b/src/screens/Profiles/UserLibrary/components/UserLibraryListCard/styles.js
@@ -1,6 +1,6 @@
import { StyleSheet } from 'react-native';
import * as colors from 'kitsu/constants/colors';
-import { commonStyles } from 'kitsu/common/styles';
+import { commonStyles, flattenCommon } from 'kitsu/common/styles';
const MENU_BUTTON_WIDTH = 24;
@@ -9,7 +9,6 @@ export const styles = StyleSheet.create({
backgroundColor: colors.white,
borderBottomWidth: 1,
borderColor: colors.lightGrey,
- flexDirection: 'row',
padding: 10,
},
content: {
@@ -19,12 +18,33 @@ export const styles = StyleSheet.create({
justifyContent: 'center',
paddingLeft: 10,
},
+ horizontalRule: {
+ borderColor: colors.lightGrey,
+ flex: 1,
+ borderTopWidth: 1,
+ margin: 5,
+ },
menuButton: {
width: MENU_BUTTON_WIDTH,
},
menuButtonContainer: {
marginLeft: 'auto',
},
+ metaDataContainer: {
+ flexDirection: 'row',
+ },
+ moved: {
+ ...flattenCommon('centerCenter'),
+ flexDirection: 'row',
+ marginBottom: 10,
+ },
+ movedText: {
+ color: colors.grey,
+ },
+ movedToText: {
+ fontWeight: '600',
+ color: colors.grey,
+ },
posterImage: {
borderRadius: 4,
},
diff --git a/src/screens/Profiles/UserLibrary/components/UserLibraryListScreen/component.js b/src/screens/Profiles/UserLibrary/components/UserLibraryListScreen/component.js
index 254009d24..89b664982 100644
--- a/src/screens/Profiles/UserLibrary/components/UserLibraryListScreen/component.js
+++ b/src/screens/Profiles/UserLibrary/components/UserLibraryListScreen/component.js
@@ -11,7 +11,7 @@ const HEADER_TEXT_MAPPING = {
current: { anime: 'Watching', manga: 'Reading' },
planned: { anime: 'Want To Watch', manga: 'Want to Read' },
completed: { anime: 'Complete', manga: 'Complete' },
- onHold: { anime: 'On Hold', manga: 'On Hold' },
+ on_hold: { anime: 'On Hold', manga: 'On Hold' },
dropped: { anime: 'Dropped', manga: 'Dropped' },
};
@@ -45,6 +45,7 @@ export class UserLibraryListScreenComponent extends React.Component {
};
state = {
+ movedEntries: [],
isSwiping: false,
}
@@ -61,6 +62,54 @@ export class UserLibraryListScreenComponent extends React.Component {
});
}, 100);
+ // wrap the dispatch with a function that checks for "moved" entries (ie: current -> completed)
+ // when a library entry is moved, add it the the moved entries array in state. once we
+ // re-render, we'll splice these back into the rendered data array so that we can show the user
+ // that they were moved to a new section, we keep the list of moved entries in local state because
+ // they've been completely removed from their respective arrays in redux and we only want to show
+ // that something has been moved until the user navigates away
+ updateUserLibraryEntry = async (type, status, updates) => {
+ const { libraryEntries, libraryStatus } = this.props;
+ const { movedEntries } = this.state;
+
+ let movedEntry;
+ let movedFromIndex;
+ const movedEntryIndex = movedEntries.findIndex(m => m.entry.id === updates.id);
+ // the first thing we want to check for is if the entry has already been moved. if it has,
+ // we want to remove it from the movedEntries array since properties are changing on it
+ if (movedEntryIndex > -1) {
+ movedEntry = movedEntries.splice(movedEntryIndex, 1)[0].entry;
+ movedFromIndex = movedEntry.index;
+ }
+
+ // if the library entry has been updated to have a status that is not for the list we're looking
+ // at, it needs to get added to the moved entry list.
+ if (updates.status !== libraryStatus) {
+ // if we're not dealing with an entry that's already moved once, go find the entry in
+ // the current library entry list
+ if (!movedEntry) {
+ movedFromIndex = libraryEntries.data.findIndex(
+ libraryEntry => libraryEntry.id === updates.id,
+ );
+ movedEntry = libraryEntries.data[movedFromIndex];
+ }
+
+ // finally push the moved entry onto the movedEntries array in state and override it with
+ // the updates
+ movedEntries.push({
+ entry: {
+ ...movedEntry,
+ ...updates,
+ },
+ index: movedFromIndex,
+ });
+ }
+
+ await this.props.updateUserLibraryEntry(type, status, updates);
+
+ this.setState({ movedEntries });
+ }
+
renderSearchBar = () => {
const { profile } = this.props.navigation.state.params;
@@ -76,25 +125,29 @@ export class UserLibraryListScreenComponent extends React.Component {
renderItem = ({ item }) => (
);
render() {
const { libraryEntries } = this.props;
+ const renderData = libraryEntries.data.slice();
+ this.state.movedEntries.forEach(({ entry, index }) => {
+ renderData.splice(index, 0, entry);
+ });
return (
{
+ const { userLibrary } = this.props;
+ const { data, loading } = userLibrary[type][status];
+
+ if (loading && data.length) {
+ return (
+
+
+
+ );
+ }
+
+ return null;
+ }
+
renderLists = (type) => {
const { userLibrary, navigation } = this.props;
const listOrder = [
{ status: 'current', anime: 'Watching', manga: 'Reading' },
{ status: 'planned', anime: 'Want To Watch', manga: 'Want To Read' },
{ status: 'completed', anime: 'Completed', manga: 'Completed' },
- { status: 'onHold', anime: 'On Hold', manga: 'On Hold' },
+ { status: 'on_hold', anime: 'On Hold', manga: 'On Hold' },
{ status: 'dropped', anime: 'Dropped', manga: 'Dropped' },
];
@@ -206,6 +222,7 @@ export class UserLibraryScreenComponent extends React.Component {
this.renderEmptyList(type, status)
:
async (dispatch, getState)
const filter = {
userId: options.userId,
- status: options.status === 'onHold' ? 'on_hold' : options.status,
+ status: options.status,
kind: options.library,
};
@@ -221,16 +221,19 @@ export const fetchUserLibrary = fetchOptions => async (dispatch, getState) => {
try {
await Promise.all([
- fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'anime', status: 'completed' })(dispatch, getState),
fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'anime', status: 'current' })(dispatch, getState),
- fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'anime', status: 'dropped' })(dispatch, getState),
- fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'anime', status: 'onHold' })(dispatch, getState),
fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'anime', status: 'planned' })(dispatch, getState),
- fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'manga', status: 'completed' })(dispatch, getState),
+ fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'anime', status: 'completed' })(dispatch, getState),
+ ]);
+
+ await Promise.all([
+ fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'anime', status: 'on_hold' })(dispatch, getState),
+ fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'anime', status: 'dropped' })(dispatch, getState),
fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'manga', status: 'current' })(dispatch, getState),
- fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'manga', status: 'dropped' })(dispatch, getState),
- fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'manga', status: 'onHold' })(dispatch, getState),
fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'manga', status: 'planned' })(dispatch, getState),
+ fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'manga', status: 'completed' })(dispatch, getState),
+ fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'manga', status: 'on_hold' })(dispatch, getState),
+ fetchUserLibraryByType({ ...fetchUserTypeOptions, library: 'manga', status: 'dropped' })(dispatch, getState),
]);
dispatch({
@@ -307,17 +310,14 @@ export const updateUserLibraryEntry = (
try {
const updateEntry = { ...newLibraryEntry };
- if (updateEntry.status === 'onHold') {
- updateEntry.status = 'on_hold';
- }
// optimistically update state
dispatch({
libraryStatus,
libraryType,
- previousLibraryStatus: previousLibraryEntry.status === 'on_hold' ? 'onHold' : previousLibraryEntry.status,
- newLibraryStatus: newLibraryEntry.status === 'on_hold' ? 'onHold' : newLibraryEntry.status,
+ previousLibraryStatus: previousLibraryEntry.status,
+ newLibraryStatus: newLibraryEntry.status,
previousLibraryEntry,
newLibraryEntry: updateEntry,
diff --git a/src/store/profile/reducer.js b/src/store/profile/reducer.js
index 50eae9463..e3adc03fa 100644
--- a/src/store/profile/reducer.js
+++ b/src/store/profile/reducer.js
@@ -23,14 +23,14 @@ const userLibraryInitial = {
completed: { data: [], loading: false },
current: { data: [], loading: false },
dropped: { data: [], loading: false },
- onHold: { data: [], loading: false },
+ on_hold: { data: [], loading: false },
planned: { data: [], loading: false },
},
manga: {
completed: { data: [], loading: false },
current: { data: [], loading: false },
dropped: { data: [], loading: false },
- onHold: { data: [], loading: false },
+ on_hold: { data: [], loading: false },
planned: { data: [], loading: false },
},
};
@@ -192,7 +192,7 @@ export const profileReducer = (state = INITIAL_STATE, action) => {
),
},
- // add to newLibraryEntry.status (newLibraryStatus alias on_hold to onHold for us)
+ // add to newLibraryEntry.status
[action.newLibraryStatus]: {
...state.userLibrary[action.libraryType][action.newLibraryStatus],
data: [
@@ -310,7 +310,7 @@ export const profileReducer = (state = INITIAL_STATE, action) => {
),
},
- // add to newLibraryEntry.status (newLibraryStatus alias on_hold to onHold for us)
+ // add to newLibraryEntry.status (newLibraryStatus alias on_hold to on_hold for us)
[action.newLibraryStatus]: {
...state.userLibrarySearch[action.libraryType][action.newLibraryStatus],
data: [