diff --git a/src/state/models/profile-view.ts b/src/state/models/profile-view.ts index 927374cc67..013a1b04fc 100644 --- a/src/state/models/profile-view.ts +++ b/src/state/models/profile-view.ts @@ -6,6 +6,7 @@ import {extractEntities} from '../../lib/strings' import {Declaration} from './_common' import {RootStoreModel} from './root-store' import * as apilib from '../lib/api' +import {Image} from 'react-native-image-crop-picker' export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser' export const ACTOR_TYPE_SCENE = 'app.bsky.system.actorScene' @@ -46,6 +47,10 @@ export class ProfileViewModel { // added data descriptionEntities?: Entity[] + // temp state while imagery is not supported by the protocol + userAvatar: Image | null = null + userBanner: Image | null = null + constructor( public rootStore: RootStoreModel, params: GetProfile.QueryParams, @@ -81,6 +86,14 @@ export class ProfileViewModel { return this.declaration.actorType === ACTOR_TYPE_SCENE } + get userAvatarPath() { + return this.userAvatar?.path + } + + get userBannerPath() { + return this.userBanner?.path + } + // public api // = @@ -123,6 +136,14 @@ export class ProfileViewModel { // state transitions // = + updateUserAvatar(img: Image) { + this.userAvatar = img + } + + updateUserBanner(img: Image) { + this.userBanner = img + } + private _xLoading(isRefreshing = false) { this.isLoading = true this.isRefreshing = isRefreshing diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx index b330120664..0a305115d6 100644 --- a/src/view/com/modals/EditProfile.tsx +++ b/src/view/com/modals/EditProfile.tsx @@ -70,14 +70,20 @@ export function Component({ Edit my profile - + diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index b271fc8b6a..e31c51418d 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -32,15 +32,15 @@ export const ProfileHeader = observer(function ProfileHeader({ const store = useStores() const isMember = useMemo( () => view.isScene && view.myState.member, - [view.myState.member], + [view.isScene, view.myState.member], ) - const onPressBack = () => { - store.nav.tab.goBack() - } - const onPressSearch = () => { - store.nav.navigate(`/search`) - } + // const onPressBack = () => { + // store.nav.tab.goBack() + // } + // const onPressSearch = () => { + // store.nav.navigate('/search') + // } const onPressToggleFollow = () => { view?.toggleFollowing().then( () => { @@ -72,7 +72,7 @@ export const ProfileHeader = observer(function ProfileHeader({ store.shell.openModal( new ConfirmModel( 'Leave this scene?', - `You'll be able to come back unless your invite is revoked.`, + "You'll be able to come back unless your invite is revoked.", onPressConfirmLeaveScene, ), ) @@ -83,7 +83,7 @@ export const ProfileHeader = observer(function ProfileHeader({ did: store.me.did || '', rkey: new AtUri(view.myState.member).rkey, }) - Toast.show(`Scene left`) + Toast.show('Scene left') } onRefreshAll() } @@ -152,13 +152,14 @@ export const ProfileHeader = observer(function ProfileHeader({ } return ( - + diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index 93500d5a45..566d4f6c90 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useState} from 'react' +import React, {useCallback} from 'react' import {StyleSheet, View, TouchableOpacity, Alert, Image} from 'react-native' import Svg, {Circle, Text, Defs, LinearGradient, Stop} from 'react-native-svg' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' @@ -10,11 +10,12 @@ import { import {getGradient} from '../../lib/asset-gen' import {colors} from '../../lib/styles' import {IMAGES_ENABLED} from '../../../build-flags' -import {loadString, saveString} from '../../../state/lib/storage' +import {ProfileViewModel} from '../../../state/models/profile-view' export function UserAvatar({ size, handle, + profileView, displayName, isMe = false, isEditable = false, @@ -24,23 +25,11 @@ export function UserAvatar({ isMe?: boolean isEditable?: boolean displayName: string | undefined + profileView: ProfileViewModel }) { const initials = getInitials(displayName || handle) const gradient = getGradient(handle) - const [userAvatarImage, setUserAvatarImage] = useState(null) - - useEffect(() => { - async function loadLocalImage() { - if (isMe) { - await loadString('userAvatarImage').then(uri => { - setUserAvatarImage(uri) - }) - } - } - loadLocalImage() - }, [isMe]) - const handleEditAvatar = useCallback(() => { Alert.alert('Select upload method', '', [ { @@ -53,7 +42,7 @@ export function UserAvatar({ height: 80, cropperCircleOverlay: true, }).then(item => { - saveString('userAvatarImage', item.path) + profileView.updateUserAvatar(item) }) }, }, @@ -70,13 +59,13 @@ export function UserAvatar({ height: 80, cropperCircleOverlay: true, }).then(croppedItem => { - saveString('userAvatarImage', croppedItem.path) + profileView.updateUserAvatar(croppedItem) }) }) }, }, ]) - }, []) + }, [profileView]) const renderSvg = (size: number, initials: string) => ( @@ -102,8 +91,11 @@ export function UserAvatar({ return isEditable && IMAGES_ENABLED ? ( {/* Added a react state temporary photo while the protocol does not support imagery */} - {userAvatarImage != null ? ( - + {profileView.userAvatar != null ? ( + ) : ( renderSvg(size, initials) )} @@ -115,11 +107,11 @@ export function UserAvatar({ /> - ) : isMe && userAvatarImage != null ? ( + ) : isMe && profileView.userAvatar != null ? ( ) : ( renderSvg(size, initials) diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx index 8809d0b92f..db37beb5d3 100644 --- a/src/view/com/util/UserBanner.tsx +++ b/src/view/com/util/UserBanner.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useState} from 'react' +import React, {useCallback} from 'react' import {StyleSheet, View, TouchableOpacity, Alert, Image} from 'react-native' import Svg, {Rect, Defs, LinearGradient, Stop} from 'react-native-svg' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' @@ -10,32 +10,21 @@ import { openPicker, } from 'react-native-image-crop-picker' import {IMAGES_ENABLED} from '../../../build-flags' -import {saveString, loadString} from '../../../state/lib/storage' +import {ProfileViewModel} from '../../../state/models/profile-view' export function UserBanner({ handle, + profileView, isMe = false, isEditable = false, }: { handle: string isMe?: boolean + profileView: ProfileViewModel isEditable?: boolean }) { const gradient = getGradient(handle) - const [userBannerImage, setUserBannerImage] = useState(null) - - useEffect(() => { - async function loadLocalImage() { - if (isMe) { - await loadString('userBannerImage').then(uri => { - setUserBannerImage(uri) - }) - } - } - loadLocalImage() - }, [isMe]) - const handleEditBanner = useCallback(() => { Alert.alert('Select upload method', '', [ { @@ -47,7 +36,7 @@ export function UserBanner({ width: 1500, height: 500, }).then(item => { - saveString('userBannerImage', item.path) + profileView.updateUserBanner(item) }) }, }, @@ -63,13 +52,13 @@ export function UserBanner({ width: 1500, height: 500, }).then(croppedItem => { - saveString('userBannerImage', croppedItem.path) + profileView.updateUserBanner(croppedItem) }) }) }, }, ]) - }, []) + }, [profileView]) const renderSvg = () => ( @@ -91,11 +80,11 @@ export function UserBanner({ return isEditable && IMAGES_ENABLED ? ( {/* Added a react state temporary photo while the protocol does not support imagery */} - {userBannerImage != null ? ( + {profileView.userBanner != null ? ( ) : ( renderSvg() @@ -108,11 +97,11 @@ export function UserBanner({ /> - ) : isMe && userBannerImage != null ? ( + ) : isMe && profileView.userBanner != null ? ( ) : ( renderSvg()