adding debounce and validation to login update

This commit is contained in:
Roland Osborne 2022-09-22 12:23:49 -07:00
parent 18716162cc
commit 4591759c2c
7 changed files with 148 additions and 29 deletions

View File

@ -39,7 +39,7 @@ export function Create() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="database" size={18} color="#888888" />
<TextInput style={styles.inputfield} value={state.server} onChangeText={actions.setServer}
autoCapitalize="none" placeholder="server" />
autoCorrect={false} autoCapitalize="none" placeholder="server" />
<View style={styles.space}>
{ (!state.server || !state.serverChecked) && (
<Text style={styles.required}></Text>
@ -57,7 +57,7 @@ export function Create() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="key" size={18} color="#888888" />
<TextInput style={styles.inputfield} value={state.token} onChangeText={actions.setToken}
autoCapitalize="none" placeholder="token" />
autoCorrect={false} autoCapitalize="none" placeholder="token" />
<View style={styles.space}>
{ (!validServer || !state.token || !state.tokenChecked) && (
<Text style={styles.required}></Text>
@ -75,7 +75,7 @@ export function Create() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="user" size={18} color="#888888" />
<TextInput style={styles.inputfield} value={state.username} onChangeText={actions.setUsername}
autoCapitalize="none" placeholder="username" />
autoCorrect={false} autoCapitalize="none" placeholder="username" />
<View style={styles.space}>
{ (!validServer || !validToken || !state.username || !state.usernameChecked) && (
<Text style={styles.required}></Text>
@ -92,7 +92,7 @@ export function Create() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="lock" size={18} color="#888888" />
<TextInput style={styles.inputfield} value={state.password} onChangeText={actions.setPassword}
autoCapitalize="none" placeholder="password" />
autoCorrect={false} autoCapitalize="none" placeholder="password" />
<TouchableOpacity onPress={actions.hidePassword}>
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
</TouchableOpacity>
@ -102,7 +102,7 @@ export function Create() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="lock" size={18} color="#888888" />
<TextInput style={styles.inputfield} value={state.password} onChangeText={actions.setPassword}
secureTextEntry={true} autoCapitalize="none" placeholder="password" />
autoCorrect={false} secureTextEntry={true} autoCapitalize="none" placeholder="password" />
<TouchableOpacity onPress={actions.showPassword}>
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
</TouchableOpacity>
@ -112,7 +112,7 @@ export function Create() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="lock" size={18} color="#888888" />
<TextInput style={styles.inputfield} value={state.confirm} onChangeText={actions.setConfirm}
autoCapitalize="none" placeholder="confirm password" />
autoCorrect={false} autoCapitalize="none" placeholder="confirm password" />
<TouchableOpacity onPress={actions.hideConfirm}>
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
</TouchableOpacity>
@ -122,7 +122,7 @@ export function Create() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="lock" size={18} color="#888888" />
<TextInput style={styles.inputfield} value={state.confirm} onChangeText={actions.setConfirm}
secureTextEntry={true} autoCapitalize="none" placeholder="confirm password" />
autoCorrect={false} secureTextEntry={true} autoCapitalize="none" placeholder="confirm password" />
<TouchableOpacity onPress={actions.showConfirm}>
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
</TouchableOpacity>

View File

@ -35,14 +35,14 @@ export function Login() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="user" size={18} color="#aaaaaa" />
<TextInput style={styles.inputfield} value={state.login} onChangeText={actions.setLogin}
autoCapitalize="none" placeholder="username@server" />
autoCorrect={false} autoCapitalize="none" placeholder="username@server" />
<View style={styles.space} />
</View>
{ state.showPassword && (
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="lock" size={18} color="#aaaaaa" />
<TextInput style={styles.inputfield} value={state.password} onChangeText={actions.setPassword}
autoCapitalize="none" placeholder="password"/>
autoCorrect={false} autoCapitalize="none" placeholder="password"/>
<TouchableOpacity onPress={actions.hidePassword}>
<Ionicons style={styles.icon} name="eye" size={18} color="#aaaaaa" />
</TouchableOpacity>
@ -52,7 +52,7 @@ export function Login() {
<View style={styles.inputwrapper}>
<Ionicons style={styles.icon} name="lock" size={18} color="#aaaaaa" />
<TextInput style={styles.inputfield} value={state.password} onChangeText={actions.setPassword}
secureTextEntry={true} autoCapitalize="none" placeholder="password" />
autoCorrect={false} secureTextEntry={true} autoCapitalize="none" placeholder="password" />
<TouchableOpacity onPress={actions.showPassword}>
<Ionicons style={styles.icon} name="eyeo" size={18} color="#aaaaaa" />
</TouchableOpacity>

View File

@ -0,0 +1,8 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function getHandle(server, token, name) {
let available = await fetchWithTimeout(`https://${server}/account/username?agent=${token}&name=${encodeURIComponent(name)}`, { method: 'GET' })
checkResponse(available)
return await available.json()
}

View File

@ -3,6 +3,7 @@ import { getProfile } from 'api/getProfile';
import { setProfileData } from 'api/setProfileData';
import { setProfileImage } from 'api/setProfileImage';
import { getProfileImageUrl } from 'api/getProfileImageUrl';
import { getHandle } from 'api/getHandle';
import { StoreContext } from 'context/StoreContext';
export function useProfileContext() {
@ -71,6 +72,10 @@ export function useProfileContext() {
const { server, appToken } = session.current;
await setProfileImage(server, appToken, image);
},
getHandle: async (name) => {
const { server, appToken } = session.current;
return await getHandle(server, appToken, name);
},
}
return { state, actions }

View File

@ -39,6 +39,17 @@ export function Profile() {
}
const saveLogin = async () => {
try {
await actions.saveLogin();
actions.hideLoginEdit();
}
catch (err) {
console.log(err);
Alert.alert(
'Failed to Change Login',
'Please try again.'
)
}
}
const onGallery = async () => {
@ -63,6 +74,8 @@ export function Profile() {
}
}
const enabled = (state.checked && state.available && state.editConfirm === state.editPassword && state.editPassword);
return (
<ScrollView>
<View style={styles.container}>
@ -113,15 +126,15 @@ export function Profile() {
<Text style={styles.editHeader}>Edit Details:</Text>
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editName} onChangeText={actions.setEditName}
autoCapitalize="word" placeholder="Name" />
autoCapitalize="words" placeholder="Name" />
</View>
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editLocation} onChangeText={actions.setEditLocation}
autoCapitalize="sentence" placeholder="Location" />
autoCapitalize="words" placeholder="Location" />
</View>
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editDescription} onChangeText={actions.setEditDescription}
autoCapitalize="none" placeholder="Description" multiline={true} />
autoCapitalize="sentences" placeholder="Description" multiline={true} />
</View>
<View style={styles.editControls}>
<TouchableOpacity style={styles.cancel} onPress={actions.hideDetailEdit}>
@ -146,23 +159,64 @@ export function Profile() {
<Text style={styles.editHeader}>Change Login:</Text>
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editHandle} onChangeText={actions.setEditHandle}
placeholder="Username" />
</View>
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editPassword} onChangeText={actions.setEditPassword}
secureTextEntry={true} placeholder="Password" />
</View>
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editConfirm} onChangeText={actions.setEditConfirm}
secureTextEntry={true} placeholder="Confirm Password" />
autoCapitalize={'none'} placeholder="Username" />
{ state.checked && state.available && (
<Ionicons style={styles.icon} name="checkcircleo" size={18} color={Colors.background} />
)}
{ state.checked && !state.available && (
<Ionicons style={styles.icon} name="exclamationcircleo" size={18} color={Colors.alert} />
)}
</View>
{ !state.showPassword && (
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editPassword} onChangeText={actions.setEditPassword}
autoCapitalize={'none'} secureTextEntry={true} placeholder="Password" />
<TouchableOpacity onPress={actions.showPassword}>
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
</TouchableOpacity>
</View>
)}
{ state.showPassword && (
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editPassword} onChangeText={actions.setEditPassword}
autoCapitalize={'none'} secureTextEntry={false} placeholder="Password" />
<TouchableOpacity onPress={actions.hidePassword}>
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
</TouchableOpacity>
</View>
)}
{ !state.showConfirm && (
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editConfirm} onChangeText={actions.setEditConfirm}
autoCapitalize={'none'} secureTextEntry={true} placeholder="Confirm" />
<TouchableOpacity onPress={actions.showConfirm}>
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
</TouchableOpacity>
</View>
)}
{ state.showConfirm && (
<View style={styles.inputField}>
<TextInput style={styles.input} value={state.editConfirm} onChangeText={actions.setEditConfirm}
autoCapitalize={'none'} secureTextEntry={false} placeholder="Confirm" />
<TouchableOpacity onPress={actions.hideConfirm}>
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
</TouchableOpacity>
</View>
)}
<View style={styles.editControls}>
<TouchableOpacity style={styles.cancel} onPress={actions.hideLoginEdit}>
<Text>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.save} onPress={saveLogin}>
<Text style={styles.saveText}>Save</Text>
</TouchableOpacity>
{ enabled && (
<TouchableOpacity style={styles.save} onPress={saveLogin}>
<Text style={styles.saveText}>Save</Text>
</TouchableOpacity>
)}
{ !enabled && (
<View style={styles.disabled}>
<Text style={styles.disabledText}>Save</Text>
</View>
)}
</View>
</View>
</View>

View File

@ -128,10 +128,12 @@ export const styles = StyleSheet.create({
padding: 8,
marginBottom: 8,
maxHeight: 92,
display: 'flex',
flexDirection: 'row',
},
input: {
fontSize: 16,
width: '100%',
flexGrow: 1,
},
editControls: {
display: 'flex',
@ -148,6 +150,18 @@ export const styles = StyleSheet.create({
display: 'flex',
alignItems: 'center',
},
disabled: {
borderWidth: 1,
borderColor: Colors.lightgrey,
padding: 8,
borderRadius: 4,
width: 72,
display: 'flex',
alignItems: 'center',
},
disabledText: {
color: Colors.disabled,
},
save: {
padding: 8,
borderRadius: 4,

View File

@ -22,12 +22,17 @@ export function useProfile() {
editHandle: null,
editPassword: null,
editConfirm: null,
checked: true,
available: true,
showPassword: false,
showConfirm: false,
});
const app = useContext(AppContext);
const account = useContext(AccountContext);
const profile = useContext(ProfileContext);
const navigate = useNavigate();
const debounce = useRef(null);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -76,8 +81,38 @@ export function useProfile() {
setEditDescription: (editDescription) => {
updateState({ editDescription });
},
showPassword: () => {
updateState({ showPassword: true });
},
hidePassword: () => {
updateState({ showPassword: false });
},
showConfirm: () => {
updateState({ showConfirm: true });
},
hideConfirm: () => {
updateState({ showConfirm: false });
},
setEditHandle: (editHandle) => {
updateState({ editHandle });
updateState({ editHandle, checked: false });
if (debounce.current != null) {
clearTimeout(debounce.current);
}
debounce.current = setTimeout(async () => {
try {
if (editHandle === state.handle) {
updateState({ available: true, checked: true });
}
else {
const available = await profile.actions.getHandle(editHandle);
updateState({ available, checked: true });
}
}
catch (err) {
console.log(err);
}
}, 1000);
},
setEditPassword: (editPassword) => {
updateState({ editPassword });
@ -85,8 +120,11 @@ export function useProfile() {
setEditConfirm: (editConfirm) => {
updateState({ editConfirm });
},
saveDetails: () => {
profile.actions.setProfileData(state.editName, state.editLocation, state.editDescription);
saveDetails: async () => {
await profile.actions.setProfileData(state.editName, state.editLocation, state.editDescription);
},
saveLogin: async () => {
await account.actions.setLogin(state.editHandle, state.editPassword);
},
};