From 80ec215be3ebdd72adf976a4e2ebfa1aaf49d459 Mon Sep 17 00:00:00 2001 From: natjms Date: Sun, 23 May 2021 01:33:13 -0300 Subject: [PATCH] Enable updating credentials from settings.js Using this interface on Pixelfed will erase your profile's metadata. See #26 for discussion and possible alternatives --- package-lock.json | 34 +++ package.json | 1 + src/components/pages/profile/settings.js | 252 ++++++++--------------- src/requests.js | 2 +- 4 files changed, 125 insertions(+), 164 deletions(-) diff --git a/package-lock.json b/package-lock.json index a26919a..21ab0a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@react-navigation/native": "5.1.1", "@react-navigation/stack": "5.2.3", "expo": "^38.0.9", + "expo-image-picker": "~8.3.0", "expo-linking": "^1.0.7", "expo-status-bar": "^1.0.2", "expo-web-browser": "~8.3.1", @@ -3608,6 +3609,23 @@ "fontfaceobserver": "^2.1.0" } }, + "node_modules/expo-image-picker": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-8.3.0.tgz", + "integrity": "sha512-HCF7SumPE4jXH+EhJ19Xw6G8Lxpms1z6Hxdq/kLlzvZGdzH4ZFBqp3NyTO7dtm0dXtjduljpNK7kQfwWtvmIWQ==", + "dependencies": { + "expo-permissions": "~9.0.1", + "uuid": "7.0.2" + } + }, + "node_modules/expo-image-picker/node_modules/uuid": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", + "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/expo-keep-awake": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-8.2.1.tgz", @@ -12340,6 +12358,22 @@ "fontfaceobserver": "^2.1.0" } }, + "expo-image-picker": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-8.3.0.tgz", + "integrity": "sha512-HCF7SumPE4jXH+EhJ19Xw6G8Lxpms1z6Hxdq/kLlzvZGdzH4ZFBqp3NyTO7dtm0dXtjduljpNK7kQfwWtvmIWQ==", + "requires": { + "expo-permissions": "~9.0.1", + "uuid": "7.0.2" + }, + "dependencies": { + "uuid": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", + "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==" + } + } + }, "expo-keep-awake": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-8.2.1.tgz", diff --git a/package.json b/package.json index ede1137..18024b2 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@react-navigation/native": "5.1.1", "@react-navigation/stack": "5.2.3", "expo": "^38.0.9", + "expo-image-picker": "~8.3.0", "expo-linking": "^1.0.7", "expo-status-bar": "^1.0.2", "expo-web-browser": "~8.3.1", diff --git a/src/components/pages/profile/settings.js b/src/components/pages/profile/settings.js index 1c57648..34ea97d 100644 --- a/src/components/pages/profile/settings.js +++ b/src/components/pages/profile/settings.js @@ -9,49 +9,22 @@ import { TouchableOpacity, Dimensions, } from "react-native"; +import { FontAwesome } from '@expo/vector-icons'; import AsyncStorage from "@react-native-async-storage/async-storage"; +import * as requests from "src/requests"; + +import * as ImagePicker from 'expo-image-picker'; import { withoutHTML } from "src/interface/rendering"; -import * as requests from "src/requests"; import { ScreenWithBackBarJsx } from "src/components/navigation/navigators"; -const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg"; -const TEST_PROFILE = { - username: "njms", - acct: "njms", - display_name: "Nat🔆", - locked: false, - bot: false, - note: "Yeah heart emoji.", - avatar: TEST_IMAGE, - followers_count: "1 jillion", - statuses_count: 334, - fields: [ - { - name: "Blog", - value: "https://njms.ca", - verified_at: "some time" - }, - { - name: "Github", - value: "https://github.com/natjms", - verified_at: null - } - ] -}; - const SettingsJsx = (props) => { const [state, setState] = useState({ - // Use Context to get this stuff eventually - profile: TEST_PROFILE, - newProfile: TEST_PROFILE, loaded: false, }); - const fields = state.newProfile.fields; - const _handleLogout = async () => { await requests.postForm( `https://${state.instance}/oauth/revoke`, @@ -96,24 +69,76 @@ const SettingsJsx = (props) => { setState({...state, profile: profile, - newProfile: newProfile, instance: instance, appObject: appObject, - token: token, + accessToken: token.access_token, + + // Malleable props that will actually go towards updating + // the profile credentials + locked: profile.locked, + newAvatar: { + uri: profile.avatar, + }, + display_name: profile.display_name, + note: profile.note, + loaded: true, }) }); }, []); + const _handleChangeProfilePhoto = async () => { + await ImagePicker.getCameraRollPermissionsAsync() + + const { base64, uri } = await ImagePicker.launchImageLibraryAsync({ + allowsEditing: true, + aspect: [1, 1], + }); + + setState({...state, + newAvatar: { + base64, + uri, + }, + }); + }; + + const _handleSaveProfile = async () => { + let params = { + display_name: state.display_name, + note: state.note, + locked: state.locked, + }; + if (state.newAvatar.base64) { + let blob = fetch(state.newAvatar.base64).then(res => res.blob()); + let filename = uri.split("/")[uri.split("/").length - 1]; + + params.avatar = new File([blob], filename); + } + + const newProfile = await fetch( + `https://${state.instance}/api/v1/accounts/update_credentials`, + { + method: "PATCH", + body: requests.objectToForm(params), + headers: { "Authorization": `Bearer ${state.accessToken}`, } + } + ).then(resp => resp.json()); + + await AsyncStorage.setItem("@user_profile", JSON.stringify(newProfile)); + + props.navigation.navigate("Profile"); + }; + return ( <> { state.loaded ? - + Change profile photo @@ -124,24 +149,11 @@ const SettingsJsx = (props) => { { setState({...state, - newProfile: {...state.newProfile, display_name: value} - }); - } - }/> - - User name - { - setState({...state, - newProfile: {...state.newProfile, username: value} + display_name: value, }); } }/> @@ -156,110 +168,40 @@ const SettingsJsx = (props) => { } multiline = { true } placeholder = { "Bio" } - value = { withoutHTML(state.newProfile.note) } + // HACK: Using withoutHTML here is dangerous + value = { withoutHTML(state.note) } onChangeText = { (value) => { setState({...state, - newProfile: {...state.newProfile, note: value} + note: value }); } }/> - { - fields.map((field, i) => - - { - let newFields; - if (fields.length == 1) { - newFields = [{ name: "", value: "" }]; - } else { - newFields = state.newProfile.fields; - newFields.splice(i, 1); - } - setState({...state, - newProfile: {...state.newProfile, - fields: newFields, - }, - }); - } - }> - - - - Name - { - let newFields = fields; - newFields[i] = {...newFields[i], - name: text, - }; - - setState({...state, - newProfile: {...state.newProfile, - fields: newFields, - }, - }); - } - } /> - - - Value - { - let newFields = fields; - newFields[i] = {...newFields[i], - value: text, - }; - - setState({...state, - newProfile: {...state.newProfile, - fields: newFields, - }, - }); - } - } /> - - - ) - } { + () => ( setState({...state, - newProfile: {...state.newProfile, - fields: state.newProfile.fields.concat({ name: "", value: ""}), - }, - }); - } + locked: !state.locked + }) + ) }> - + + <> + { !state.locked + ? + : + } + + + Manually approve follow requests? + + - + + Save Profile