Skip to content

Commit

Permalink
Edit library entry (#61)
Browse files Browse the repository at this point in the history
* adding user library search screen

* adding simple header component

* wip adding edit entry screen

* wiring up more pieces of the edit entry screen

* fixing multiple action calls on swipeable

* edit user library screen should take entire screen
  • Loading branch information
joshmadewell authored Sep 20, 2017
1 parent 896f8ee commit 0b488d1
Show file tree
Hide file tree
Showing 16 changed files with 470 additions and 27 deletions.
3 changes: 3 additions & 0 deletions src/common/styles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export const commonStyles = StyleSheet.create({
fontSize: 12,
fontWeight: '600',
},
textSmall: {
fontSize: 10,
},
textHeavy: {
fontWeight: '800',
},
Expand Down
8 changes: 4 additions & 4 deletions src/components/ProfileHeader/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const ProfileHeader = (
<View style={styles.headerWrapper}>
<View style={[styles.header]}>
{/* if there is no follow button, render the absolute-centered header first
so that the back button still lays over top of it.*/}
so that the back button still lays over top of it. */}
{!showFollowButton && (
<View style={styles.titleOnlyContainer}>
<Text style={[
Expand Down Expand Up @@ -67,9 +67,9 @@ export const ProfileHeader = (
</TouchableOpacity>

{showFollowButton && (
<TouchableOpacity transparent style={styles.followButton} onPress={goBack}>
<Text style={[commonStyles.text, commonStyles.colorWhite]}>Follow</Text>
</TouchableOpacity>
<TouchableOpacity transparent style={styles.followButton} onPress={goBack}>
<Text style={[commonStyles.text, commonStyles.colorWhite]}>Follow</Text>
</TouchableOpacity>
)}
</View>
</View>
Expand Down
4 changes: 2 additions & 2 deletions src/components/SimpleHeader/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export const SimpleHeader = (props) => {
const RightWrapper = props.rightAction ? TouchableOpacity : View;

const leftAction = props.leftAction ? () => props.leftAction() : null;
const titleAction = props.titleAction ? () => props.leftAction() : null;
const rightAction = props.rightAction ? () => props.leftAction() : null;
const titleAction = props.titleAction ? () => props.titleAction() : null;
const rightAction = props.rightAction ? () => props.rightAction() : null;

return (
<View style={styles.headerContainer}>
Expand Down
3 changes: 3 additions & 0 deletions src/routes/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ const ProfileStack = DismissableStackNavigator(
UserLibrary: {
screen: ProfileScreens.UserLibraryScreen,
},
UserLibraryEdit: {
screen: ProfileScreens.UserLibraryEditScreen,
},
UserLibraryList: {
screen: ProfileScreens.UserLibraryListScreen,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import moment from 'moment';
import { Text, TextInput, View } from 'react-native';
import { STATUS_SELECT_OPTIONS } from 'kitsu/screens/Profiles/UserLibrary';
import { Counter } from 'kitsu/components/Counter';
import { Rating } from 'kitsu/components/Rating';
import { SimpleHeader } from 'kitsu/components/SimpleHeader';
import { SelectMenu } from 'kitsu/components/SelectMenu';
import { styles } from './styles';

const visibilityOptions = [
{ text: 'Private', value: true },
{ text: 'Public', value: false },
{ text: 'Nevermind', value: 'cancel' },
];

export class UserLibraryEditScreenComponent extends React.Component {
static propTypes = {
navigation: PropTypes.object.isRequired,
deleteUserLibraryEntry: PropTypes.func.isRequired,
}

static navigationOptions = () => ({
headerStyle: {
shadowColor: 'transparent',
elevation: 0,
},
header: null,
});

state = {
finishedAt: this.getLibraryEntry().finishedAt,
notes: this.getLibraryEntry().notes,
private: this.getLibraryEntry().private,
progress: this.getLibraryEntry().progress,
ratingTwenty: this.getLibraryEntry().ratingTwenty,
reconsumeCount: this.getLibraryEntry().reconsumeCount,
startedAt: this.getLibraryEntry().startedAt,
status: this.getLibraryEntry().status,
}

onFinishedAtChanged = (finishedAt) => {
this.setState({ finishedAt });
}

onNotesChanged = (notes) => {
this.setState({ notes });
}

onVisibilityChanged = (isPrivate) => {
this.setState({ private: isPrivate });
}

onProgressValueChanged = (progress) => {
this.setState({ progress });
}

onRatingChanged = (ratingTwenty) => {
this.setState({ ratingTwenty });
}

onTimesRewatchedChanged = (timesRewatched) => {
this.setState({ timesRewatched });
}

onStartedAtChanged = (startedAt) => {
this.setState({ startedAt });
}

onStatusChanged = (libraryStatus) => {
this.setState({ libraryStatus });
}

onDeleteEntry = async () => {
const { libraryEntry, libraryType } = this.props.navigation.state.params;
await this.props.deleteUserLibraryEntry(libraryEntry.id, libraryEntry.status, libraryType);
this.props.navigation.goBack();
}

getMaxProgress() {
const { libraryEntry, libraryType } = this.props.navigation.state.params;
const mediaData = libraryEntry[libraryType];

if (mediaData.type === 'anime') {
return mediaData.episodeCount;
}

return mediaData.chapterCount;
}

getLibraryEntry() {
return this.props.navigation.state.params.libraryEntry;
}

selectOptions = STATUS_SELECT_OPTIONS.map(option => ({
value: option.value,
text: option[this.props.navigation.state.params.libraryType],
})).filter(option => option.value !== this.props.navigation.state.params.libraryStatus);

saveEntry = async () => {
// 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 {
libraryEntry,
libraryStatus,
libraryType,
updateUserLibraryEntry,
} = this.props.navigation.state.params;

const {
finishedAt,
notes,
private: isPrivate,
progress,
ratingTwenty,
reconsumeCount,
startedAt,
status,
} = this.state;

await updateUserLibraryEntry(libraryType, libraryStatus, {
id: libraryEntry.id,
finishedAt,
notes,
progress,
ratingTwenty,
reconsumeCount,
startedAt,
status,
private: isPrivate,
});

this.props.navigation.goBack();
}

render() {
const { canEdit, libraryEntry, libraryType, ratingSystem } = this.props.navigation.state.params;
const maxProgress = this.getMaxProgress();

return (
<View style={styles.container}>
<SimpleHeader
leftAction={this.props.navigation.goBack}
leftContent={canEdit ? 'Cancel' : 'Done'}
titleContent={canEdit ? 'Edit Entry' : 'Details'}
rightContent={canEdit ? 'Save' : null}
rightAction={this.saveEntry}
/>

<View style={styles.editRow}>
<View>
<Text style={[styles.editRowLabel, styles.withValueLabel]}>
Library Status
</Text>
<Text style={styles.editRowValue}>Currently Watching</Text>
</View>
{canEdit &&
<SelectMenu options={this.selectOptions} onOptionSelected={this.onStatusChanged}>
<Text>X</Text>
</SelectMenu>
}
</View>
<View style={styles.editRow}>
<Text style={styles.editRowLabel}>
{libraryType === 'anime' ? 'Episode' : 'Chapter'} Progress:
</Text>
<Counter
disabled={!canEdit}
initialValue={libraryEntry.progress}
maxValue={typeof maxProgress === 'number' ? maxProgress : undefined}
progressCounter={typeof maxProgress === 'number'}
onValueChanged={this.onProgressValueChanged}
/>
</View>
<View style={styles.editRow}>
<Text style={styles.editRowLabel}>Rating:</Text>
<Rating
disabled={!canEdit}
size="large"
viewType="single"
onRatingChanged={this.onRatingChanged}
style={styles.ratingStyle}
rating={libraryEntry.ratingTwenty}
ratingSystem={ratingSystem}
/>
</View>
<View style={styles.editRow}>
<Text style={styles.editRowLabel}>Times {libraryType === 'anime' ? 'Rewatched' : 'Read'}:</Text>
<Counter
disabled={!canEdit}
initialValue={libraryEntry.reconsumeCount}
onValueChanged={this.onTimesRewatchedChanged}
/>
</View>
<View style={styles.editRow}>
<View>
<Text style={[styles.editRowLabel, styles.withValueLabel, { paddingBottom: 4 }]}>
Library Entry Visibility
</Text>
<Text style={styles.editRowValue}>
{this.state.private ? 'Private' : 'Public'}
</Text>
</View>
{canEdit &&
<SelectMenu options={visibilityOptions} onOptionSelected={this.onVisibilityChanged}>
<Text>X</Text>
</SelectMenu>
}
</View>
<View style={styles.splitRow}>
<View style={[styles.editRow, styles.dateStarted]}>
<View>
<Text style={[
styles.editRowLabel,
this.state.startedAt && styles.withValueLabel,
]}
>
Date Started
</Text>
{this.state.startedAt &&
<Text style={styles.editRowValue}>
{moment(this.state.startedAt).format('MMM. DD, YYYY')}
</Text>
}
</View>
</View>
<View style={[styles.editRow, styles.dateFinished]}>
<View>
<Text style={[
styles.editRowLabel,
this.state.finishedAt && styles.withValueLabel,
]}
>
Date Finished
</Text>
{this.state.finishedAt &&
<Text style={styles.editRowValue}>
{moment(this.state.finishedAt).format('MMM. DD, YYYY')}
</Text>
}
</View>
</View>
</View>
<View style={styles.editRow}>
<View style={styles.notesSection}>
<Text style={[
styles.editRowLabel,
this.state.notes && styles.withValueLabel,
]}
>
Personal Notes
</Text>
{this.state.notes &&
<TextInput
value={this.state.notes}
onChangeText={this.onNotesChanged}
multiline
/>
}
</View>
</View>
{canEdit &&
<View style={[styles.deleteEntryRow]}>
<SelectMenu options={['Yes, Delete.', 'Nevermind']} onOptionSelected={this.onDeleteEntry}>
<Text style={styles.deleteEntryText}>Delete Library Entry</Text>
</SelectMenu>
</View>
}
</View>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { connect } from 'react-redux';
import { deleteUserLibraryEntry } from 'kitsu/store/profile/actions';
import { UserLibraryEditScreenComponent } from './component';

export const UserLibraryEditScreen = connect(null, {
deleteUserLibraryEntry,
})(UserLibraryEditScreenComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './container';
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { StyleSheet } from 'react-native';
import * as colors from 'kitsu/constants/colors';
import { flattenCommon } from 'kitsu/common/styles';

export const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.white,
},
dateStarted: {
flex: 1,
},
dateFinished: {
flex: 1,
borderLeftWidth: StyleSheet.hairlineWidth,
borderLeftColor: colors.lightGrey,
},
deleteEntry: {
borderBottomWidth: 0,
},
editRow: {
alignItems: 'center',
borderBottomColor: colors.lightGrey,
borderBottomWidth: StyleSheet.hairlineWidth,
flexDirection: 'row',
justifyContent: 'space-between',
maxHeight: 60,
minHeight: 60,
padding: 14,
},
editRowLabel: {
...flattenCommon('text'),
fontSize: 16,
fontWeight: '400',
color: colors.grey,
},
editRowValue: {
...flattenCommon('text'),
fontSize: 16,
fontWeight: '400',
},
splitRow: {
flexDirection: 'row',
},
notesSection: {
flex: 1,
},
deleteEntryRow: {
height: '100%',
padding: 14,
},
deleteEntryText: {
...flattenCommon('text'),
color: colors.red,
},
withValueLabel: {
...flattenCommon('textSmall'),
paddingBottom: 4,
},
});
Loading

0 comments on commit 0b488d1

Please sign in to comment.