Factor lightbox out into hook/context (#1919)
parent
03b20c70e4
commit
e749f2f3a5
|
@ -25,6 +25,7 @@ import {queryClient} from 'lib/react-query'
|
||||||
import {TestCtrls} from 'view/com/testing/TestCtrls'
|
import {TestCtrls} from 'view/com/testing/TestCtrls'
|
||||||
import {Provider as ShellStateProvider} from 'state/shell'
|
import {Provider as ShellStateProvider} from 'state/shell'
|
||||||
import {Provider as ModalStateProvider} from 'state/modals'
|
import {Provider as ModalStateProvider} from 'state/modals'
|
||||||
|
import {Provider as LightboxStateProvider} from 'state/lightbox'
|
||||||
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
||||||
import {Provider as InvitesStateProvider} from 'state/invites'
|
import {Provider as InvitesStateProvider} from 'state/invites'
|
||||||
import {Provider as PrefsStateProvider} from 'state/preferences'
|
import {Provider as PrefsStateProvider} from 'state/preferences'
|
||||||
|
@ -124,7 +125,9 @@ function App() {
|
||||||
<MutedThreadsProvider>
|
<MutedThreadsProvider>
|
||||||
<InvitesStateProvider>
|
<InvitesStateProvider>
|
||||||
<ModalStateProvider>
|
<ModalStateProvider>
|
||||||
<InnerApp />
|
<LightboxStateProvider>
|
||||||
|
<InnerApp />
|
||||||
|
</LightboxStateProvider>
|
||||||
</ModalStateProvider>
|
</ModalStateProvider>
|
||||||
</InvitesStateProvider>
|
</InvitesStateProvider>
|
||||||
</MutedThreadsProvider>
|
</MutedThreadsProvider>
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {I18nProvider} from '@lingui/react'
|
||||||
import {defaultLocale, dynamicActivate} from './locale/i18n'
|
import {defaultLocale, dynamicActivate} from './locale/i18n'
|
||||||
import {Provider as ShellStateProvider} from 'state/shell'
|
import {Provider as ShellStateProvider} from 'state/shell'
|
||||||
import {Provider as ModalStateProvider} from 'state/modals'
|
import {Provider as ModalStateProvider} from 'state/modals'
|
||||||
|
import {Provider as LightboxStateProvider} from 'state/lightbox'
|
||||||
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
||||||
import {Provider as InvitesStateProvider} from 'state/invites'
|
import {Provider as InvitesStateProvider} from 'state/invites'
|
||||||
import {Provider as PrefsStateProvider} from 'state/preferences'
|
import {Provider as PrefsStateProvider} from 'state/preferences'
|
||||||
|
@ -111,7 +112,9 @@ function App() {
|
||||||
<MutedThreadsProvider>
|
<MutedThreadsProvider>
|
||||||
<InvitesStateProvider>
|
<InvitesStateProvider>
|
||||||
<ModalStateProvider>
|
<ModalStateProvider>
|
||||||
<InnerApp />
|
<LightboxStateProvider>
|
||||||
|
<InnerApp />
|
||||||
|
</LightboxStateProvider>
|
||||||
</ModalStateProvider>
|
</ModalStateProvider>
|
||||||
</InvitesStateProvider>
|
</InvitesStateProvider>
|
||||||
</MutedThreadsProvider>
|
</MutedThreadsProvider>
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
|
|
||||||
|
interface Lightbox {
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProfileImageLightbox implements Lightbox {
|
||||||
|
name = 'profile-image'
|
||||||
|
constructor(public profile: AppBskyActorDefs.ProfileViewDetailed) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImagesLightboxItem {
|
||||||
|
uri: string
|
||||||
|
alt?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ImagesLightbox implements Lightbox {
|
||||||
|
name = 'images'
|
||||||
|
constructor(public images: ImagesLightboxItem[], public index: number) {}
|
||||||
|
setIndex(index: number) {
|
||||||
|
this.index = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LightboxContext = React.createContext<{
|
||||||
|
activeLightbox: Lightbox | null
|
||||||
|
}>({
|
||||||
|
activeLightbox: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const LightboxControlContext = React.createContext<{
|
||||||
|
openLightbox: (lightbox: Lightbox) => void
|
||||||
|
closeLightbox: () => void
|
||||||
|
}>({
|
||||||
|
openLightbox: () => {},
|
||||||
|
closeLightbox: () => {},
|
||||||
|
})
|
||||||
|
|
||||||
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
const [activeLightbox, setActiveLightbox] = React.useState<Lightbox | null>(
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
const openLightbox = React.useCallback(
|
||||||
|
(lightbox: Lightbox) => {
|
||||||
|
setActiveLightbox(lightbox)
|
||||||
|
},
|
||||||
|
[setActiveLightbox],
|
||||||
|
)
|
||||||
|
|
||||||
|
const closeLightbox = React.useCallback(() => {
|
||||||
|
setActiveLightbox(null)
|
||||||
|
}, [setActiveLightbox])
|
||||||
|
|
||||||
|
const state = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
activeLightbox,
|
||||||
|
}),
|
||||||
|
[activeLightbox],
|
||||||
|
)
|
||||||
|
|
||||||
|
const methods = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
openLightbox,
|
||||||
|
closeLightbox,
|
||||||
|
}),
|
||||||
|
[openLightbox, closeLightbox],
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LightboxContext.Provider value={state}>
|
||||||
|
<LightboxControlContext.Provider value={methods}>
|
||||||
|
{children}
|
||||||
|
</LightboxControlContext.Provider>
|
||||||
|
</LightboxContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLightbox() {
|
||||||
|
return React.useContext(LightboxContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLightboxControls() {
|
||||||
|
return React.useContext(LightboxControlContext)
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {AppBskyActorDefs, AppBskyGraphDefs, ModerationUI} from '@atproto/api'
|
import {AppBskyActorDefs, AppBskyGraphDefs, ModerationUI} from '@atproto/api'
|
||||||
import {StyleProp, ViewStyle, DeviceEventEmitter} from 'react-native'
|
import {StyleProp, ViewStyle} from 'react-native'
|
||||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||||
|
|
||||||
import {ImageModel} from '#/state/models/media/image'
|
import {ImageModel} from '#/state/models/media/image'
|
||||||
|
@ -232,7 +232,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
|
||||||
const openModal = React.useCallback(
|
const openModal = React.useCallback(
|
||||||
(modal: Modal) => {
|
(modal: Modal) => {
|
||||||
DeviceEventEmitter.emit('navigation')
|
|
||||||
setActiveModals(activeModals => [...activeModals, modal])
|
setActiveModals(activeModals => [...activeModals, modal])
|
||||||
setIsModalActive(true)
|
setIsModalActive(true)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
|
||||||
import {RootStoreModel} from '../root-store'
|
import {RootStoreModel} from '../root-store'
|
||||||
import {makeAutoObservable} from 'mobx'
|
import {makeAutoObservable} from 'mobx'
|
||||||
import {
|
import {
|
||||||
|
@ -13,34 +12,7 @@ export function isColorMode(v: unknown): v is ColorMode {
|
||||||
return v === 'system' || v === 'light' || v === 'dark'
|
return v === 'system' || v === 'light' || v === 'dark'
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LightboxModel {}
|
|
||||||
|
|
||||||
export class ProfileImageLightbox implements LightboxModel {
|
|
||||||
name = 'profile-image'
|
|
||||||
constructor(public profile: AppBskyActorDefs.ProfileViewDetailed) {
|
|
||||||
makeAutoObservable(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImagesLightboxItem {
|
|
||||||
uri: string
|
|
||||||
alt?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ImagesLightbox implements LightboxModel {
|
|
||||||
name = 'images'
|
|
||||||
constructor(public images: ImagesLightboxItem[], public index: number) {
|
|
||||||
makeAutoObservable(this)
|
|
||||||
}
|
|
||||||
setIndex(index: number) {
|
|
||||||
this.index = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ShellUiModel {
|
export class ShellUiModel {
|
||||||
isLightboxActive = false
|
|
||||||
activeLightbox: ProfileImageLightbox | ImagesLightbox | null = null
|
|
||||||
|
|
||||||
constructor(public rootStore: RootStoreModel) {
|
constructor(public rootStore: RootStoreModel) {
|
||||||
makeAutoObservable(this, {
|
makeAutoObservable(this, {
|
||||||
rootStore: false,
|
rootStore: false,
|
||||||
|
@ -54,32 +26,13 @@ export class ShellUiModel {
|
||||||
* (used by the android hardware back btn)
|
* (used by the android hardware back btn)
|
||||||
*/
|
*/
|
||||||
closeAnyActiveElement(): boolean {
|
closeAnyActiveElement(): boolean {
|
||||||
if (this.isLightboxActive) {
|
|
||||||
this.closeLightbox()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* used to clear out any modals, eg for a navigation
|
* used to clear out any modals, eg for a navigation
|
||||||
*/
|
*/
|
||||||
closeAllActiveElements() {
|
closeAllActiveElements() {}
|
||||||
if (this.isLightboxActive) {
|
|
||||||
this.closeLightbox()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openLightbox(lightbox: ProfileImageLightbox | ImagesLightbox) {
|
|
||||||
this.rootStore.emitNavigation()
|
|
||||||
this.isLightboxActive = true
|
|
||||||
this.activeLightbox = lightbox
|
|
||||||
}
|
|
||||||
|
|
||||||
closeLightbox() {
|
|
||||||
this.isLightboxActive = false
|
|
||||||
this.activeLightbox = null
|
|
||||||
}
|
|
||||||
|
|
||||||
setupLoginModals() {
|
setupLoginModals() {
|
||||||
this.rootStore.onSessionReady(() => {
|
this.rootStore.onSessionReady(() => {
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Pressable, StyleSheet, View} from 'react-native'
|
import {Pressable, StyleSheet, View} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import ImageView from './ImageViewing'
|
import ImageView from './ImageViewing'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import * as models from 'state/models/ui/shell'
|
|
||||||
import {shareImageModal, saveImageToMediaLibrary} from 'lib/media/manip'
|
import {shareImageModal, saveImageToMediaLibrary} from 'lib/media/manip'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
|
@ -12,17 +9,24 @@ import {s, colors} from 'lib/styles'
|
||||||
import {Button} from '../util/forms/Button'
|
import {Button} from '../util/forms/Button'
|
||||||
import {isIOS} from 'platform/detection'
|
import {isIOS} from 'platform/detection'
|
||||||
import * as MediaLibrary from 'expo-media-library'
|
import * as MediaLibrary from 'expo-media-library'
|
||||||
|
import {
|
||||||
|
useLightbox,
|
||||||
|
useLightboxControls,
|
||||||
|
ProfileImageLightbox,
|
||||||
|
ImagesLightbox,
|
||||||
|
} from '#/state/lightbox'
|
||||||
|
|
||||||
export const Lightbox = observer(function Lightbox() {
|
export function Lightbox() {
|
||||||
const store = useStores()
|
const {activeLightbox} = useLightbox()
|
||||||
|
const {closeLightbox} = useLightboxControls()
|
||||||
const onClose = React.useCallback(() => {
|
const onClose = React.useCallback(() => {
|
||||||
store.shell.closeLightbox()
|
closeLightbox()
|
||||||
}, [store])
|
}, [closeLightbox])
|
||||||
|
|
||||||
if (!store.shell.activeLightbox) {
|
if (!activeLightbox) {
|
||||||
return null
|
return null
|
||||||
} else if (store.shell.activeLightbox.name === 'profile-image') {
|
} else if (activeLightbox.name === 'profile-image') {
|
||||||
const opts = store.shell.activeLightbox as models.ProfileImageLightbox
|
const opts = activeLightbox as ProfileImageLightbox
|
||||||
return (
|
return (
|
||||||
<ImageView
|
<ImageView
|
||||||
images={[{uri: opts.profile.avatar || ''}]}
|
images={[{uri: opts.profile.avatar || ''}]}
|
||||||
|
@ -32,8 +36,8 @@ export const Lightbox = observer(function Lightbox() {
|
||||||
FooterComponent={LightboxFooter}
|
FooterComponent={LightboxFooter}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (store.shell.activeLightbox.name === 'images') {
|
} else if (activeLightbox.name === 'images') {
|
||||||
const opts = store.shell.activeLightbox as models.ImagesLightbox
|
const opts = activeLightbox as ImagesLightbox
|
||||||
return (
|
return (
|
||||||
<ImageView
|
<ImageView
|
||||||
images={opts.images.map(img => ({...img}))}
|
images={opts.images.map(img => ({...img}))}
|
||||||
|
@ -46,14 +50,10 @@ export const Lightbox = observer(function Lightbox() {
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
const LightboxFooter = observer(function LightboxFooter({
|
function LightboxFooter({imageIndex}: {imageIndex: number}) {
|
||||||
imageIndex,
|
const {activeLightbox} = useLightbox()
|
||||||
}: {
|
|
||||||
imageIndex: number
|
|
||||||
}) {
|
|
||||||
const store = useStores()
|
|
||||||
const [isAltExpanded, setAltExpanded] = React.useState(false)
|
const [isAltExpanded, setAltExpanded] = React.useState(false)
|
||||||
const [permissionResponse, requestPermission] = MediaLibrary.usePermissions()
|
const [permissionResponse, requestPermission] = MediaLibrary.usePermissions()
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ const LightboxFooter = observer(function LightboxFooter({
|
||||||
[permissionResponse, requestPermission],
|
[permissionResponse, requestPermission],
|
||||||
)
|
)
|
||||||
|
|
||||||
const lightbox = store.shell.activeLightbox
|
const lightbox = activeLightbox
|
||||||
if (!lightbox) {
|
if (!lightbox) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -89,11 +89,11 @@ const LightboxFooter = observer(function LightboxFooter({
|
||||||
let altText = ''
|
let altText = ''
|
||||||
let uri = ''
|
let uri = ''
|
||||||
if (lightbox.name === 'images') {
|
if (lightbox.name === 'images') {
|
||||||
const opts = lightbox as models.ImagesLightbox
|
const opts = lightbox as ImagesLightbox
|
||||||
uri = opts.images[imageIndex].uri
|
uri = opts.images[imageIndex].uri
|
||||||
altText = opts.images[imageIndex].alt || ''
|
altText = opts.images[imageIndex].alt || ''
|
||||||
} else if (lightbox.name === 'profile-image') {
|
} else if (lightbox.name === 'profile-image') {
|
||||||
const opts = lightbox as models.ProfileImageLightbox
|
const opts = lightbox as ProfileImageLightbox
|
||||||
uri = opts.profile.avatar || ''
|
uri = opts.profile.avatar || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ const LightboxFooter = observer(function LightboxFooter({
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
footer: {
|
footer: {
|
||||||
|
|
|
@ -7,41 +7,42 @@ import {
|
||||||
View,
|
View,
|
||||||
Pressable,
|
Pressable,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import * as models from 'state/models/ui/shell'
|
|
||||||
import {colors, s} from 'lib/styles'
|
import {colors, s} from 'lib/styles'
|
||||||
import ImageDefaultHeader from './ImageViewing/components/ImageDefaultHeader'
|
import ImageDefaultHeader from './ImageViewing/components/ImageDefaultHeader'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
|
import {
|
||||||
|
useLightbox,
|
||||||
|
useLightboxControls,
|
||||||
|
ImagesLightbox,
|
||||||
|
ProfileImageLightbox,
|
||||||
|
} from '#/state/lightbox'
|
||||||
|
|
||||||
interface Img {
|
interface Img {
|
||||||
uri: string
|
uri: string
|
||||||
alt?: string
|
alt?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Lightbox = observer(function Lightbox() {
|
export function Lightbox() {
|
||||||
const store = useStores()
|
const {activeLightbox} = useLightbox()
|
||||||
|
const {closeLightbox} = useLightboxControls()
|
||||||
|
|
||||||
const onClose = useCallback(() => store.shell.closeLightbox(), [store.shell])
|
if (!activeLightbox) {
|
||||||
|
|
||||||
if (!store.shell.isLightboxActive) {
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeLightbox = store.shell.activeLightbox
|
|
||||||
const initialIndex =
|
const initialIndex =
|
||||||
activeLightbox instanceof models.ImagesLightbox ? activeLightbox.index : 0
|
activeLightbox instanceof ImagesLightbox ? activeLightbox.index : 0
|
||||||
|
|
||||||
let imgs: Img[] | undefined
|
let imgs: Img[] | undefined
|
||||||
if (activeLightbox instanceof models.ProfileImageLightbox) {
|
if (activeLightbox instanceof ProfileImageLightbox) {
|
||||||
const opts = activeLightbox
|
const opts = activeLightbox
|
||||||
if (opts.profile.avatar) {
|
if (opts.profile.avatar) {
|
||||||
imgs = [{uri: opts.profile.avatar}]
|
imgs = [{uri: opts.profile.avatar}]
|
||||||
}
|
}
|
||||||
} else if (activeLightbox instanceof models.ImagesLightbox) {
|
} else if (activeLightbox instanceof ImagesLightbox) {
|
||||||
const opts = activeLightbox
|
const opts = activeLightbox
|
||||||
imgs = opts.images
|
imgs = opts.images
|
||||||
}
|
}
|
||||||
|
@ -51,9 +52,13 @@ export const Lightbox = observer(function Lightbox() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LightboxInner imgs={imgs} initialIndex={initialIndex} onClose={onClose} />
|
<LightboxInner
|
||||||
|
imgs={imgs}
|
||||||
|
initialIndex={initialIndex}
|
||||||
|
onClose={closeLightbox}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
function LightboxInner({
|
function LightboxInner({
|
||||||
imgs,
|
imgs,
|
||||||
|
|
|
@ -17,7 +17,6 @@ import {useLingui} from '@lingui/react'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {isNative} from 'platform/detection'
|
import {isNative} from 'platform/detection'
|
||||||
import {BlurView} from '../util/BlurView'
|
import {BlurView} from '../util/BlurView'
|
||||||
import {ProfileImageLightbox} from 'state/models/ui/shell'
|
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
|
@ -30,8 +29,8 @@ import {formatCount} from '../util/numeric/format'
|
||||||
import {NativeDropdown, DropdownItem} from '../util/forms/NativeDropdown'
|
import {NativeDropdown, DropdownItem} from '../util/forms/NativeDropdown'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {ProfileHeaderSuggestedFollows} from './ProfileHeaderSuggestedFollows'
|
import {ProfileHeaderSuggestedFollows} from './ProfileHeaderSuggestedFollows'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {useModalControls} from '#/state/modals'
|
import {useModalControls} from '#/state/modals'
|
||||||
|
import {useLightboxControls, ProfileImageLightbox} from '#/state/lightbox'
|
||||||
import {
|
import {
|
||||||
useProfileFollowMutation,
|
useProfileFollowMutation,
|
||||||
useProfileUnfollowMutation,
|
useProfileUnfollowMutation,
|
||||||
|
@ -115,10 +114,10 @@ function ProfileHeaderLoaded({
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const palInverted = usePalette('inverted')
|
const palInverted = usePalette('inverted')
|
||||||
const store = useStores()
|
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {openModal} = useModalControls()
|
const {openModal} = useModalControls()
|
||||||
|
const {openLightbox} = useLightboxControls()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
const invalidHandle = isInvalidHandle(profile.handle)
|
const invalidHandle = isInvalidHandle(profile.handle)
|
||||||
|
@ -151,9 +150,9 @@ function ProfileHeaderLoaded({
|
||||||
profile.avatar &&
|
profile.avatar &&
|
||||||
!(moderation.avatar.blur && moderation.avatar.noOverride)
|
!(moderation.avatar.blur && moderation.avatar.noOverride)
|
||||||
) {
|
) {
|
||||||
store.shell.openLightbox(new ProfileImageLightbox(profile))
|
openLightbox(new ProfileImageLightbox(profile))
|
||||||
}
|
}
|
||||||
}, [store, profile, moderation])
|
}, [openLightbox, profile, moderation])
|
||||||
|
|
||||||
const onPressFollow = React.useCallback(async () => {
|
const onPressFollow = React.useCallback(async () => {
|
||||||
if (profile.viewer?.following) {
|
if (profile.viewer?.following) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {useStores} from 'state/index'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {BACK_HITSLOP} from 'lib/constants'
|
import {BACK_HITSLOP} from 'lib/constants'
|
||||||
import {isNative} from 'platform/detection'
|
import {isNative} from 'platform/detection'
|
||||||
import {ImagesLightbox} from 'state/models/ui/shell'
|
import {useLightboxControls, ImagesLightbox} from '#/state/lightbox'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useSetDrawerOpen} from '#/state/shell'
|
import {useSetDrawerOpen} from '#/state/shell'
|
||||||
|
@ -50,6 +50,7 @@ export const ProfileSubpageHeader = observer(function HeaderImpl({
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
|
const {openLightbox} = useLightboxControls()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const canGoBack = navigation.canGoBack()
|
const canGoBack = navigation.canGoBack()
|
||||||
|
|
||||||
|
@ -69,9 +70,9 @@ export const ProfileSubpageHeader = observer(function HeaderImpl({
|
||||||
if (
|
if (
|
||||||
avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
|
avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
|
||||||
) {
|
) {
|
||||||
store.shell.openLightbox(new ImagesLightbox([{uri: avatar}], 0))
|
openLightbox(new ImagesLightbox([{uri: avatar}], 0))
|
||||||
}
|
}
|
||||||
}, [store, avatar])
|
}, [openLightbox, avatar])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CenteredView style={pal.view}>
|
<CenteredView style={pal.view}>
|
||||||
|
|
|
@ -19,8 +19,7 @@ import {
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {Link} from '../Link'
|
import {Link} from '../Link'
|
||||||
import {ImageLayoutGrid} from '../images/ImageLayoutGrid'
|
import {ImageLayoutGrid} from '../images/ImageLayoutGrid'
|
||||||
import {ImagesLightbox} from 'state/models/ui/shell'
|
import {useLightboxControls, ImagesLightbox} from '#/state/lightbox'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {YoutubeEmbed} from './YoutubeEmbed'
|
import {YoutubeEmbed} from './YoutubeEmbed'
|
||||||
|
@ -49,7 +48,7 @@ export function PostEmbeds({
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const {openLightbox} = useLightboxControls()
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
|
|
||||||
// quote post with media
|
// quote post with media
|
||||||
|
@ -104,8 +103,8 @@ export function PostEmbeds({
|
||||||
alt: img.alt,
|
alt: img.alt,
|
||||||
aspectRatio: img.aspectRatio,
|
aspectRatio: img.aspectRatio,
|
||||||
}))
|
}))
|
||||||
const openLightbox = (index: number) => {
|
const _openLightbox = (index: number) => {
|
||||||
store.shell.openLightbox(new ImagesLightbox(items, index))
|
openLightbox(new ImagesLightbox(items, index))
|
||||||
}
|
}
|
||||||
const onPressIn = (_: number) => {
|
const onPressIn = (_: number) => {
|
||||||
InteractionManager.runAfterInteractions(() => {
|
InteractionManager.runAfterInteractions(() => {
|
||||||
|
@ -121,7 +120,7 @@ export function PostEmbeds({
|
||||||
alt={alt}
|
alt={alt}
|
||||||
uri={thumb}
|
uri={thumb}
|
||||||
dimensionsHint={aspectRatio}
|
dimensionsHint={aspectRatio}
|
||||||
onPress={() => openLightbox(0)}
|
onPress={() => _openLightbox(0)}
|
||||||
onPressIn={() => onPressIn(0)}
|
onPressIn={() => onPressIn(0)}
|
||||||
style={[
|
style={[
|
||||||
styles.singleImage,
|
styles.singleImage,
|
||||||
|
@ -143,7 +142,7 @@ export function PostEmbeds({
|
||||||
<View style={[styles.imagesContainer, style]}>
|
<View style={[styles.imagesContainer, style]}>
|
||||||
<ImageLayoutGrid
|
<ImageLayoutGrid
|
||||||
images={embed.images}
|
images={embed.images}
|
||||||
onPress={openLightbox}
|
onPress={_openLightbox}
|
||||||
onPressIn={onPressIn}
|
onPressIn={onPressIn}
|
||||||
style={
|
style={
|
||||||
embed.images.length === 1
|
embed.images.length === 1
|
||||||
|
|
Loading…
Reference in New Issue