about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/state/models/shell-ui.ts45
-rw-r--r--src/view/com/lightbox/Lightbox.tsx62
-rw-r--r--src/view/com/lightbox/ProfileImage.tsx26
-rw-r--r--src/view/com/modals/Modal.tsx8
-rw-r--r--src/view/com/notifications/InviteAccepter.tsx4
-rw-r--r--src/view/com/profile/ProfileHeader.tsx36
-rw-r--r--src/view/com/util/DropdownBtn.tsx4
-rw-r--r--src/view/screens/Login.tsx6
-rw-r--r--src/view/screens/Profile.tsx4
-rw-r--r--src/view/shell/mobile/Menu.tsx4
-rw-r--r--src/view/shell/mobile/index.tsx2
11 files changed, 154 insertions, 47 deletions
diff --git a/src/state/models/shell-ui.ts b/src/state/models/shell-ui.ts
index bb4bf1ffa..3199e421e 100644
--- a/src/state/models/shell-ui.ts
+++ b/src/state/models/shell-ui.ts
@@ -1,7 +1,7 @@
 import {makeAutoObservable} from 'mobx'
 import {ProfileViewModel} from './profile-view'
 
-export class ConfirmModel {
+export class ConfirmModal {
   name = 'confirm'
 
   constructor(
@@ -13,7 +13,7 @@ export class ConfirmModel {
   }
 }
 
-export class EditProfileModel {
+export class EditProfileModal {
   name = 'edit-profile'
 
   constructor(
@@ -24,7 +24,7 @@ export class EditProfileModel {
   }
 }
 
-export class CreateSceneModel {
+export class CreateSceneModal {
   name = 'create-scene'
 
   constructor() {
@@ -32,7 +32,7 @@ export class CreateSceneModel {
   }
 }
 
-export class InviteToSceneModel {
+export class InviteToSceneModal {
   name = 'invite-to-scene'
 
   constructor(public profileView: ProfileViewModel) {
@@ -40,7 +40,7 @@ export class InviteToSceneModel {
   }
 }
 
-export class ServerInputModel {
+export class ServerInputModal {
   name = 'server-input'
 
   constructor(
@@ -51,6 +51,13 @@ export class ServerInputModel {
   }
 }
 
+export class ProfileImageLightbox {
+  name = 'profile-image'
+  constructor(public profileView: ProfileViewModel) {
+    makeAutoObservable(this)
+  }
+}
+
 export interface ComposerOptsPostRef {
   uri: string
   cid: string
@@ -70,11 +77,13 @@ export class ShellUiModel {
   isMainMenuOpen = false
   isModalActive = false
   activeModal:
-    | ConfirmModel
-    | EditProfileModel
-    | CreateSceneModel
-    | ServerInputModel
+    | ConfirmModal
+    | EditProfileModal
+    | CreateSceneModal
+    | ServerInputModal
     | undefined
+  isLightboxActive = false
+  activeLightbox: ProfileImageLightbox | undefined
   isComposerActive = false
   composerOpts: ComposerOpts | undefined
 
@@ -88,10 +97,10 @@ export class ShellUiModel {
 
   openModal(
     modal:
-      | ConfirmModel
-      | EditProfileModel
-      | CreateSceneModel
-      | ServerInputModel,
+      | ConfirmModal
+      | EditProfileModal
+      | CreateSceneModal
+      | ServerInputModal,
   ) {
     this.isModalActive = true
     this.activeModal = modal
@@ -102,6 +111,16 @@ export class ShellUiModel {
     this.activeModal = undefined
   }
 
+  openLightbox(lightbox: ProfileImageLightbox) {
+    this.isLightboxActive = true
+    this.activeLightbox = lightbox
+  }
+
+  closeLightbox() {
+    this.isLightboxActive = false
+    this.activeLightbox = undefined
+  }
+
   openComposer(opts: ComposerOpts) {
     this.isComposerActive = true
     this.composerOpts = opts
diff --git a/src/view/com/lightbox/Lightbox.tsx b/src/view/com/lightbox/Lightbox.tsx
new file mode 100644
index 000000000..9432f0151
--- /dev/null
+++ b/src/view/com/lightbox/Lightbox.tsx
@@ -0,0 +1,62 @@
+import React from 'react'
+import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import {observer} from 'mobx-react-lite'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {useStores} from '../../../state'
+
+import * as models from '../../../state/models/shell-ui'
+
+import * as ProfileImageLightbox from './ProfileImage'
+
+export const Lightbox = observer(function Lightbox() {
+  const store = useStores()
+
+  const onClose = () => {
+    store.shell.closeLightbox()
+  }
+
+  if (!store.shell.isLightboxActive) {
+    return <View />
+  }
+
+  let element
+  if (store.shell.activeLightbox?.name === 'profile-image') {
+    element = (
+      <ProfileImageLightbox.Component
+        {...(store.shell.activeLightbox as models.ProfileImageLightbox)}
+      />
+    )
+  } else {
+    return <View />
+  }
+
+  return (
+    <>
+      <TouchableOpacity style={styles.bg} onPress={onClose} />
+      <TouchableOpacity style={styles.xIcon} onPress={onClose}>
+        <FontAwesomeIcon icon="x" size={24} style={{color: '#fff'}} />
+      </TouchableOpacity>
+      {element}
+    </>
+  )
+})
+
+const styles = StyleSheet.create({
+  bg: {
+    position: 'absolute',
+    top: 0,
+    left: 0,
+    bottom: 0,
+    right: 0,
+    backgroundColor: '#000',
+    opacity: 0.9,
+  },
+  xIcon: {
+    position: 'absolute',
+    top: 30,
+    right: 30,
+  },
+  container: {
+    position: 'absolute',
+  },
+})
diff --git a/src/view/com/lightbox/ProfileImage.tsx b/src/view/com/lightbox/ProfileImage.tsx
new file mode 100644
index 000000000..f9c4ae73e
--- /dev/null
+++ b/src/view/com/lightbox/ProfileImage.tsx
@@ -0,0 +1,26 @@
+import React from 'react'
+import {StyleSheet, useWindowDimensions, View} from 'react-native'
+import {UserAvatar} from '../util/UserAvatar'
+import {ProfileViewModel} from '../../../state/models/profile-view'
+
+export function Component({profileView}: {profileView: ProfileViewModel}) {
+  const winDim = useWindowDimensions()
+  const top = winDim.height / 2 - (winDim.width - 40) / 2 - 100
+  return (
+    <View style={[styles.container, {top}]}>
+      <UserAvatar
+        handle={profileView.handle}
+        displayName={profileView.displayName}
+        avatar={profileView.avatar}
+        size={winDim.width - 40}
+      />
+    </View>
+  )
+}
+
+const styles = StyleSheet.create({
+  container: {
+    position: 'absolute',
+    left: 20,
+  },
+})
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index 47b627435..610d30eba 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -43,14 +43,14 @@ export const Modal = observer(function Modal() {
     snapPoints = ConfirmModal.snapPoints
     element = (
       <ConfirmModal.Component
-        {...(store.shell.activeModal as models.ConfirmModel)}
+        {...(store.shell.activeModal as models.ConfirmModal)}
       />
     )
   } else if (store.shell.activeModal?.name === 'edit-profile') {
     snapPoints = EditProfileModal.snapPoints
     element = (
       <EditProfileModal.Component
-        {...(store.shell.activeModal as models.EditProfileModel)}
+        {...(store.shell.activeModal as models.EditProfileModal)}
       />
     )
   } else if (store.shell.activeModal?.name === 'create-scene') {
@@ -60,14 +60,14 @@ export const Modal = observer(function Modal() {
     snapPoints = InviteToSceneModal.snapPoints
     element = (
       <InviteToSceneModal.Component
-        {...(store.shell.activeModal as models.InviteToSceneModel)}
+        {...(store.shell.activeModal as models.InviteToSceneModal)}
       />
     )
   } else if (store.shell.activeModal?.name === 'server-input') {
     snapPoints = ServerInputModal.snapPoints
     element = (
       <ServerInputModal.Component
-        {...(store.shell.activeModal as models.ServerInputModel)}
+        {...(store.shell.activeModal as models.ServerInputModal)}
       />
     )
   } else {
diff --git a/src/view/com/notifications/InviteAccepter.tsx b/src/view/com/notifications/InviteAccepter.tsx
index 852744006..767c57e3f 100644
--- a/src/view/com/notifications/InviteAccepter.tsx
+++ b/src/view/com/notifications/InviteAccepter.tsx
@@ -4,7 +4,7 @@ import LinearGradient from 'react-native-linear-gradient'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import * as apilib from '../../../state/lib/api'
 import {NotificationsViewItemModel} from '../../../state/models/notifications-view'
-import {ConfirmModel} from '../../../state/models/shell-ui'
+import {ConfirmModal} from '../../../state/models/shell-ui'
 import {useStores} from '../../../state'
 import {ProfileCard} from '../profile/ProfileCard'
 import * as Toast from '../util/Toast'
@@ -17,7 +17,7 @@ export function InviteAccepter({item}: {item: NotificationsViewItemModel}) {
     confirmationUri !== '' || store.me.memberships?.isMemberOf(item.author.did)
   const onPressAccept = async () => {
     store.shell.openModal(
-      new ConfirmModel(
+      new ConfirmModal(
         'Join this scene?',
         () => (
           <View>
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index eb0a7477c..a998a4614 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -7,9 +7,10 @@ import {AtUri} from '../../../third-party/uri'
 import {ProfileViewModel} from '../../../state/models/profile-view'
 import {useStores} from '../../../state'
 import {
-  ConfirmModel,
-  EditProfileModel,
-  InviteToSceneModel,
+  ConfirmModal,
+  EditProfileModal,
+  InviteToSceneModal,
+  ProfileImageLightbox,
 } from '../../../state/models/shell-ui'
 import {pluralize} from '../../../lib/strings'
 import {s, colors} from '../../lib/styles'
@@ -35,11 +36,8 @@ export const ProfileHeader = observer(function ProfileHeader({
     [view.myState.member],
   )
 
-  const onPressBack = () => {
-    store.nav.tab.goBack()
-  }
-  const onPressSearch = () => {
-    store.nav.navigate(`/search`)
+  const onPressAvi = () => {
+    store.shell.openLightbox(new ProfileImageLightbox(view))
   }
   const onPressToggleFollow = () => {
     view?.toggleFollowing().then(
@@ -54,7 +52,7 @@ export const ProfileHeader = observer(function ProfileHeader({
     )
   }
   const onPressEditProfile = () => {
-    store.shell.openModal(new EditProfileModel(view, onRefreshAll))
+    store.shell.openModal(new EditProfileModal(view, onRefreshAll))
   }
   const onPressFollowers = () => {
     store.nav.navigate(`/profile/${view.handle}/followers`)
@@ -66,11 +64,11 @@ export const ProfileHeader = observer(function ProfileHeader({
     store.nav.navigate(`/profile/${view.handle}/members`)
   }
   const onPressInviteMembers = () => {
-    store.shell.openModal(new InviteToSceneModel(view))
+    store.shell.openModal(new InviteToSceneModal(view))
   }
   const onPressLeaveScene = () => {
     store.shell.openModal(
-      new ConfirmModel(
+      new ConfirmModal(
         'Leave this scene?',
         `You'll be able to come back unless your invite is revoked.`,
         onPressConfirmLeaveScene,
@@ -153,14 +151,6 @@ export const ProfileHeader = observer(function ProfileHeader({
   return (
     <View style={styles.outer}>
       <UserBanner handle={view.handle} banner={view.banner} />
-      <View style={styles.avi}>
-        <UserAvatar
-          size={80}
-          handle={view.handle}
-          displayName={view.displayName}
-          avatar={view.avatar}
-        />
-      </View>
       <View style={styles.content}>
         <View style={[styles.buttonsLine]}>
           {isMe ? (
@@ -304,6 +294,14 @@ export const ProfileHeader = observer(function ProfileHeader({
           </TouchableOpacity>
         </View>
       ) : undefined}
+      <TouchableOpacity style={styles.avi} onPress={onPressAvi}>
+        <UserAvatar
+          size={80}
+          handle={view.handle}
+          displayName={view.displayName}
+          avatar={view.avatar}
+        />
+      </TouchableOpacity>
     </View>
   )
 })
diff --git a/src/view/com/util/DropdownBtn.tsx b/src/view/com/util/DropdownBtn.tsx
index b38a6ed99..d2b82c919 100644
--- a/src/view/com/util/DropdownBtn.tsx
+++ b/src/view/com/util/DropdownBtn.tsx
@@ -15,7 +15,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {colors} from '../../lib/styles'
 import {toShareUrl} from '../../../lib/strings'
 import {useStores} from '../../../state'
-import {ConfirmModel} from '../../../state/models/shell-ui'
+import {ConfirmModal} from '../../../state/models/shell-ui'
 import {TABS_ENABLED} from '../../../build-flags'
 
 const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
@@ -122,7 +122,7 @@ export function PostDropdownBtn({
           label: 'Delete post',
           onPress() {
             store.shell.openModal(
-              new ConfirmModel(
+              new ConfirmModal(
                 'Delete this post?',
                 'Are you sure? This can not be undone.',
                 onDeletePost,
diff --git a/src/view/screens/Login.tsx b/src/view/screens/Login.tsx
index 38c2c1c79..20b30fe01 100644
--- a/src/view/screens/Login.tsx
+++ b/src/view/screens/Login.tsx
@@ -24,7 +24,7 @@ import {
 } from '../../lib/strings'
 import {useStores, DEFAULT_SERVICE} from '../../state'
 import {ServiceDescription} from '../../state/models/session'
-import {ServerInputModel} from '../../state/models/shell-ui'
+import {ServerInputModal} from '../../state/models/shell-ui'
 import {ComAtprotoAccountCreate} from '../../third-party/api/index'
 import {isNetworkError} from '../../lib/errors'
 
@@ -149,7 +149,7 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
   }, [serviceUrl])
 
   const onPressSelectService = () => {
-    store.shell.openModal(new ServerInputModel(serviceUrl, setServiceUrl))
+    store.shell.openModal(new ServerInputModal(serviceUrl, setServiceUrl))
   }
 
   const onPressNext = async () => {
@@ -309,7 +309,7 @@ const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
   }, [serviceUrl])
 
   const onPressSelectService = () => {
-    store.shell.openModal(new ServerInputModel(serviceUrl, setServiceUrl))
+    store.shell.openModal(new ServerInputModal(serviceUrl, setServiceUrl))
   }
 
   const onPressNext = async () => {
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 14a9af4c7..93a7147b5 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -7,7 +7,7 @@ import {ScreenParams} from '../routes'
 import {ProfileUiModel, Sections} from '../../state/models/profile-ui'
 import {MembershipItem} from '../../state/models/memberships-view'
 import {useStores} from '../../state'
-import {ConfirmModel} from '../../state/models/shell-ui'
+import {ConfirmModal} from '../../state/models/shell-ui'
 import {ProfileHeader} from '../com/profile/ProfileHeader'
 import {FeedItem} from '../com/posts/FeedItem'
 import {ProfileCard} from '../com/profile/ProfileCard'
@@ -73,7 +73,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
   }
   const onPressRemoveMember = (membership: MembershipItem) => {
     store.shell.openModal(
-      new ConfirmModel(
+      new ConfirmModal(
         `Remove ${membership.displayName || membership.handle}?`,
         `You'll be able to invite them again if you change your mind.`,
         async () => {
diff --git a/src/view/shell/mobile/Menu.tsx b/src/view/shell/mobile/Menu.tsx
index 793b05276..d57447d41 100644
--- a/src/view/shell/mobile/Menu.tsx
+++ b/src/view/shell/mobile/Menu.tsx
@@ -18,7 +18,7 @@ import {
   MagnifyingGlassIcon,
 } from '../../lib/icons'
 import {UserAvatar} from '../../com/util/UserAvatar'
-import {CreateSceneModel} from '../../../state/models/shell-ui'
+import {CreateSceneModal} from '../../../state/models/shell-ui'
 
 export const Menu = ({
   visible,
@@ -53,7 +53,7 @@ export const Menu = ({
   }
   const onPressCreateScene = () => {
     onClose()
-    store.shell.openModal(new CreateSceneModel())
+    store.shell.openModal(new CreateSceneModal())
   }
 
   // rendering
diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx
index ef980066c..4567ab67a 100644
--- a/src/view/shell/mobile/index.tsx
+++ b/src/view/shell/mobile/index.tsx
@@ -28,6 +28,7 @@ import {Menu} from './Menu'
 import {Onboard} from '../../screens/Onboard'
 import {HorzSwipe} from '../../com/util/gestures/HorzSwipe'
 import {Modal} from '../../com/modals/Modal'
+import {Lightbox} from '../../com/lightbox/Lightbox'
 import {TabsSelector} from './TabsSelector'
 import {Composer} from './Composer'
 import {s, colors} from '../../lib/styles'
@@ -420,6 +421,7 @@ export const MobileShell: React.FC = observer(() => {
         />
       </View>
       <Modal />
+      <Lightbox />
       <Composer
         active={store.shell.isComposerActive}
         onClose={() => store.shell.closeComposer()}