Add modal state provider, replace usage except methods (#1833)
* Add modal state provider, replace usage except methods * Replace easy spots * Fix sticky spots * Replace final usages * Memorize context objects * Add more warningszio/stable
parent
5eadadffbf
commit
f18b15241a
|
@ -22,6 +22,7 @@ import * as Toast from 'view/com/util/Toast'
|
|||
import {queryClient} from 'lib/react-query'
|
||||
import {TestCtrls} from 'view/com/testing/TestCtrls'
|
||||
import {Provider as ShellStateProvider} from 'state/shell'
|
||||
import {Provider as ModalStateProvider} from 'state/modals'
|
||||
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
||||
import {Provider as InvitesStateProvider} from 'state/invites'
|
||||
import {Provider as PrefsStateProvider} from 'state/preferences'
|
||||
|
@ -84,7 +85,9 @@ function App() {
|
|||
<PrefsStateProvider>
|
||||
<MutedThreadsProvider>
|
||||
<InvitesStateProvider>
|
||||
<InnerApp />
|
||||
<ModalStateProvider>
|
||||
<InnerApp />
|
||||
</ModalStateProvider>
|
||||
</InvitesStateProvider>
|
||||
</MutedThreadsProvider>
|
||||
</PrefsStateProvider>
|
||||
|
|
|
@ -17,6 +17,7 @@ import {ToastContainer} from 'view/com/util/Toast.web'
|
|||
import {ThemeProvider} from 'lib/ThemeContext'
|
||||
import {queryClient} from 'lib/react-query'
|
||||
import {Provider as ShellStateProvider} from 'state/shell'
|
||||
import {Provider as ModalStateProvider} from 'state/modals'
|
||||
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
||||
import {Provider as InvitesStateProvider} from 'state/invites'
|
||||
import {Provider as PrefsStateProvider} from 'state/preferences'
|
||||
|
@ -74,7 +75,9 @@ function App() {
|
|||
<PrefsStateProvider>
|
||||
<MutedThreadsProvider>
|
||||
<InvitesStateProvider>
|
||||
<InnerApp />
|
||||
<ModalStateProvider>
|
||||
<InnerApp />
|
||||
</ModalStateProvider>
|
||||
</InvitesStateProvider>
|
||||
</MutedThreadsProvider>
|
||||
</PrefsStateProvider>
|
||||
|
|
|
@ -7,6 +7,7 @@ import {AccountData} from 'state/models/session'
|
|||
import {reset as resetNavigation} from '../../Navigation'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function useAccountSwitcher(): [
|
||||
boolean,
|
||||
|
@ -16,6 +17,7 @@ export function useAccountSwitcher(): [
|
|||
const {track} = useAnalytics()
|
||||
const store = useStores()
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const {closeModal} = useModalControls()
|
||||
const [isSwitching, setIsSwitching] = useState(false)
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
|
||||
|
@ -25,6 +27,7 @@ export function useAccountSwitcher(): [
|
|||
setIsSwitching(true)
|
||||
const success = await store.session.resumeSession(acct)
|
||||
setDrawerOpen(false)
|
||||
closeModal()
|
||||
store.shell.closeAllActiveElements()
|
||||
if (success) {
|
||||
resetNavigation()
|
||||
|
@ -36,7 +39,7 @@ export function useAccountSwitcher(): [
|
|||
store.session.clear()
|
||||
}
|
||||
},
|
||||
[track, setIsSwitching, navigation, store, setDrawerOpen],
|
||||
[track, setIsSwitching, navigation, store, setDrawerOpen, closeModal],
|
||||
)
|
||||
|
||||
return [isSwitching, setIsSwitching, onPressSwitchAccount]
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import * as Updates from 'expo-updates'
|
||||
import {useCallback, useEffect} from 'react'
|
||||
import {AppState} from 'react-native'
|
||||
import {useStores} from 'state/index'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function useOTAUpdate() {
|
||||
const store = useStores()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
const showUpdatePopup = useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Update Available',
|
||||
message:
|
||||
|
@ -20,7 +20,7 @@ export function useOTAUpdate() {
|
|||
})
|
||||
},
|
||||
})
|
||||
}, [store.shell])
|
||||
}, [openModal])
|
||||
const checkForUpdate = useCallback(async () => {
|
||||
logger.debug('useOTAUpdate: Checking for update...')
|
||||
try {
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import {RootStoreModel} from 'state/index'
|
||||
import {ImageModel} from 'state/models/media/image'
|
||||
|
||||
export async function openAltTextModal(
|
||||
store: RootStoreModel,
|
||||
image: ImageModel,
|
||||
) {
|
||||
store.shell.openModal({
|
||||
name: 'alt-text-image',
|
||||
image,
|
||||
})
|
||||
}
|
|
@ -4,6 +4,7 @@ import {CameraOpts, CropperOptions} from './types'
|
|||
import {RootStoreModel} from 'state/index'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
export {openPicker} from './picker.shared'
|
||||
import {unstable__openModal} from '#/state/modals'
|
||||
|
||||
export async function openCamera(
|
||||
_store: RootStoreModel,
|
||||
|
@ -14,12 +15,12 @@ export async function openCamera(
|
|||
}
|
||||
|
||||
export async function openCropper(
|
||||
store: RootStoreModel,
|
||||
_store: RootStoreModel,
|
||||
opts: CropperOptions,
|
||||
): Promise<RNImage> {
|
||||
// TODO handle more opts
|
||||
return new Promise((resolve, reject) => {
|
||||
store.shell.openModal({
|
||||
unstable__openModal({
|
||||
name: 'crop-image',
|
||||
uri: opts.path,
|
||||
onSelect: (img?: RNImage) => {
|
||||
|
|
|
@ -0,0 +1,284 @@
|
|||
import React from 'react'
|
||||
import {AppBskyActorDefs, ModerationUI} from '@atproto/api'
|
||||
import {StyleProp, ViewStyle, DeviceEventEmitter} from 'react-native'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
|
||||
import {ProfileModel} from '#/state/models/content/profile'
|
||||
import {ImageModel} from '#/state/models/media/image'
|
||||
import {ListModel} from '#/state/models/content/list'
|
||||
import {GalleryModel} from '#/state/models/media/gallery'
|
||||
|
||||
export interface ConfirmModal {
|
||||
name: 'confirm'
|
||||
title: string
|
||||
message: string | (() => JSX.Element)
|
||||
onPressConfirm: () => void | Promise<void>
|
||||
onPressCancel?: () => void | Promise<void>
|
||||
confirmBtnText?: string
|
||||
confirmBtnStyle?: StyleProp<ViewStyle>
|
||||
cancelBtnText?: string
|
||||
}
|
||||
|
||||
export interface EditProfileModal {
|
||||
name: 'edit-profile'
|
||||
profileView: ProfileModel
|
||||
onUpdate?: () => void
|
||||
}
|
||||
|
||||
export interface ProfilePreviewModal {
|
||||
name: 'profile-preview'
|
||||
did: string
|
||||
}
|
||||
|
||||
export interface ServerInputModal {
|
||||
name: 'server-input'
|
||||
initialService: string
|
||||
onSelect: (url: string) => void
|
||||
}
|
||||
|
||||
export interface ModerationDetailsModal {
|
||||
name: 'moderation-details'
|
||||
context: 'account' | 'content'
|
||||
moderation: ModerationUI
|
||||
}
|
||||
|
||||
export type ReportModal = {
|
||||
name: 'report'
|
||||
} & (
|
||||
| {
|
||||
uri: string
|
||||
cid: string
|
||||
}
|
||||
| {did: string}
|
||||
)
|
||||
|
||||
export interface CreateOrEditListModal {
|
||||
name: 'create-or-edit-list'
|
||||
purpose?: string
|
||||
list?: ListModel
|
||||
onSave?: (uri: string) => void
|
||||
}
|
||||
|
||||
export interface UserAddRemoveListsModal {
|
||||
name: 'user-add-remove-lists'
|
||||
subject: string
|
||||
displayName: string
|
||||
onAdd?: (listUri: string) => void
|
||||
onRemove?: (listUri: string) => void
|
||||
}
|
||||
|
||||
export interface ListAddUserModal {
|
||||
name: 'list-add-user'
|
||||
list: ListModel
|
||||
onAdd?: (profile: AppBskyActorDefs.ProfileViewBasic) => void
|
||||
}
|
||||
|
||||
export interface EditImageModal {
|
||||
name: 'edit-image'
|
||||
image: ImageModel
|
||||
gallery: GalleryModel
|
||||
}
|
||||
|
||||
export interface CropImageModal {
|
||||
name: 'crop-image'
|
||||
uri: string
|
||||
onSelect: (img?: RNImage) => void
|
||||
}
|
||||
|
||||
export interface AltTextImageModal {
|
||||
name: 'alt-text-image'
|
||||
image: ImageModel
|
||||
}
|
||||
|
||||
export interface DeleteAccountModal {
|
||||
name: 'delete-account'
|
||||
}
|
||||
|
||||
export interface RepostModal {
|
||||
name: 'repost'
|
||||
onRepost: () => void
|
||||
onQuote: () => void
|
||||
isReposted: boolean
|
||||
}
|
||||
|
||||
export interface SelfLabelModal {
|
||||
name: 'self-label'
|
||||
labels: string[]
|
||||
hasMedia: boolean
|
||||
onChange: (labels: string[]) => void
|
||||
}
|
||||
|
||||
export interface ChangeHandleModal {
|
||||
name: 'change-handle'
|
||||
onChanged: () => void
|
||||
}
|
||||
|
||||
export interface WaitlistModal {
|
||||
name: 'waitlist'
|
||||
}
|
||||
|
||||
export interface InviteCodesModal {
|
||||
name: 'invite-codes'
|
||||
}
|
||||
|
||||
export interface AddAppPasswordModal {
|
||||
name: 'add-app-password'
|
||||
}
|
||||
|
||||
export interface ContentFilteringSettingsModal {
|
||||
name: 'content-filtering-settings'
|
||||
}
|
||||
|
||||
export interface ContentLanguagesSettingsModal {
|
||||
name: 'content-languages-settings'
|
||||
}
|
||||
|
||||
export interface PostLanguagesSettingsModal {
|
||||
name: 'post-languages-settings'
|
||||
}
|
||||
|
||||
export interface BirthDateSettingsModal {
|
||||
name: 'birth-date-settings'
|
||||
}
|
||||
|
||||
export interface VerifyEmailModal {
|
||||
name: 'verify-email'
|
||||
showReminder?: boolean
|
||||
}
|
||||
|
||||
export interface ChangeEmailModal {
|
||||
name: 'change-email'
|
||||
}
|
||||
|
||||
export interface SwitchAccountModal {
|
||||
name: 'switch-account'
|
||||
}
|
||||
|
||||
export interface LinkWarningModal {
|
||||
name: 'link-warning'
|
||||
text: string
|
||||
href: string
|
||||
}
|
||||
|
||||
export type Modal =
|
||||
// Account
|
||||
| AddAppPasswordModal
|
||||
| ChangeHandleModal
|
||||
| DeleteAccountModal
|
||||
| EditProfileModal
|
||||
| ProfilePreviewModal
|
||||
| BirthDateSettingsModal
|
||||
| VerifyEmailModal
|
||||
| ChangeEmailModal
|
||||
| SwitchAccountModal
|
||||
|
||||
// Curation
|
||||
| ContentFilteringSettingsModal
|
||||
| ContentLanguagesSettingsModal
|
||||
| PostLanguagesSettingsModal
|
||||
|
||||
// Moderation
|
||||
| ModerationDetailsModal
|
||||
| ReportModal
|
||||
|
||||
// Lists
|
||||
| CreateOrEditListModal
|
||||
| UserAddRemoveListsModal
|
||||
| ListAddUserModal
|
||||
|
||||
// Posts
|
||||
| AltTextImageModal
|
||||
| CropImageModal
|
||||
| EditImageModal
|
||||
| ServerInputModal
|
||||
| RepostModal
|
||||
| SelfLabelModal
|
||||
|
||||
// Bluesky access
|
||||
| WaitlistModal
|
||||
| InviteCodesModal
|
||||
|
||||
// Generic
|
||||
| ConfirmModal
|
||||
| LinkWarningModal
|
||||
|
||||
const ModalContext = React.createContext<{
|
||||
isModalActive: boolean
|
||||
activeModals: Modal[]
|
||||
}>({
|
||||
isModalActive: false,
|
||||
activeModals: [],
|
||||
})
|
||||
|
||||
const ModalControlContext = React.createContext<{
|
||||
openModal: (modal: Modal) => void
|
||||
closeModal: () => void
|
||||
}>({
|
||||
openModal: () => {},
|
||||
closeModal: () => {},
|
||||
})
|
||||
|
||||
/**
|
||||
* @deprecated DO NOT USE THIS unless you have no other choice.
|
||||
*/
|
||||
export let unstable__openModal: (modal: Modal) => void = () => {
|
||||
throw new Error(`ModalContext is not initialized`)
|
||||
}
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const [isModalActive, setIsModalActive] = React.useState(false)
|
||||
const [activeModals, setActiveModals] = React.useState<Modal[]>([])
|
||||
|
||||
const openModal = React.useCallback(
|
||||
(modal: Modal) => {
|
||||
DeviceEventEmitter.emit('navigation')
|
||||
setActiveModals(activeModals => [...activeModals, modal])
|
||||
setIsModalActive(true)
|
||||
},
|
||||
[setIsModalActive, setActiveModals],
|
||||
)
|
||||
|
||||
unstable__openModal = openModal
|
||||
|
||||
const closeModal = React.useCallback(() => {
|
||||
let totalActiveModals = 0
|
||||
setActiveModals(activeModals => {
|
||||
activeModals.pop()
|
||||
totalActiveModals = activeModals.length
|
||||
return activeModals
|
||||
})
|
||||
setIsModalActive(totalActiveModals > 0)
|
||||
}, [setIsModalActive, setActiveModals])
|
||||
|
||||
const state = React.useMemo(
|
||||
() => ({
|
||||
isModalActive,
|
||||
activeModals,
|
||||
}),
|
||||
[isModalActive, activeModals],
|
||||
)
|
||||
|
||||
const methods = React.useMemo(
|
||||
() => ({
|
||||
openModal,
|
||||
closeModal,
|
||||
}),
|
||||
[openModal, closeModal],
|
||||
)
|
||||
|
||||
return (
|
||||
<ModalContext.Provider value={state}>
|
||||
<ModalControlContext.Provider value={methods}>
|
||||
{children}
|
||||
</ModalControlContext.Provider>
|
||||
</ModalContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useModals() {
|
||||
return React.useContext(ModalContext)
|
||||
}
|
||||
|
||||
export function useModalControls() {
|
||||
return React.useContext(ModalControlContext)
|
||||
}
|
|
@ -4,7 +4,6 @@ import {ImageModel} from './image'
|
|||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {openPicker} from 'lib/media/picker'
|
||||
import {getImageDim} from 'lib/media/manip'
|
||||
import {isNative} from 'platform/detection'
|
||||
|
||||
export class GalleryModel {
|
||||
images: ImageModel[] = []
|
||||
|
@ -42,18 +41,6 @@ export class GalleryModel {
|
|||
}
|
||||
}
|
||||
|
||||
async edit(image: ImageModel) {
|
||||
if (isNative) {
|
||||
this.crop(image)
|
||||
} else {
|
||||
this.rootStore.shell.openModal({
|
||||
name: 'edit-image',
|
||||
image,
|
||||
gallery: this,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async paste(uri: string) {
|
||||
if (this.size >= 4) {
|
||||
return
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
import {AppBskyEmbedRecord, AppBskyActorDefs, ModerationUI} from '@atproto/api'
|
||||
import {AppBskyEmbedRecord} from '@atproto/api'
|
||||
import {RootStoreModel} from '../root-store'
|
||||
import {makeAutoObservable, runInAction} from 'mobx'
|
||||
import {ProfileModel} from '../content/profile'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {ImageModel} from '../media/image'
|
||||
import {ListModel} from '../content/list'
|
||||
import {GalleryModel} from '../media/gallery'
|
||||
import {StyleProp, ViewStyle} from 'react-native'
|
||||
import {
|
||||
shouldRequestEmailConfirmation,
|
||||
setEmailConfirmationRequested,
|
||||
} from '#/state/shell/reminders'
|
||||
import {unstable__openModal} from '#/state/modals'
|
||||
|
||||
export type ColorMode = 'system' | 'light' | 'dark'
|
||||
|
||||
|
@ -18,200 +14,6 @@ export function isColorMode(v: unknown): v is ColorMode {
|
|||
return v === 'system' || v === 'light' || v === 'dark'
|
||||
}
|
||||
|
||||
export interface ConfirmModal {
|
||||
name: 'confirm'
|
||||
title: string
|
||||
message: string | (() => JSX.Element)
|
||||
onPressConfirm: () => void | Promise<void>
|
||||
onPressCancel?: () => void | Promise<void>
|
||||
confirmBtnText?: string
|
||||
confirmBtnStyle?: StyleProp<ViewStyle>
|
||||
cancelBtnText?: string
|
||||
}
|
||||
|
||||
export interface EditProfileModal {
|
||||
name: 'edit-profile'
|
||||
profileView: ProfileModel
|
||||
onUpdate?: () => void
|
||||
}
|
||||
|
||||
export interface ProfilePreviewModal {
|
||||
name: 'profile-preview'
|
||||
did: string
|
||||
}
|
||||
|
||||
export interface ServerInputModal {
|
||||
name: 'server-input'
|
||||
initialService: string
|
||||
onSelect: (url: string) => void
|
||||
}
|
||||
|
||||
export interface ModerationDetailsModal {
|
||||
name: 'moderation-details'
|
||||
context: 'account' | 'content'
|
||||
moderation: ModerationUI
|
||||
}
|
||||
|
||||
export type ReportModal = {
|
||||
name: 'report'
|
||||
} & (
|
||||
| {
|
||||
uri: string
|
||||
cid: string
|
||||
}
|
||||
| {did: string}
|
||||
)
|
||||
|
||||
export interface CreateOrEditListModal {
|
||||
name: 'create-or-edit-list'
|
||||
purpose?: string
|
||||
list?: ListModel
|
||||
onSave?: (uri: string) => void
|
||||
}
|
||||
|
||||
export interface UserAddRemoveListsModal {
|
||||
name: 'user-add-remove-lists'
|
||||
subject: string
|
||||
displayName: string
|
||||
onAdd?: (listUri: string) => void
|
||||
onRemove?: (listUri: string) => void
|
||||
}
|
||||
|
||||
export interface ListAddUserModal {
|
||||
name: 'list-add-user'
|
||||
list: ListModel
|
||||
onAdd?: (profile: AppBskyActorDefs.ProfileViewBasic) => void
|
||||
}
|
||||
|
||||
export interface EditImageModal {
|
||||
name: 'edit-image'
|
||||
image: ImageModel
|
||||
gallery: GalleryModel
|
||||
}
|
||||
|
||||
export interface CropImageModal {
|
||||
name: 'crop-image'
|
||||
uri: string
|
||||
onSelect: (img?: RNImage) => void
|
||||
}
|
||||
|
||||
export interface AltTextImageModal {
|
||||
name: 'alt-text-image'
|
||||
image: ImageModel
|
||||
}
|
||||
|
||||
export interface DeleteAccountModal {
|
||||
name: 'delete-account'
|
||||
}
|
||||
|
||||
export interface RepostModal {
|
||||
name: 'repost'
|
||||
onRepost: () => void
|
||||
onQuote: () => void
|
||||
isReposted: boolean
|
||||
}
|
||||
|
||||
export interface SelfLabelModal {
|
||||
name: 'self-label'
|
||||
labels: string[]
|
||||
hasMedia: boolean
|
||||
onChange: (labels: string[]) => void
|
||||
}
|
||||
|
||||
export interface ChangeHandleModal {
|
||||
name: 'change-handle'
|
||||
onChanged: () => void
|
||||
}
|
||||
|
||||
export interface WaitlistModal {
|
||||
name: 'waitlist'
|
||||
}
|
||||
|
||||
export interface InviteCodesModal {
|
||||
name: 'invite-codes'
|
||||
}
|
||||
|
||||
export interface AddAppPasswordModal {
|
||||
name: 'add-app-password'
|
||||
}
|
||||
|
||||
export interface ContentFilteringSettingsModal {
|
||||
name: 'content-filtering-settings'
|
||||
}
|
||||
|
||||
export interface ContentLanguagesSettingsModal {
|
||||
name: 'content-languages-settings'
|
||||
}
|
||||
|
||||
export interface PostLanguagesSettingsModal {
|
||||
name: 'post-languages-settings'
|
||||
}
|
||||
|
||||
export interface BirthDateSettingsModal {
|
||||
name: 'birth-date-settings'
|
||||
}
|
||||
|
||||
export interface VerifyEmailModal {
|
||||
name: 'verify-email'
|
||||
showReminder?: boolean
|
||||
}
|
||||
|
||||
export interface ChangeEmailModal {
|
||||
name: 'change-email'
|
||||
}
|
||||
|
||||
export interface SwitchAccountModal {
|
||||
name: 'switch-account'
|
||||
}
|
||||
|
||||
export interface LinkWarningModal {
|
||||
name: 'link-warning'
|
||||
text: string
|
||||
href: string
|
||||
}
|
||||
|
||||
export type Modal =
|
||||
// Account
|
||||
| AddAppPasswordModal
|
||||
| ChangeHandleModal
|
||||
| DeleteAccountModal
|
||||
| EditProfileModal
|
||||
| ProfilePreviewModal
|
||||
| BirthDateSettingsModal
|
||||
| VerifyEmailModal
|
||||
| ChangeEmailModal
|
||||
| SwitchAccountModal
|
||||
|
||||
// Curation
|
||||
| ContentFilteringSettingsModal
|
||||
| ContentLanguagesSettingsModal
|
||||
| PostLanguagesSettingsModal
|
||||
|
||||
// Moderation
|
||||
| ModerationDetailsModal
|
||||
| ReportModal
|
||||
|
||||
// Lists
|
||||
| CreateOrEditListModal
|
||||
| UserAddRemoveListsModal
|
||||
| ListAddUserModal
|
||||
|
||||
// Posts
|
||||
| AltTextImageModal
|
||||
| CropImageModal
|
||||
| EditImageModal
|
||||
| ServerInputModal
|
||||
| RepostModal
|
||||
| SelfLabelModal
|
||||
|
||||
// Bluesky access
|
||||
| WaitlistModal
|
||||
| InviteCodesModal
|
||||
|
||||
// Generic
|
||||
| ConfirmModal
|
||||
| LinkWarningModal
|
||||
|
||||
interface LightboxModel {}
|
||||
|
||||
export class ProfileImageLightbox implements LightboxModel {
|
||||
|
@ -267,8 +69,6 @@ export interface ComposerOpts {
|
|||
}
|
||||
|
||||
export class ShellUiModel {
|
||||
isModalActive = false
|
||||
activeModals: Modal[] = []
|
||||
isLightboxActive = false
|
||||
activeLightbox: ProfileImageLightbox | ImagesLightbox | null = null
|
||||
isComposerActive = false
|
||||
|
@ -293,10 +93,6 @@ export class ShellUiModel {
|
|||
this.closeLightbox()
|
||||
return true
|
||||
}
|
||||
if (this.isModalActive) {
|
||||
this.closeModal()
|
||||
return true
|
||||
}
|
||||
if (this.isComposerActive) {
|
||||
this.closeComposer()
|
||||
return true
|
||||
|
@ -311,25 +107,11 @@ export class ShellUiModel {
|
|||
if (this.isLightboxActive) {
|
||||
this.closeLightbox()
|
||||
}
|
||||
while (this.isModalActive) {
|
||||
this.closeModal()
|
||||
}
|
||||
if (this.isComposerActive) {
|
||||
this.closeComposer()
|
||||
}
|
||||
}
|
||||
|
||||
openModal(modal: Modal) {
|
||||
this.rootStore.emitNavigation()
|
||||
this.isModalActive = true
|
||||
this.activeModals.push(modal)
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
this.activeModals.pop()
|
||||
this.isModalActive = this.activeModals.length > 0
|
||||
}
|
||||
|
||||
openLightbox(lightbox: ProfileImageLightbox | ImagesLightbox) {
|
||||
this.rootStore.emitNavigation()
|
||||
this.isLightboxActive = true
|
||||
|
@ -363,7 +145,7 @@ export class ShellUiModel {
|
|||
setupLoginModals() {
|
||||
this.rootStore.onSessionReady(() => {
|
||||
if (shouldRequestEmailConfirmation(this.rootStore.session)) {
|
||||
this.openModal({name: 'verify-email', showReminder: true})
|
||||
unstable__openModal({name: 'verify-email', showReminder: true})
|
||||
setEmailConfirmationRequested()
|
||||
}
|
||||
})
|
||||
|
|
|
@ -10,8 +10,8 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {TextInput} from '../util/TextInput'
|
||||
import {Policies} from './Policies'
|
||||
import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
|
||||
import {useStores} from 'state/index'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
/** STEP 2: Your account
|
||||
* @field Invite code or waitlist
|
||||
|
@ -28,11 +28,11 @@ export const Step2 = observer(function Step2Impl({
|
|||
model: CreateAccountModel
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const onPressWaitlist = React.useCallback(() => {
|
||||
store.shell.openModal({name: 'waitlist'})
|
||||
}, [store])
|
||||
openModal({name: 'waitlist'})
|
||||
}, [openModal])
|
||||
|
||||
return (
|
||||
<View>
|
||||
|
|
|
@ -31,6 +31,7 @@ import {useTheme} from 'lib/ThemeContext'
|
|||
import {cleanError} from 'lib/strings/errors'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
enum Forms {
|
||||
Login,
|
||||
|
@ -303,9 +304,10 @@ const LoginForm = ({
|
|||
const [identifier, setIdentifier] = useState<string>(initialHandle)
|
||||
const [password, setPassword] = useState<string>('')
|
||||
const passwordInputRef = useRef<TextInput>(null)
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const onPressSelectService = () => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'server-input',
|
||||
initialService: serviceUrl,
|
||||
onSelect: setServiceUrl,
|
||||
|
@ -528,7 +530,6 @@ const LoginForm = ({
|
|||
}
|
||||
|
||||
const ForgotPasswordForm = ({
|
||||
store,
|
||||
error,
|
||||
serviceUrl,
|
||||
serviceDescription,
|
||||
|
@ -551,13 +552,14 @@ const ForgotPasswordForm = ({
|
|||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||
const [email, setEmail] = useState<string>('')
|
||||
const {screen} = useAnalytics()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
useEffect(() => {
|
||||
screen('Signin:ForgotPassword')
|
||||
}, [screen])
|
||||
|
||||
const onPressSelectService = () => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'server-input',
|
||||
initialService: serviceUrl,
|
||||
onSelect: setServiceUrl,
|
||||
|
|
|
@ -49,6 +49,7 @@ import {LabelsBtn} from './labels/LabelsBtn'
|
|||
import {SelectLangBtn} from './select-language/SelectLangBtn'
|
||||
import {EmojiPickerButton} from './text-input/web/EmojiPicker.web'
|
||||
import {insertMentionAt} from 'lib/strings/mention-manip'
|
||||
import {useModals, useModalControls} from '#/state/modals'
|
||||
import {useRequireAltTextEnabled} from '#/state/preferences'
|
||||
import {
|
||||
useLanguagePrefs,
|
||||
|
@ -64,6 +65,8 @@ export const ComposePost = observer(function ComposePost({
|
|||
quote: initQuote,
|
||||
mention: initMention,
|
||||
}: Props) {
|
||||
const {activeModals} = useModals()
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
const {track} = useAnalytics()
|
||||
const pal = usePalette('default')
|
||||
const {isDesktop, isMobile} = useWebMediaQueries()
|
||||
|
@ -118,18 +121,18 @@ export const ComposePost = observer(function ComposePost({
|
|||
|
||||
const onPressCancel = useCallback(() => {
|
||||
if (graphemeLength > 0 || !gallery.isEmpty) {
|
||||
if (store.shell.activeModals.some(modal => modal.name === 'confirm')) {
|
||||
store.shell.closeModal()
|
||||
if (activeModals.some(modal => modal.name === 'confirm')) {
|
||||
closeModal()
|
||||
}
|
||||
if (Keyboard) {
|
||||
Keyboard.dismiss()
|
||||
}
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Discard draft',
|
||||
onPressConfirm: onClose,
|
||||
onPressCancel: () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
},
|
||||
message: "Are you sure you'd like to discard this draft?",
|
||||
confirmBtnText: 'Discard',
|
||||
|
@ -138,7 +141,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
} else {
|
||||
onClose()
|
||||
}
|
||||
}, [store, onClose, graphemeLength, gallery])
|
||||
}, [openModal, closeModal, activeModals, onClose, graphemeLength, gallery])
|
||||
// android back button
|
||||
useEffect(() => {
|
||||
if (!isAndroid) {
|
||||
|
|
|
@ -3,11 +3,11 @@ import {Keyboard, StyleSheet} from 'react-native'
|
|||
import {observer} from 'mobx-react-lite'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useStores} from 'state/index'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
||||
import {isNative} from 'platform/detection'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const LabelsBtn = observer(function LabelsBtn({
|
||||
labels,
|
||||
|
@ -19,7 +19,7 @@ export const LabelsBtn = observer(function LabelsBtn({
|
|||
onChange: (v: string[]) => void
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
return (
|
||||
<Button
|
||||
|
@ -34,7 +34,7 @@ export const LabelsBtn = observer(function LabelsBtn({
|
|||
Keyboard.dismiss()
|
||||
}
|
||||
}
|
||||
store.shell.openModal({name: 'self-label', labels, hasMedia, onChange})
|
||||
openModal({name: 'self-label', labels, hasMedia, onChange})
|
||||
}}>
|
||||
<ShieldExclamation style={pal.link} size={26} />
|
||||
{labels.length > 0 ? (
|
||||
|
|
|
@ -7,11 +7,11 @@ import {s, colors} from 'lib/styles'
|
|||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {openAltTextModal} from 'lib/media/alt-text'
|
||||
import {Dimensions} from 'lib/media/types'
|
||||
import {useStores} from 'state/index'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {isNative} from 'platform/detection'
|
||||
|
||||
const IMAGE_GAP = 8
|
||||
|
||||
|
@ -47,9 +47,9 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
gallery,
|
||||
containerInfo,
|
||||
}: GalleryInnerProps) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
let side: number
|
||||
|
||||
|
@ -117,7 +117,10 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
Keyboard.dismiss()
|
||||
openAltTextModal(store, image)
|
||||
openModal({
|
||||
name: 'alt-text-image',
|
||||
image,
|
||||
})
|
||||
}}
|
||||
style={[styles.altTextControl, altTextControlStyle]}>
|
||||
<Text style={styles.altTextControlLabel} accessible={false}>
|
||||
|
@ -137,7 +140,17 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
accessibilityRole="button"
|
||||
accessibilityLabel="Edit image"
|
||||
accessibilityHint=""
|
||||
onPress={() => gallery.edit(image)}
|
||||
onPress={() => {
|
||||
if (isNative) {
|
||||
gallery.crop(image)
|
||||
} else {
|
||||
openModal({
|
||||
name: 'edit-image',
|
||||
image,
|
||||
gallery,
|
||||
})
|
||||
}
|
||||
}}
|
||||
style={styles.imageControl}>
|
||||
<FontAwesomeIcon
|
||||
icon="pen"
|
||||
|
@ -165,7 +178,10 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
Keyboard.dismiss()
|
||||
openAltTextModal(store, image)
|
||||
openModal({
|
||||
name: 'alt-text-image',
|
||||
image,
|
||||
})
|
||||
}}
|
||||
style={styles.altTextHiddenRegion}
|
||||
/>
|
||||
|
|
|
@ -12,9 +12,9 @@ import {
|
|||
DropdownItemButton,
|
||||
} from 'view/com/util/forms/DropdownButton'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useStores} from 'state/index'
|
||||
import {isNative} from 'platform/detection'
|
||||
import {codeToLanguageName} from '../../../../locale/helpers'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useLanguagePrefs,
|
||||
useSetLanguagePrefs,
|
||||
|
@ -24,7 +24,7 @@ import {
|
|||
|
||||
export const SelectLangBtn = observer(function SelectLangBtn() {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {openModal} = useModalControls()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useSetLanguagePrefs()
|
||||
|
||||
|
@ -34,8 +34,8 @@ export const SelectLangBtn = observer(function SelectLangBtn() {
|
|||
Keyboard.dismiss()
|
||||
}
|
||||
}
|
||||
store.shell.openModal({name: 'post-languages-settings'})
|
||||
}, [store])
|
||||
openModal({name: 'post-languages-settings'})
|
||||
}, [openModal])
|
||||
|
||||
const postLanguagesPref = toPostLanguages(langPrefs.postLanguage)
|
||||
const items: DropdownItem[] = useMemo(() => {
|
||||
|
|
|
@ -10,12 +10,12 @@ import {observer} from 'mobx-react-lite'
|
|||
import {FeedSourceModel} from 'state/models/content/feed-source'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {useStores} from 'state/index'
|
||||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {sanitizeHandle} from 'lib/strings/handles'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const FeedSourceCard = observer(function FeedSourceCardImpl({
|
||||
item,
|
||||
|
@ -30,13 +30,13 @@ export const FeedSourceCard = observer(function FeedSourceCardImpl({
|
|||
showDescription?: boolean
|
||||
showLikes?: boolean
|
||||
}) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const onToggleSaved = React.useCallback(async () => {
|
||||
if (item.isSaved) {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Remove from my feeds',
|
||||
message: `Remove ${item.displayName} from my feeds?`,
|
||||
|
@ -59,7 +59,7 @@ export const FeedSourceCard = observer(function FeedSourceCardImpl({
|
|||
logger.error('Failed to save feed', {error: e})
|
||||
}
|
||||
}
|
||||
}, [store, item])
|
||||
}, [openModal, item])
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
|
|
|
@ -17,11 +17,11 @@ import {Button} from '../util/forms/Button'
|
|||
import {ListModel} from 'state/models/content/list'
|
||||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useStores} from 'state/index'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {s} from 'lib/styles'
|
||||
import {OnScrollCb} from 'lib/hooks/useOnMainScroll'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||
const EMPTY_ITEM = {_reactKey: '__empty__'}
|
||||
|
@ -54,10 +54,10 @@ export const ListItems = observer(function ListItemsImpl({
|
|||
desktopFixedHeightOffset?: number
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {track} = useAnalytics()
|
||||
const [isRefreshing, setIsRefreshing] = React.useState(false)
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const data = React.useMemo(() => {
|
||||
let items: any[] = []
|
||||
|
@ -115,7 +115,7 @@ export const ListItems = observer(function ListItemsImpl({
|
|||
|
||||
const onPressEditMembership = React.useCallback(
|
||||
(profile: AppBskyActorDefs.ProfileViewBasic) => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'user-add-remove-lists',
|
||||
subject: profile.did,
|
||||
displayName: profile.displayName || profile.handle,
|
||||
|
@ -131,7 +131,7 @@ export const ListItems = observer(function ListItemsImpl({
|
|||
},
|
||||
})
|
||||
},
|
||||
[store, list],
|
||||
[openModal, list],
|
||||
)
|
||||
|
||||
// rendering
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
import Clipboard from '@react-native-clipboard/clipboard'
|
||||
import * as Toast from '../util/Toast'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['70%']
|
||||
|
||||
|
@ -54,6 +55,7 @@ const shadesOfBlue: string[] = [
|
|||
export function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const [name, setName] = useState(
|
||||
shadesOfBlue[Math.floor(Math.random() * shadesOfBlue.length)],
|
||||
)
|
||||
|
@ -69,8 +71,8 @@ export function Component({}: {}) {
|
|||
}, [appPassword])
|
||||
|
||||
const onDone = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const createAppPassword = async () => {
|
||||
// if name is all whitespace, we don't allow it
|
||||
|
|
|
@ -17,9 +17,9 @@ import {MAX_ALT_TEXT} from 'lib/constants'
|
|||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {Text} from '../util/text/Text'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {useStores} from 'state/index'
|
||||
import {isAndroid, isWeb} from 'platform/detection'
|
||||
import {ImageModel} from 'state/models/media/image'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['fullscreen']
|
||||
|
||||
|
@ -29,10 +29,10 @@ interface Props {
|
|||
|
||||
export function Component({image}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const theme = useTheme()
|
||||
const [altText, setAltText] = useState(image.altText)
|
||||
const windim = useWindowDimensions()
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const imageStyles = useMemo<ImageStyle>(() => {
|
||||
const maxWidth = isWeb ? 450 : windim.width
|
||||
|
@ -53,11 +53,11 @@ export function Component({image}: Props) {
|
|||
|
||||
const onPressSave = useCallback(() => {
|
||||
image.setAltText(altText)
|
||||
store.shell.closeModal()
|
||||
}, [store, image, altText])
|
||||
closeModal()
|
||||
}, [closeModal, image, altText])
|
||||
|
||||
const onPressCancel = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -15,12 +15,14 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['50%']
|
||||
|
||||
export const Component = observer(function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const [date, setDate] = useState<Date>(
|
||||
store.preferences.birthDate || new Date(),
|
||||
)
|
||||
|
@ -33,7 +35,7 @@ export const Component = observer(function Component({}: {}) {
|
|||
setIsProcessing(true)
|
||||
try {
|
||||
await store.preferences.setBirthDate(date)
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e) {
|
||||
setError(cleanError(String(e)))
|
||||
} finally {
|
||||
|
|
|
@ -12,6 +12,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
enum Stages {
|
||||
InputEmail,
|
||||
|
@ -32,6 +33,7 @@ export const Component = observer(function Component({}: {}) {
|
|||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||
const [error, setError] = useState<string>('')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
||||
const onRequestChange = async () => {
|
||||
if (email === store.session.currentSession?.email) {
|
||||
|
@ -90,8 +92,8 @@ export const Component = observer(function Component({}: {}) {
|
|||
}
|
||||
|
||||
const onVerify = async () => {
|
||||
store.shell.closeModal()
|
||||
store.shell.openModal({name: 'verify-email'})
|
||||
closeModal()
|
||||
openModal({name: 'verify-email'})
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -207,7 +209,7 @@ export const Component = observer(function Component({}: {}) {
|
|||
<Button
|
||||
testID="cancelBtn"
|
||||
type="default"
|
||||
onPress={() => store.shell.closeModal()}
|
||||
onPress={() => closeModal()}
|
||||
accessibilityLabel="Cancel"
|
||||
accessibilityHint=""
|
||||
label="Cancel"
|
||||
|
|
|
@ -22,6 +22,7 @@ import {useTheme} from 'lib/ThemeContext'
|
|||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['100%']
|
||||
|
||||
|
@ -30,6 +31,7 @@ export function Component({onChanged}: {onChanged: () => void}) {
|
|||
const [error, setError] = useState<string>('')
|
||||
const pal = usePalette('default')
|
||||
const {track} = useAnalytics()
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const [isProcessing, setProcessing] = useState<boolean>(false)
|
||||
const [retryDescribeTrigger, setRetryDescribeTrigger] = React.useState<any>(
|
||||
|
@ -85,8 +87,8 @@ export function Component({onChanged}: {onChanged: () => void}) {
|
|||
// events
|
||||
// =
|
||||
const onPressCancel = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
const onPressRetryConnect = React.useCallback(
|
||||
() => setRetryDescribeTrigger({}),
|
||||
[setRetryDescribeTrigger],
|
||||
|
@ -110,7 +112,7 @@ export function Component({onChanged}: {onChanged: () => void}) {
|
|||
await store.agent.updateHandle({
|
||||
handle: newHandle,
|
||||
})
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
onChanged()
|
||||
} catch (err: any) {
|
||||
setError(cleanError(err))
|
||||
|
@ -127,6 +129,7 @@ export function Component({onChanged}: {onChanged: () => void}) {
|
|||
isCustom,
|
||||
onChanged,
|
||||
track,
|
||||
closeModal,
|
||||
])
|
||||
|
||||
// rendering
|
||||
|
|
|
@ -6,13 +6,13 @@ import {
|
|||
View,
|
||||
} from 'react-native'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import type {ConfirmModal} from 'state/models/ui/shell'
|
||||
import type {ConfirmModal} from '#/state/modals'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['50%']
|
||||
|
||||
|
@ -26,7 +26,7 @@ export function Component({
|
|||
cancelBtnText,
|
||||
}: ConfirmModal) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||
const [error, setError] = useState<string>('')
|
||||
const onPress = async () => {
|
||||
|
@ -34,7 +34,7 @@ export function Component({
|
|||
setIsProcessing(true)
|
||||
try {
|
||||
await onPressConfirm()
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
return
|
||||
} catch (e: any) {
|
||||
setError(cleanError(e))
|
||||
|
|
|
@ -16,6 +16,7 @@ import {isIOS} from 'platform/detection'
|
|||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import * as Toast from '../util/Toast'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['90%']
|
||||
|
||||
|
@ -24,14 +25,15 @@ export const Component = observer(
|
|||
const store = useStores()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
React.useEffect(() => {
|
||||
store.preferences.sync()
|
||||
}, [store])
|
||||
|
||||
const onPressDone = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
return (
|
||||
<View testID="contentFilteringModal" style={[pal.view, styles.container]}>
|
||||
|
@ -89,8 +91,9 @@ const AdultContentEnabledPref = observer(
|
|||
function AdultContentEnabledPrefImpl() {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const onSetAge = () => store.shell.openModal({name: 'birth-date-settings'})
|
||||
const onSetAge = () => openModal({name: 'birth-date-settings'})
|
||||
|
||||
const onToggleAdultContent = async () => {
|
||||
if (isIOS) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import {useTheme} from 'lib/ThemeContext'
|
|||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {cleanError, isNetworkError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const MAX_NAME = 64 // todo
|
||||
const MAX_DESCRIPTION = 300 // todo
|
||||
|
@ -40,6 +41,7 @@ export function Component({
|
|||
list?: ListModel
|
||||
}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [error, setError] = useState<string>('')
|
||||
const pal = usePalette('default')
|
||||
|
@ -67,8 +69,8 @@ export function Component({
|
|||
const [newAvatar, setNewAvatar] = useState<RNImage | undefined | null>()
|
||||
|
||||
const onPressCancel = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const onSelectNewAvatar = useCallback(
|
||||
async (img: RNImage | null) => {
|
||||
|
@ -123,7 +125,7 @@ export function Component({
|
|||
Toast.show(`${purposeLabel} list created`)
|
||||
onSave?.(res.uri)
|
||||
}
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
if (isNetworkError(e)) {
|
||||
setError(
|
||||
|
@ -141,6 +143,7 @@ export function Component({
|
|||
error,
|
||||
onSave,
|
||||
store,
|
||||
closeModal,
|
||||
activePurpose,
|
||||
isCurateList,
|
||||
purposeLabel,
|
||||
|
|
|
@ -17,6 +17,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {resetToTab} from '../../../Navigation'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['60%']
|
||||
|
||||
|
@ -24,6 +25,7 @@ export function Component({}: {}) {
|
|||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false)
|
||||
const [confirmCode, setConfirmCode] = React.useState<string>('')
|
||||
|
@ -55,14 +57,14 @@ export function Component({}: {}) {
|
|||
Toast.show('Your account has been deleted')
|
||||
resetToTab('HomeTab')
|
||||
store.session.clear()
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
setError(cleanError(e))
|
||||
}
|
||||
setIsProcessing(false)
|
||||
}
|
||||
const onCancel = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
return (
|
||||
<View style={[styles.container, pal.view]}>
|
||||
|
|
|
@ -6,7 +6,6 @@ import {gradients, s} from 'lib/styles'
|
|||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {Text} from '../util/text/Text'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {useStores} from 'state/index'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import ImageEditor, {Position} from 'react-avatar-editor'
|
||||
import {TextInput} from './util'
|
||||
|
@ -19,6 +18,7 @@ import {Slider} from '@miblanchard/react-native-slider'
|
|||
import {MaterialIcons} from '@expo/vector-icons'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {getKeys} from 'lib/type-assertions'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['80%']
|
||||
|
||||
|
@ -52,9 +52,9 @@ export const Component = observer(function EditImageImpl({
|
|||
}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const store = useStores()
|
||||
const windowDimensions = useWindowDimensions()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const {
|
||||
aspectRatio,
|
||||
|
@ -128,8 +128,8 @@ export const Component = observer(function EditImageImpl({
|
|||
}, [image])
|
||||
|
||||
const onCloseModal = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store.shell])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const onPressCancel = useCallback(async () => {
|
||||
await gallery.previous(image)
|
||||
|
|
|
@ -13,7 +13,6 @@ import LinearGradient from 'react-native-linear-gradient'
|
|||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {useStores} from 'state/index'
|
||||
import {ProfileModel} from 'state/models/content/profile'
|
||||
import {s, colors, gradients} from 'lib/styles'
|
||||
import {enforceLen} from 'lib/strings/helpers'
|
||||
|
@ -27,6 +26,7 @@ import {useAnalytics} from 'lib/analytics/analytics'
|
|||
import {cleanError, isNetworkError} from 'lib/strings/errors'
|
||||
import Animated, {FadeOut} from 'react-native-reanimated'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const AnimatedTouchableOpacity =
|
||||
Animated.createAnimatedComponent(TouchableOpacity)
|
||||
|
@ -40,11 +40,11 @@ export function Component({
|
|||
profileView: ProfileModel
|
||||
onUpdate?: () => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
const [error, setError] = useState<string>('')
|
||||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const {track} = useAnalytics()
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const [isProcessing, setProcessing] = useState<boolean>(false)
|
||||
const [displayName, setDisplayName] = useState<string>(
|
||||
|
@ -66,7 +66,7 @@ export function Component({
|
|||
RNImage | undefined | null
|
||||
>()
|
||||
const onPressCancel = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
const onSelectNewAvatar = useCallback(
|
||||
async (img: RNImage | null) => {
|
||||
|
@ -123,7 +123,7 @@ export function Component({
|
|||
)
|
||||
Toast.show('Profile updated')
|
||||
onUpdate?.()
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
if (isNetworkError(e)) {
|
||||
setError(
|
||||
|
@ -141,7 +141,7 @@ export function Component({
|
|||
error,
|
||||
profileView,
|
||||
onUpdate,
|
||||
store,
|
||||
closeModal,
|
||||
displayName,
|
||||
description,
|
||||
newUserAvatar,
|
||||
|
|
|
@ -15,6 +15,7 @@ import {ScrollView} from './util'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useInvitesState, useInvitesAPI} from '#/state/invites'
|
||||
import {UserInfoText} from '../util/UserInfoText'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
|
@ -25,11 +26,12 @@ export const snapPoints = ['70%']
|
|||
export function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
|
||||
const onClose = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
if (store.me.invites.length === 0) {
|
||||
return (
|
||||
|
|
|
@ -5,12 +5,12 @@ import {observer} from 'mobx-react-lite'
|
|||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {Button} from '../util/forms/Button'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {isPossiblyAUrl, splitApexDomain} from 'lib/strings/url-helpers'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['50%']
|
||||
|
||||
|
@ -22,12 +22,12 @@ export const Component = observer(function Component({
|
|||
href: string
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const potentiallyMisleading = isPossiblyAUrl(text)
|
||||
|
||||
const onPressVisit = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
Linking.openURL(href)
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ export const Component = observer(function Component({
|
|||
<Button
|
||||
testID="cancelBtn"
|
||||
type="default"
|
||||
onPress={() => store.shell.closeModal()}
|
||||
onPress={() => closeModal()}
|
||||
accessibilityLabel="Cancel"
|
||||
accessibilityHint=""
|
||||
label="Cancel"
|
||||
|
|
|
@ -26,6 +26,7 @@ import {cleanError} from 'lib/strings/errors'
|
|||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||
import {sanitizeHandle} from 'lib/strings/handles'
|
||||
import {HITSLOP_20} from '#/lib/constants'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['90%']
|
||||
|
||||
|
@ -38,6 +39,7 @@ export const Component = observer(function Component({
|
|||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [query, setQuery] = useState('')
|
||||
const autocompleteView = useMemo<UserAutocompleteModel>(
|
||||
|
@ -146,7 +148,7 @@ export const Component = observer(function Component({
|
|||
<Button
|
||||
testID="doneBtn"
|
||||
type="default"
|
||||
onPress={() => store.shell.closeModal()}
|
||||
onPress={() => closeModal()}
|
||||
accessibilityLabel="Done"
|
||||
accessibilityHint=""
|
||||
label="Done"
|
||||
|
|
|
@ -3,13 +3,13 @@ import {StyleSheet} from 'react-native'
|
|||
import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import BottomSheet from '@gorhom/bottom-sheet'
|
||||
import {useStores} from 'state/index'
|
||||
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {timeout} from 'lib/async/timeout'
|
||||
import {navigate} from '../../../Navigation'
|
||||
import once from 'lodash.once'
|
||||
|
||||
import {useModals, useModalControls} from '#/state/modals'
|
||||
import * as ConfirmModal from './Confirm'
|
||||
import * as EditProfileModal from './EditProfile'
|
||||
import * as ProfilePreviewModal from './ProfilePreview'
|
||||
|
@ -41,17 +41,17 @@ const DEFAULT_SNAPPOINTS = ['90%']
|
|||
const HANDLE_HEIGHT = 24
|
||||
|
||||
export const ModalsContainer = observer(function ModalsContainer() {
|
||||
const store = useStores()
|
||||
const {isModalActive, activeModals} = useModals()
|
||||
const {closeModal} = useModalControls()
|
||||
const bottomSheetRef = useRef<BottomSheet>(null)
|
||||
const pal = usePalette('default')
|
||||
const safeAreaInsets = useSafeAreaInsets()
|
||||
|
||||
const activeModal =
|
||||
store.shell.activeModals[store.shell.activeModals.length - 1]
|
||||
const activeModal = activeModals[activeModals.length - 1]
|
||||
|
||||
const navigateOnce = once(navigate)
|
||||
|
||||
const onBottomSheetAnimate = (fromIndex: number, toIndex: number) => {
|
||||
const onBottomSheetAnimate = (_fromIndex: number, toIndex: number) => {
|
||||
if (activeModal?.name === 'profile-preview' && toIndex === 1) {
|
||||
// begin loading the profile screen behind the scenes
|
||||
navigateOnce('Profile', {name: activeModal.did})
|
||||
|
@ -59,7 +59,7 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||
}
|
||||
const onBottomSheetChange = async (snapPoint: number) => {
|
||||
if (snapPoint === -1) {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} else if (activeModal?.name === 'profile-preview' && snapPoint === 1) {
|
||||
await navigateOnce('Profile', {name: activeModal.did})
|
||||
// There is no particular callback for when the view has actually been presented.
|
||||
|
@ -67,21 +67,21 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||
// It's acceptable because the data is already being fetched + it usually takes longer anyway.
|
||||
// TODO: Figure out why avatar/cover don't always show instantly from cache.
|
||||
await timeout(200)
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
const onClose = () => {
|
||||
bottomSheetRef.current?.close()
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (store.shell.isModalActive) {
|
||||
if (isModalActive) {
|
||||
bottomSheetRef.current?.expand()
|
||||
} else {
|
||||
bottomSheetRef.current?.close()
|
||||
}
|
||||
}, [store.shell.isModalActive, bottomSheetRef, activeModal?.name])
|
||||
}, [isModalActive, bottomSheetRef, activeModal?.name])
|
||||
|
||||
let needsSafeTopInset = false
|
||||
let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS
|
||||
|
@ -184,12 +184,12 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||
snapPoints={snapPoints}
|
||||
topInset={topInset}
|
||||
handleHeight={HANDLE_HEIGHT}
|
||||
index={store.shell.isModalActive ? 0 : -1}
|
||||
index={isModalActive ? 0 : -1}
|
||||
enablePanDownToClose
|
||||
android_keyboardInputMode="adjustResize"
|
||||
keyboardBlurBehavior="restore"
|
||||
backdropComponent={
|
||||
store.shell.isModalActive ? createCustomBackdrop(onClose) : undefined
|
||||
isModalActive ? createCustomBackdrop(onClose) : undefined
|
||||
}
|
||||
handleIndicatorStyle={{backgroundColor: pal.text.color}}
|
||||
handleStyle={[styles.handle, pal.view]}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react'
|
||||
import {TouchableWithoutFeedback, StyleSheet, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {useStores} from 'state/index'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import type {Modal as ModalIface} from 'state/models/ui/shell'
|
||||
import type {Modal as ModalIface} from '#/state/modals'
|
||||
|
||||
import {useModals, useModalControls} from '#/state/modals'
|
||||
import * as ConfirmModal from './Confirm'
|
||||
import * as EditProfileModal from './EditProfile'
|
||||
import * as ProfilePreviewModal from './ProfilePreview'
|
||||
|
@ -34,15 +34,15 @@ import * as ChangeEmailModal from './ChangeEmail'
|
|||
import * as LinkWarningModal from './LinkWarning'
|
||||
|
||||
export const ModalsContainer = observer(function ModalsContainer() {
|
||||
const store = useStores()
|
||||
const {isModalActive, activeModals} = useModals()
|
||||
|
||||
if (!store.shell.isModalActive) {
|
||||
if (!isModalActive) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{store.shell.activeModals.map((modal, i) => (
|
||||
{activeModals.map((modal, i) => (
|
||||
<Modal key={`modal-${i}`} modal={modal} />
|
||||
))}
|
||||
</>
|
||||
|
@ -50,11 +50,12 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||
})
|
||||
|
||||
function Modal({modal}: {modal: ModalIface}) {
|
||||
const store = useStores()
|
||||
const {isModalActive} = useModals()
|
||||
const {closeModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
|
||||
if (!store.shell.isModalActive) {
|
||||
if (!isModalActive) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -62,7 +63,7 @@ function Modal({modal}: {modal: ModalIface}) {
|
|||
if (modal.name === 'crop-image' || modal.name === 'edit-image') {
|
||||
return // dont close on mask presses during crop
|
||||
}
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
const onInnerPress = () => {
|
||||
// TODO: can we use prevent default?
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {ModerationUI} from '@atproto/api'
|
||||
import {useStores} from 'state/index'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {s} from 'lib/styles'
|
||||
import {Text} from '../util/text/Text'
|
||||
|
@ -10,6 +9,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {listUriToHref} from 'lib/strings/url-helpers'
|
||||
import {Button} from '../util/forms/Button'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = [300]
|
||||
|
||||
|
@ -20,7 +20,7 @@ export function Component({
|
|||
context: 'account' | 'content'
|
||||
moderation: ModerationUI
|
||||
}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
|
||||
|
@ -99,10 +99,7 @@ export function Component({
|
|||
{description}
|
||||
</Text>
|
||||
<View style={s.flex1} />
|
||||
<Button
|
||||
type="primary"
|
||||
style={styles.btn}
|
||||
onPress={() => store.shell.closeModal()}>
|
||||
<Button type="primary" style={styles.btn} onPress={() => closeModal()}>
|
||||
<Text type="button-lg" style={[pal.textLight, s.textCenter, s.white]}>
|
||||
Okay
|
||||
</Text>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors, gradients} from 'lib/styles'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {RepostIcon} from 'lib/icons'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = [250]
|
||||
|
||||
|
@ -20,10 +20,10 @@ export function Component({
|
|||
isReposted: boolean
|
||||
// TODO: Add author into component
|
||||
}) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {closeModal} = useModalControls()
|
||||
const onPress = async () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -2,7 +2,6 @@ import React, {useState} from 'react'
|
|||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
|
@ -10,6 +9,7 @@ import {isWeb} from 'platform/detection'
|
|||
import {Button} from '../util/forms/Button'
|
||||
import {SelectableBtn} from '../util/forms/SelectableBtn'
|
||||
import {ScrollView} from 'view/com/modals/util'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const ADULT_CONTENT_LABELS = ['sexual', 'nudity', 'porn']
|
||||
|
||||
|
@ -25,7 +25,7 @@ export const Component = observer(function Component({
|
|||
onChange: (labels: string[]) => void
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [selected, setSelected] = useState(labels)
|
||||
|
||||
|
@ -143,7 +143,7 @@ export const Component = observer(function Component({
|
|||
<TouchableOpacity
|
||||
testID="confirmBtn"
|
||||
onPress={() => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}}
|
||||
style={styles.btn}
|
||||
accessibilityRole="button"
|
||||
|
|
|
@ -6,26 +6,26 @@ import {
|
|||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {ScrollView, TextInput} from './util'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'state/index'
|
||||
import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['80%']
|
||||
|
||||
export function Component({onSelect}: {onSelect: (url: string) => void}) {
|
||||
const theme = useTheme()
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const [customUrl, setCustomUrl] = useState<string>('')
|
||||
const {closeModal} = useModalControls()
|
||||
|
||||
const doSelect = (url: string) => {
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
url = `https://${url}`
|
||||
}
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
onSelect(url)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb, isAndroid} from 'platform/detection'
|
||||
import isEqual from 'lodash.isequal'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['fullscreen']
|
||||
|
||||
|
@ -36,6 +37,7 @@ export const Component = observer(function UserAddRemoveListsImpl({
|
|||
onRemove?: (listUri: string) => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const palPrimary = usePalette('primary')
|
||||
const palInverted = usePalette('inverted')
|
||||
|
@ -69,8 +71,8 @@ export const Component = observer(function UserAddRemoveListsImpl({
|
|||
}, [memberships, listsList, store, setSelected, setMembershipsLoaded])
|
||||
|
||||
const onPressCancel = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const onPressSave = useCallback(async () => {
|
||||
let changes
|
||||
|
@ -87,8 +89,8 @@ export const Component = observer(function UserAddRemoveListsImpl({
|
|||
for (const uri of changes.removed) {
|
||||
onRemove?.(uri)
|
||||
}
|
||||
store.shell.closeModal()
|
||||
}, [store, selected, memberships, onAdd, onRemove])
|
||||
closeModal()
|
||||
}, [closeModal, selected, memberships, onAdd, onRemove])
|
||||
|
||||
const onToggleSelected = useCallback(
|
||||
(uri: string) => {
|
||||
|
|
|
@ -20,6 +20,7 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['90%']
|
||||
|
||||
|
@ -43,6 +44,7 @@ export const Component = observer(function Component({
|
|||
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||
const [error, setError] = useState<string>('')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
||||
const onSendEmail = async () => {
|
||||
setError('')
|
||||
|
@ -67,7 +69,7 @@ export const Component = observer(function Component({
|
|||
})
|
||||
store.session.updateLocalAccountData({emailConfirmed: true})
|
||||
Toast.show('Email verified')
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
} catch (e) {
|
||||
setError(cleanError(String(e)))
|
||||
} finally {
|
||||
|
@ -76,8 +78,8 @@ export const Component = observer(function Component({
|
|||
}
|
||||
|
||||
const onEmailIncorrect = () => {
|
||||
store.shell.closeModal()
|
||||
store.shell.openModal({name: 'change-email'})
|
||||
closeModal()
|
||||
openModal({name: 'change-email'})
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -224,7 +226,7 @@ export const Component = observer(function Component({
|
|||
<Button
|
||||
testID="cancelBtn"
|
||||
type="default"
|
||||
onPress={() => store.shell.closeModal()}
|
||||
onPress={() => closeModal()}
|
||||
accessibilityLabel={
|
||||
stage === Stages.Reminder ? 'Not right now' : 'Cancel'
|
||||
}
|
||||
|
|
|
@ -12,19 +12,19 @@ import {
|
|||
} from '@fortawesome/react-native-fontawesome'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s, gradients} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {cleanError} from 'lib/strings/errors'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const snapPoints = ['80%']
|
||||
|
||||
export function Component({}: {}) {
|
||||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const [email, setEmail] = React.useState<string>('')
|
||||
const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false)
|
||||
const [isProcessing, setIsProcessing] = React.useState<boolean>(false)
|
||||
|
@ -54,7 +54,7 @@ export function Component({}: {}) {
|
|||
setIsProcessing(false)
|
||||
}
|
||||
const onCancel = () => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -7,10 +7,10 @@ import {Text} from 'view/com/util/text/Text'
|
|||
import {Dimensions} from 'lib/media/types'
|
||||
import {getDataUriSize} from 'lib/media/util'
|
||||
import {s, gradients} from 'lib/styles'
|
||||
import {useStores} from 'state/index'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {SquareIcon, RectWideIcon, RectTallIcon} from 'lib/icons'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
enum AspectRatio {
|
||||
Square = 'square',
|
||||
|
@ -33,7 +33,7 @@ export function Component({
|
|||
uri: string
|
||||
onSelect: (img?: RNImage) => void
|
||||
}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const [as, setAs] = React.useState<AspectRatio>(AspectRatio.Square)
|
||||
const [scale, setScale] = React.useState<number>(1)
|
||||
|
@ -43,7 +43,7 @@ export function Component({
|
|||
|
||||
const onPressCancel = () => {
|
||||
onSelect(undefined)
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
const onPressDone = () => {
|
||||
const canvas = editorRef.current?.getImageScaledToCanvas()
|
||||
|
@ -59,7 +59,7 @@ export function Component({
|
|||
} else {
|
||||
onSelect(undefined)
|
||||
}
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
}
|
||||
|
||||
let cropperStyle
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {ScrollView} from '../util'
|
||||
import {useStores} from 'state/index'
|
||||
import {Text} from '../../util/text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
|
@ -9,6 +8,7 @@ import {deviceLocales} from 'platform/detection'
|
|||
import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages'
|
||||
import {LanguageToggle} from './LanguageToggle'
|
||||
import {ConfirmLanguagesButton} from './ConfirmLanguagesButton'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useLanguagePrefs,
|
||||
useSetLanguagePrefs,
|
||||
|
@ -18,14 +18,14 @@ import {
|
|||
export const snapPoints = ['100%']
|
||||
|
||||
export function Component({}: {}) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useSetLanguagePrefs()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const onPressDone = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const languages = React.useMemo(() => {
|
||||
const langs = LANGUAGES.filter(
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react'
|
|||
import {StyleSheet, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {ScrollView} from '../util'
|
||||
import {useStores} from 'state/index'
|
||||
import {Text} from '../../util/text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
|
@ -10,6 +9,7 @@ import {deviceLocales} from 'platform/detection'
|
|||
import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages'
|
||||
import {ConfirmLanguagesButton} from './ConfirmLanguagesButton'
|
||||
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useLanguagePrefs,
|
||||
useSetLanguagePrefs,
|
||||
|
@ -20,14 +20,14 @@ import {
|
|||
export const snapPoints = ['100%']
|
||||
|
||||
export const Component = observer(function PostLanguagesSettingsImpl() {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useSetLanguagePrefs()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const onPressDone = React.useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
}, [store])
|
||||
closeModal()
|
||||
}, [closeModal])
|
||||
|
||||
const languages = React.useMemo(() => {
|
||||
const langs = LANGUAGES.filter(
|
||||
|
|
|
@ -14,6 +14,7 @@ import {SendReportButton} from './SendReportButton'
|
|||
import {InputIssueDetails} from './InputIssueDetails'
|
||||
import {ReportReasonOptions} from './ReasonOptions'
|
||||
import {CollectionId} from './types'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const DMCA_LINK = 'https://blueskyweb.xyz/support/copyright'
|
||||
|
||||
|
@ -37,6 +38,7 @@ type ReportComponentProps =
|
|||
|
||||
export function Component(content: ReportComponentProps) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
|
@ -60,7 +62,7 @@ export function Component(content: ReportComponentProps) {
|
|||
try {
|
||||
if (issue === '__copyright__') {
|
||||
Linking.openURL(DMCA_LINK)
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
return
|
||||
}
|
||||
const $type = !isAccountReport
|
||||
|
@ -76,7 +78,7 @@ export function Component(content: ReportComponentProps) {
|
|||
})
|
||||
Toast.show("Thank you for your report! We'll look into it promptly.")
|
||||
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
return
|
||||
} catch (e: any) {
|
||||
setError(cleanError(e))
|
||||
|
|
|
@ -11,6 +11,7 @@ import {useNavigation} from '@react-navigation/native'
|
|||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {useStores} from 'state/index'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const MESSAGES = {
|
||||
[KnownError.Unknown]: '',
|
||||
|
@ -57,13 +58,14 @@ function FeedgenErrorMessage({
|
|||
const msg = MESSAGES[knownError]
|
||||
const uri = (feed.params as GetCustomFeed.QueryParams).feed
|
||||
const [ownerDid] = safeParseFeedgenUri(uri)
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
||||
const onViewProfile = React.useCallback(() => {
|
||||
navigation.navigate('Profile', {name: ownerDid})
|
||||
}, [navigation, ownerDid])
|
||||
|
||||
const onRemoveFeed = React.useCallback(async () => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Remove feed',
|
||||
message: 'Remove this feed from your saved feeds?',
|
||||
|
@ -78,10 +80,10 @@ function FeedgenErrorMessage({
|
|||
}
|
||||
},
|
||||
onPressCancel() {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
},
|
||||
})
|
||||
}, [store, uri])
|
||||
}, [store, openModal, closeModal, uri])
|
||||
|
||||
return (
|
||||
<View
|
||||
|
|
|
@ -40,6 +40,7 @@ import {makeProfileLink} from 'lib/routes/links'
|
|||
import {Link} from '../util/Link'
|
||||
import {ProfileHeaderSuggestedFollows} from './ProfileHeaderSuggestedFollows'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
interface Props {
|
||||
view: ProfileModel
|
||||
|
@ -113,6 +114,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({
|
|||
const pal = usePalette('default')
|
||||
const palInverted = usePalette('inverted')
|
||||
const store = useStores()
|
||||
const {openModal} = useModalControls()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {track} = useAnalytics()
|
||||
const invalidHandle = isInvalidHandle(view.handle)
|
||||
|
@ -157,12 +159,12 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({
|
|||
|
||||
const onPressEditProfile = React.useCallback(() => {
|
||||
track('ProfileHeader:EditProfileButtonClicked')
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'edit-profile',
|
||||
profileView: view,
|
||||
onUpdate: onRefreshAll,
|
||||
})
|
||||
}, [track, store, view, onRefreshAll])
|
||||
}, [track, openModal, view, onRefreshAll])
|
||||
|
||||
const trackPress = React.useCallback(
|
||||
(f: 'Followers' | 'Follows') => {
|
||||
|
@ -181,12 +183,12 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({
|
|||
|
||||
const onPressAddRemoveLists = React.useCallback(() => {
|
||||
track('ProfileHeader:AddToListsButtonClicked')
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'user-add-remove-lists',
|
||||
subject: view.did,
|
||||
displayName: view.displayName || view.handle,
|
||||
})
|
||||
}, [track, view, store])
|
||||
}, [track, view, openModal])
|
||||
|
||||
const onPressMuteAccount = React.useCallback(async () => {
|
||||
track('ProfileHeader:MuteAccountButtonClicked')
|
||||
|
@ -212,7 +214,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({
|
|||
|
||||
const onPressBlockAccount = React.useCallback(async () => {
|
||||
track('ProfileHeader:BlockAccountButtonClicked')
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Block Account',
|
||||
message:
|
||||
|
@ -228,11 +230,11 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({
|
|||
}
|
||||
},
|
||||
})
|
||||
}, [track, view, store, onRefreshAll])
|
||||
}, [track, view, openModal, onRefreshAll])
|
||||
|
||||
const onPressUnblockAccount = React.useCallback(async () => {
|
||||
track('ProfileHeader:UnblockAccountButtonClicked')
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Unblock Account',
|
||||
message:
|
||||
|
@ -248,15 +250,15 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({
|
|||
}
|
||||
},
|
||||
})
|
||||
}, [track, view, store, onRefreshAll])
|
||||
}, [track, view, openModal, onRefreshAll])
|
||||
|
||||
const onPressReportAccount = React.useCallback(() => {
|
||||
track('ProfileHeader:ReportAccountButtonClicked')
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'report',
|
||||
did: view.did,
|
||||
})
|
||||
}, [track, store, view])
|
||||
}, [track, openModal, view])
|
||||
|
||||
const isMe = React.useMemo(
|
||||
() => store.me.did === view.did,
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import {Pressable, View} from 'react-native'
|
||||
import {useStores} from 'state/index'
|
||||
import {navigate} from '../../../Navigation'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
/**
|
||||
* This utility component is only included in the test simulator
|
||||
|
@ -13,6 +14,7 @@ const BTN = {height: 1, width: 1, backgroundColor: 'red'}
|
|||
|
||||
export function TestCtrls() {
|
||||
const store = useStores()
|
||||
const {openModal} = useModalControls()
|
||||
const onPressSignInAlice = async () => {
|
||||
await store.session.login({
|
||||
service: 'http://localhost:3000',
|
||||
|
@ -85,7 +87,7 @@ export function TestCtrls() {
|
|||
/>
|
||||
<Pressable
|
||||
testID="e2eOpenInviteCodesModal"
|
||||
onPress={() => store.shell.openModal({name: 'invite-codes'})}
|
||||
onPress={() => openModal({name: 'invite-codes'})}
|
||||
accessibilityRole="button"
|
||||
style={BTN}
|
||||
/>
|
||||
|
|
|
@ -21,7 +21,6 @@ import {Text} from './text/Text'
|
|||
import {TypographyVariant} from 'lib/ThemeContext'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {router} from '../../../routes'
|
||||
import {useStores, RootStoreModel} from 'state/index'
|
||||
import {
|
||||
convertBskyAppUrlIfNeeded,
|
||||
isExternalUrl,
|
||||
|
@ -31,6 +30,7 @@ import {isAndroid, isWeb} from 'platform/detection'
|
|||
import {sanitizeUrl} from '@braintree/sanitize-url'
|
||||
import {PressableWithHover} from './PressableWithHover'
|
||||
import FixedTouchableHighlight from '../pager/FixedTouchableHighlight'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
type Event =
|
||||
| React.MouseEvent<HTMLAnchorElement, MouseEvent>
|
||||
|
@ -60,17 +60,17 @@ export const Link = memo(function Link({
|
|||
anchorNoUnderline,
|
||||
...props
|
||||
}: Props) {
|
||||
const store = useStores()
|
||||
const {closeModal} = useModalControls()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const anchorHref = asAnchor ? sanitizeUrl(href) : undefined
|
||||
|
||||
const onPress = React.useCallback(
|
||||
(e?: Event) => {
|
||||
if (typeof href === 'string') {
|
||||
return onPressInner(store, navigation, sanitizeUrl(href), e)
|
||||
return onPressInner(closeModal, navigation, sanitizeUrl(href), e)
|
||||
}
|
||||
},
|
||||
[store, navigation, href],
|
||||
[closeModal, navigation, href],
|
||||
)
|
||||
|
||||
if (noFeedback) {
|
||||
|
@ -160,8 +160,8 @@ export const TextLink = memo(function TextLink({
|
|||
warnOnMismatchingLabel?: boolean
|
||||
} & TextProps) {
|
||||
const {...props} = useLinkProps({to: sanitizeUrl(href)})
|
||||
const store = useStores()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
||||
if (warnOnMismatchingLabel && typeof text !== 'string') {
|
||||
console.error('Unable to detect mismatching label')
|
||||
|
@ -174,7 +174,7 @@ export const TextLink = memo(function TextLink({
|
|||
linkRequiresWarning(href, typeof text === 'string' ? text : '')
|
||||
if (requiresWarning) {
|
||||
e?.preventDefault?.()
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'link-warning',
|
||||
text: typeof text === 'string' ? text : '',
|
||||
href,
|
||||
|
@ -185,9 +185,17 @@ export const TextLink = memo(function TextLink({
|
|||
// @ts-ignore function signature differs by platform -prf
|
||||
return onPress()
|
||||
}
|
||||
return onPressInner(store, navigation, sanitizeUrl(href), e)
|
||||
return onPressInner(closeModal, navigation, sanitizeUrl(href), e)
|
||||
},
|
||||
[onPress, store, navigation, href, text, warnOnMismatchingLabel],
|
||||
[
|
||||
onPress,
|
||||
closeModal,
|
||||
openModal,
|
||||
navigation,
|
||||
href,
|
||||
text,
|
||||
warnOnMismatchingLabel,
|
||||
],
|
||||
)
|
||||
const hrefAttrs = useMemo(() => {
|
||||
const isExternal = isExternalUrl(href)
|
||||
|
@ -285,7 +293,7 @@ export const TextLinkOnWebOnly = memo(function DesktopWebTextLink({
|
|||
// needed customizations
|
||||
// -prf
|
||||
function onPressInner(
|
||||
store: RootStoreModel,
|
||||
closeModal = () => {},
|
||||
navigation: NavigationProp,
|
||||
href: string,
|
||||
e?: Event,
|
||||
|
@ -318,7 +326,7 @@ function onPressInner(
|
|||
if (newTab || href.startsWith('http') || href.startsWith('mailto')) {
|
||||
Linking.openURL(href)
|
||||
} else {
|
||||
store.shell.closeModal() // close any active modals
|
||||
closeModal() // close any active modals
|
||||
|
||||
// @ts-ignore we're not able to type check on this one -prf
|
||||
navigation.dispatch(StackActions.push(...router.matchPath(href)))
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react'
|
||||
import {Pressable, StyleProp, ViewStyle} from 'react-native'
|
||||
import {useStores} from 'state/index'
|
||||
import {Link} from './Link'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
interface UserPreviewLinkProps {
|
||||
did: string
|
||||
|
@ -13,7 +13,7 @@ interface UserPreviewLinkProps {
|
|||
export function UserPreviewLink(
|
||||
props: React.PropsWithChildren<UserPreviewLinkProps>,
|
||||
) {
|
||||
const store = useStores()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
if (isWeb) {
|
||||
return (
|
||||
|
@ -29,7 +29,7 @@ export function UserPreviewLink(
|
|||
return (
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'profile-preview',
|
||||
did: props.did,
|
||||
})
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react'
|
|||
import {StyleProp, View, ViewStyle} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||
import {useStores} from 'state/index'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {shareUrl} from 'lib/sharing'
|
||||
import {
|
||||
|
@ -10,6 +9,7 @@ import {
|
|||
DropdownItem as NativeDropdownItem,
|
||||
} from './NativeDropdown'
|
||||
import {EventStopper} from '../EventStopper'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function PostDropdownBtn({
|
||||
testID,
|
||||
|
@ -37,9 +37,9 @@ export function PostDropdownBtn({
|
|||
onDeletePost: () => void
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const store = useStores()
|
||||
const theme = useTheme()
|
||||
const defaultCtrlColor = theme.palette.default.postCtrl
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const dropdownItems: NativeDropdownItem[] = [
|
||||
{
|
||||
|
@ -108,7 +108,7 @@ export function PostDropdownBtn({
|
|||
!isAuthor && {
|
||||
label: 'Report post',
|
||||
onPress() {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'report',
|
||||
uri: itemUri,
|
||||
cid: itemCid,
|
||||
|
@ -129,7 +129,7 @@ export function PostDropdownBtn({
|
|||
isAuthor && {
|
||||
label: 'Delete post',
|
||||
onPress() {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Delete this post?',
|
||||
message: 'Are you sure? This can not be undone.',
|
||||
|
|
|
@ -6,7 +6,7 @@ import {ModerationUI} from '@atproto/api'
|
|||
import {Text} from '../text/Text'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {useStores} from 'state/index'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function ContentHider({
|
||||
testID,
|
||||
|
@ -22,10 +22,10 @@ export function ContentHider({
|
|||
style?: StyleProp<ViewStyle>
|
||||
childContainerStyle?: StyleProp<ViewStyle>
|
||||
}>) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [override, setOverride] = React.useState(false)
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
if (!moderation.blur || (ignoreMute && moderation.cause?.type === 'muted')) {
|
||||
return (
|
||||
|
@ -43,7 +43,7 @@ export function ContentHider({
|
|||
if (!moderation.noOverride) {
|
||||
setOverride(v => !v)
|
||||
} else {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'moderation-details',
|
||||
context: 'content',
|
||||
moderation,
|
||||
|
@ -62,7 +62,7 @@ export function ContentHider({
|
|||
]}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'moderation-details',
|
||||
context: 'content',
|
||||
moderation,
|
||||
|
|
|
@ -5,7 +5,7 @@ import {Text} from '../text/Text'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {useStores} from 'state/index'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function PostAlerts({
|
||||
moderation,
|
||||
|
@ -15,8 +15,8 @@ export function PostAlerts({
|
|||
includeMute?: boolean
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const shouldAlert = !!moderation.cause && moderation.alert
|
||||
if (!shouldAlert) {
|
||||
|
@ -27,7 +27,7 @@ export function PostAlerts({
|
|||
return (
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'moderation-details',
|
||||
context: 'content',
|
||||
moderation,
|
||||
|
|
|
@ -8,7 +8,7 @@ import {Text} from '../text/Text'
|
|||
import {addStyle} from 'lib/styles'
|
||||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {useStores} from 'state/index'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
interface Props extends ComponentProps<typeof Link> {
|
||||
// testID?: string
|
||||
|
@ -25,10 +25,10 @@ export function PostHider({
|
|||
children,
|
||||
...props
|
||||
}: Props) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [override, setOverride] = React.useState(false)
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
if (!moderation.blur) {
|
||||
return (
|
||||
|
@ -63,7 +63,7 @@ export function PostHider({
|
|||
]}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'moderation-details',
|
||||
context: 'content',
|
||||
moderation,
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
describeModerationCause,
|
||||
getProfileModerationCauses,
|
||||
} from 'lib/moderation'
|
||||
import {useStores} from 'state/index'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function ProfileHeaderAlerts({
|
||||
moderation,
|
||||
|
@ -17,8 +17,8 @@ export function ProfileHeaderAlerts({
|
|||
moderation: ProfileModeration
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const causes = getProfileModerationCauses(moderation)
|
||||
if (!causes.length) {
|
||||
|
@ -34,7 +34,7 @@ export function ProfileHeaderAlerts({
|
|||
testID="profileHeaderAlert"
|
||||
key={desc.name}
|
||||
onPress={() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'moderation-details',
|
||||
context: 'content',
|
||||
moderation: {cause},
|
||||
|
|
|
@ -18,7 +18,7 @@ import {NavigationProp} from 'lib/routes/types'
|
|||
import {Text} from '../text/Text'
|
||||
import {Button} from '../forms/Button'
|
||||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {useStores} from 'state/index'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function ScreenHider({
|
||||
testID,
|
||||
|
@ -34,12 +34,12 @@ export function ScreenHider({
|
|||
style?: StyleProp<ViewStyle>
|
||||
containerStyle?: StyleProp<ViewStyle>
|
||||
}>) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const palInverted = usePalette('inverted')
|
||||
const [override, setOverride] = React.useState(false)
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
if (!moderation.blur || override) {
|
||||
return (
|
||||
|
@ -72,7 +72,7 @@ export function ScreenHider({
|
|||
.{' '}
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'moderation-details',
|
||||
context: 'account',
|
||||
moderation,
|
||||
|
|
|
@ -16,6 +16,7 @@ import {useStores} from 'state/index'
|
|||
import {RepostButton} from './RepostButton'
|
||||
import {Haptics} from 'lib/haptics'
|
||||
import {HITSLOP_10, HITSLOP_20} from 'lib/constants'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
interface PostCtrlsOpts {
|
||||
itemUri: string
|
||||
|
@ -51,6 +52,7 @@ interface PostCtrlsOpts {
|
|||
export function PostCtrls(opts: PostCtrlsOpts) {
|
||||
const store = useStores()
|
||||
const theme = useTheme()
|
||||
const {closeModal} = useModalControls()
|
||||
const defaultCtrlColor = React.useMemo(
|
||||
() => ({
|
||||
color: theme.palette.default.postCtrl,
|
||||
|
@ -58,17 +60,17 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
[theme],
|
||||
) as StyleProp<ViewStyle>
|
||||
const onRepost = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
if (!opts.isReposted) {
|
||||
Haptics.default()
|
||||
opts.onPressToggleRepost().catch(_e => undefined)
|
||||
} else {
|
||||
opts.onPressToggleRepost().catch(_e => undefined)
|
||||
}
|
||||
}, [opts, store.shell])
|
||||
}, [opts, closeModal])
|
||||
|
||||
const onQuote = useCallback(() => {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
store.shell.openComposer({
|
||||
quote: {
|
||||
uri: opts.itemUri,
|
||||
|
@ -86,6 +88,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
opts.itemUri,
|
||||
opts.text,
|
||||
store.shell,
|
||||
closeModal,
|
||||
])
|
||||
|
||||
const onPressToggleLikeWrapper = async () => {
|
||||
|
|
|
@ -5,8 +5,8 @@ import {s, colors} from 'lib/styles'
|
|||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {Text} from '../text/Text'
|
||||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {useStores} from 'state/index'
|
||||
import {HITSLOP_10, HITSLOP_20} from 'lib/constants'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
interface Props {
|
||||
isReposted: boolean
|
||||
|
@ -23,8 +23,8 @@ export const RepostButton = ({
|
|||
onRepost,
|
||||
onQuote,
|
||||
}: Props) => {
|
||||
const store = useStores()
|
||||
const theme = useTheme()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const defaultControlColor = React.useMemo(
|
||||
() => ({
|
||||
|
@ -34,13 +34,13 @@ export const RepostButton = ({
|
|||
)
|
||||
|
||||
const onPressToggleRepostWrapper = useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'repost',
|
||||
onRepost: onRepost,
|
||||
onQuote: onQuote,
|
||||
isReposted,
|
||||
})
|
||||
}, [onRepost, onQuote, isReposted, store.shell])
|
||||
}, [onRepost, onQuote, isReposted, openModal])
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
|
|
|
@ -17,6 +17,7 @@ import {useFocusEffect} from '@react-navigation/native'
|
|||
import {ViewHeader} from '../com/util/ViewHeader'
|
||||
import {CenteredView} from 'view/com/util/Views'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppPasswords'>
|
||||
|
@ -27,6 +28,7 @@ export const AppPasswords = withAuthRequired(
|
|||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {screen} = useAnalytics()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -36,8 +38,8 @@ export const AppPasswords = withAuthRequired(
|
|||
)
|
||||
|
||||
const onAdd = React.useCallback(async () => {
|
||||
store.shell.openModal({name: 'add-app-password'})
|
||||
}, [store])
|
||||
openModal({name: 'add-app-password'})
|
||||
}, [openModal])
|
||||
|
||||
// no app passwords (empty) state
|
||||
if (store.me.appPasswords.length === 0) {
|
||||
|
@ -162,10 +164,11 @@ function AppPassword({
|
|||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {openModal} = useModalControls()
|
||||
const {contentLanguages} = useLanguagePrefs()
|
||||
|
||||
const onDelete = React.useCallback(async () => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Delete App Password',
|
||||
message: `Are you sure you want to delete the app password "${name}"?`,
|
||||
|
@ -174,7 +177,7 @@ function AppPassword({
|
|||
Toast.show('App password deleted')
|
||||
},
|
||||
})
|
||||
}, [store, name])
|
||||
}, [store, openModal, name])
|
||||
|
||||
const primaryLocale =
|
||||
contentLanguages.length > 0 ? contentLanguages[0] : 'en-US'
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react'
|
|||
import {StyleSheet, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Text} from '../com/util/text/Text'
|
||||
import {useStores} from 'state/index'
|
||||
import {s} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
|
@ -19,6 +18,7 @@ import {useFocusEffect} from '@react-navigation/native'
|
|||
import {LANGUAGES} from 'lib/../locale/languages'
|
||||
import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useLanguagePrefs, useSetLanguagePrefs} from '#/state/preferences'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'LanguageSettings'>
|
||||
|
@ -27,12 +27,12 @@ export const LanguageSettingsScreen = observer(function LanguageSettingsImpl(
|
|||
_: Props,
|
||||
) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useSetLanguagePrefs()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const {screen, track} = useAnalytics()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -43,8 +43,8 @@ export const LanguageSettingsScreen = observer(function LanguageSettingsImpl(
|
|||
|
||||
const onPressContentLanguages = React.useCallback(() => {
|
||||
track('Settings:ContentlanguagesButtonClicked')
|
||||
store.shell.openModal({name: 'content-languages-settings'})
|
||||
}, [track, store])
|
||||
openModal({name: 'content-languages-settings'})
|
||||
}, [track, openModal])
|
||||
|
||||
const onChangePrimaryLanguage = React.useCallback(
|
||||
(value: Parameters<PickerSelectProps['onValueChange']>[0]) => {
|
||||
|
|
|
@ -17,6 +17,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|||
import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
|
||||
import {s} from 'lib/styles'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Lists'>
|
||||
export const ListsScreen = withAuthRequired(
|
||||
|
@ -26,6 +27,7 @@ export const ListsScreen = withAuthRequired(
|
|||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const listsLists: ListsListModel = React.useMemo(
|
||||
() => new ListsListModel(store, 'my-curatelists'),
|
||||
|
@ -40,7 +42,7 @@ export const ListsScreen = withAuthRequired(
|
|||
)
|
||||
|
||||
const onPressNewList = React.useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'create-or-edit-list',
|
||||
purpose: 'app.bsky.graph.defs#curatelist',
|
||||
onSave: (uri: string) => {
|
||||
|
@ -53,7 +55,7 @@ export const ListsScreen = withAuthRequired(
|
|||
} catch {}
|
||||
},
|
||||
})
|
||||
}, [store, navigation])
|
||||
}, [openModal, navigation])
|
||||
|
||||
return (
|
||||
<View style={s.hContentRegion} testID="listsScreen">
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
import {observer} from 'mobx-react-lite'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
|
||||
import {useStores} from 'state/index'
|
||||
import {s} from 'lib/styles'
|
||||
import {CenteredView} from '../com/util/Views'
|
||||
import {ViewHeader} from '../com/util/ViewHeader'
|
||||
|
@ -18,15 +17,16 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {useAnalytics} from 'lib/analytics/analytics'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>
|
||||
export const ModerationScreen = withAuthRequired(
|
||||
observer(function Moderation({}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {screen, track} = useAnalytics()
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
|
@ -37,8 +37,8 @@ export const ModerationScreen = withAuthRequired(
|
|||
|
||||
const onPressContentFiltering = React.useCallback(() => {
|
||||
track('Moderation:ContentfilteringButtonClicked')
|
||||
store.shell.openModal({name: 'content-filtering-settings'})
|
||||
}, [track, store])
|
||||
openModal({name: 'content-filtering-settings'})
|
||||
}, [track, openModal])
|
||||
|
||||
return (
|
||||
<CenteredView
|
||||
|
|
|
@ -17,6 +17,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|||
import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
|
||||
import {s} from 'lib/styles'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ModerationModlists'>
|
||||
export const ModerationModlistsScreen = withAuthRequired(
|
||||
|
@ -26,6 +27,7 @@ export const ModerationModlistsScreen = withAuthRequired(
|
|||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const mutelists: ListsListModel = React.useMemo(
|
||||
() => new ListsListModel(store, 'my-modlists'),
|
||||
|
@ -40,7 +42,7 @@ export const ModerationModlistsScreen = withAuthRequired(
|
|||
)
|
||||
|
||||
const onPressNewList = React.useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'create-or-edit-list',
|
||||
purpose: 'app.bsky.graph.defs#modlist',
|
||||
onSave: (uri: string) => {
|
||||
|
@ -53,7 +55,7 @@ export const ModerationModlistsScreen = withAuthRequired(
|
|||
} catch {}
|
||||
},
|
||||
})
|
||||
}, [store, navigation])
|
||||
}, [openModal, navigation])
|
||||
|
||||
return (
|
||||
<View style={s.hContentRegion} testID="moderationModlistsScreen">
|
||||
|
|
|
@ -47,6 +47,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
|
|||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {ComposeIcon2} from 'lib/icons'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const SECTION_TITLES = ['Posts', 'About']
|
||||
|
||||
|
@ -137,6 +138,7 @@ export const ProfileFeedScreenInner = observer(
|
|||
route,
|
||||
feedOwnerDid,
|
||||
}: Props & {feedOwnerDid: string}) {
|
||||
const {openModal} = useModalControls()
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const {track} = useAnalytics()
|
||||
|
@ -210,12 +212,12 @@ export const ProfileFeedScreenInner = observer(
|
|||
|
||||
const onPressReport = React.useCallback(() => {
|
||||
if (!feedInfo) return
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'report',
|
||||
uri: feedInfo.uri,
|
||||
cid: feedInfo.cid,
|
||||
})
|
||||
}, [store, feedInfo])
|
||||
}, [openModal, feedInfo])
|
||||
|
||||
const onCurrentPageSelected = React.useCallback(
|
||||
(index: number) => {
|
||||
|
|
|
@ -46,6 +46,7 @@ import {ComposeIcon2} from 'lib/icons'
|
|||
import {ListItems} from 'view/com/lists/ListItems'
|
||||
import {logger} from '#/logger'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const SECTION_TITLES_CURATE = ['Posts', 'About']
|
||||
const SECTION_TITLES_MOD = ['About']
|
||||
|
@ -110,6 +111,7 @@ export const ProfileListScreenInner = observer(
|
|||
const {rkey} = route.params
|
||||
const feedSectionRef = React.useRef<SectionRef>(null)
|
||||
const aboutSectionRef = React.useRef<SectionRef>(null)
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const list: ListModel = useMemo(() => {
|
||||
const model = new ListModel(
|
||||
|
@ -136,7 +138,7 @@ export const ProfileListScreenInner = observer(
|
|||
)
|
||||
|
||||
const onPressAddUser = useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'list-add-user',
|
||||
list,
|
||||
onAdd() {
|
||||
|
@ -145,7 +147,7 @@ export const ProfileListScreenInner = observer(
|
|||
}
|
||||
},
|
||||
})
|
||||
}, [store, list, feed])
|
||||
}, [openModal, list, feed])
|
||||
|
||||
const onCurrentPageSelected = React.useCallback(
|
||||
(index: number) => {
|
||||
|
@ -268,8 +270,8 @@ const Header = observer(function HeaderImpl({
|
|||
}) {
|
||||
const pal = usePalette('default')
|
||||
const palInverted = usePalette('inverted')
|
||||
const store = useStores()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {openModal, closeModal} = useModalControls()
|
||||
|
||||
const onTogglePinned = useCallback(async () => {
|
||||
Haptics.default()
|
||||
|
@ -280,7 +282,7 @@ const Header = observer(function HeaderImpl({
|
|||
}, [list])
|
||||
|
||||
const onSubscribeMute = useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Mute these accounts?',
|
||||
message:
|
||||
|
@ -297,10 +299,10 @@ const Header = observer(function HeaderImpl({
|
|||
}
|
||||
},
|
||||
onPressCancel() {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
},
|
||||
})
|
||||
}, [store, list])
|
||||
}, [openModal, closeModal, list])
|
||||
|
||||
const onUnsubscribeMute = useCallback(async () => {
|
||||
try {
|
||||
|
@ -314,7 +316,7 @@ const Header = observer(function HeaderImpl({
|
|||
}, [list])
|
||||
|
||||
const onSubscribeBlock = useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Block these accounts?',
|
||||
message:
|
||||
|
@ -331,10 +333,10 @@ const Header = observer(function HeaderImpl({
|
|||
}
|
||||
},
|
||||
onPressCancel() {
|
||||
store.shell.closeModal()
|
||||
closeModal()
|
||||
},
|
||||
})
|
||||
}, [store, list])
|
||||
}, [openModal, closeModal, list])
|
||||
|
||||
const onUnsubscribeBlock = useCallback(async () => {
|
||||
try {
|
||||
|
@ -348,17 +350,17 @@ const Header = observer(function HeaderImpl({
|
|||
}, [list])
|
||||
|
||||
const onPressEdit = useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'create-or-edit-list',
|
||||
list,
|
||||
onSave() {
|
||||
list.refresh()
|
||||
},
|
||||
})
|
||||
}, [store, list])
|
||||
}, [openModal, list])
|
||||
|
||||
const onPressDelete = useCallback(() => {
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'confirm',
|
||||
title: 'Delete List',
|
||||
message: 'Are you sure?',
|
||||
|
@ -372,16 +374,16 @@ const Header = observer(function HeaderImpl({
|
|||
}
|
||||
},
|
||||
})
|
||||
}, [store, list, navigation])
|
||||
}, [openModal, list, navigation])
|
||||
|
||||
const onPressReport = useCallback(() => {
|
||||
if (!list.data) return
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'report',
|
||||
uri: list.uri,
|
||||
cid: list.data.cid,
|
||||
})
|
||||
}, [store, list])
|
||||
}, [openModal, list])
|
||||
|
||||
const onPressShare = useCallback(() => {
|
||||
const url = toShareUrl(`/profile/${list.creatorDid}/lists/${rkey}`)
|
||||
|
|
|
@ -46,6 +46,7 @@ import Clipboard from '@react-native-clipboard/clipboard'
|
|||
import {makeProfileLink} from 'lib/routes/links'
|
||||
import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn'
|
||||
import {logger} from '#/logger'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useSetMinimalShellMode,
|
||||
useColorMode,
|
||||
|
@ -82,6 +83,7 @@ export const SettingsScreen = withAuthRequired(
|
|||
const [debugHeaderEnabled, toggleDebugHeader] = useDebugHeaderSetting(
|
||||
store.agent,
|
||||
)
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const primaryBg = useCustomPalette<ViewStyle>({
|
||||
light: {backgroundColor: colors.blue0},
|
||||
|
@ -117,7 +119,7 @@ export const SettingsScreen = withAuthRequired(
|
|||
|
||||
const onPressChangeHandle = React.useCallback(() => {
|
||||
track('Settings:ChangeHandleButtonClicked')
|
||||
store.shell.openModal({
|
||||
openModal({
|
||||
name: 'change-handle',
|
||||
onChanged() {
|
||||
setIsSwitching(true)
|
||||
|
@ -135,12 +137,12 @@ export const SettingsScreen = withAuthRequired(
|
|||
)
|
||||
},
|
||||
})
|
||||
}, [track, store, setIsSwitching])
|
||||
}, [track, store, openModal, setIsSwitching])
|
||||
|
||||
const onPressInviteCodes = React.useCallback(() => {
|
||||
track('Settings:InvitecodesButtonClicked')
|
||||
store.shell.openModal({name: 'invite-codes'})
|
||||
}, [track, store])
|
||||
openModal({name: 'invite-codes'})
|
||||
}, [track, openModal])
|
||||
|
||||
const onPressLanguageSettings = React.useCallback(() => {
|
||||
navigation.navigate('LanguageSettings')
|
||||
|
@ -152,8 +154,8 @@ export const SettingsScreen = withAuthRequired(
|
|||
}, [track, store])
|
||||
|
||||
const onPressDeleteAccount = React.useCallback(() => {
|
||||
store.shell.openModal({name: 'delete-account'})
|
||||
}, [store])
|
||||
openModal({name: 'delete-account'})
|
||||
}, [openModal])
|
||||
|
||||
const onPressResetPreferences = React.useCallback(async () => {
|
||||
await store.preferences.reset()
|
||||
|
@ -229,8 +231,7 @@ export const SettingsScreen = withAuthRequired(
|
|||
<Text type="lg" style={pal.text}>
|
||||
{store.session.currentSession?.email}{' '}
|
||||
</Text>
|
||||
<Link
|
||||
onPress={() => store.shell.openModal({name: 'change-email'})}>
|
||||
<Link onPress={() => openModal({name: 'change-email'})}>
|
||||
<Text type="lg" style={pal.link}>
|
||||
Change
|
||||
</Text>
|
||||
|
@ -240,10 +241,7 @@ export const SettingsScreen = withAuthRequired(
|
|||
<Text type="lg-medium" style={pal.text}>
|
||||
Birthday:{' '}
|
||||
</Text>
|
||||
<Link
|
||||
onPress={() =>
|
||||
store.shell.openModal({name: 'birth-date-settings'})
|
||||
}>
|
||||
<Link onPress={() => openModal({name: 'birth-date-settings'})}>
|
||||
<Text type="lg" style={pal.link}>
|
||||
Show
|
||||
</Text>
|
||||
|
@ -649,6 +647,7 @@ const EmailConfirmationNotice = observer(
|
|||
const palInverted = usePalette('inverted')
|
||||
const store = useStores()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
if (!store.session.emailNeedsConfirmation) {
|
||||
return null
|
||||
|
@ -684,7 +683,7 @@ const EmailConfirmationNotice = observer(
|
|||
accessibilityRole="button"
|
||||
accessibilityLabel="Verify my email"
|
||||
accessibilityHint=""
|
||||
onPress={() => store.shell.openModal({name: 'verify-email'})}>
|
||||
onPress={() => openModal({name: 'verify-email'})}>
|
||||
<FontAwesomeIcon
|
||||
icon="envelope"
|
||||
color={palInverted.colors.text}
|
||||
|
|
|
@ -44,6 +44,7 @@ import {useNavigationTabState} from 'lib/hooks/useNavigationTabState'
|
|||
import {isWeb} from 'platform/detection'
|
||||
import {formatCount, formatCountShortOnly} from 'view/com/util/numeric/format'
|
||||
import {useSetDrawerOpen} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const DrawerContent = observer(function DrawerContentImpl() {
|
||||
const theme = useTheme()
|
||||
|
@ -442,11 +443,12 @@ const InviteCodes = observer(function InviteCodesImpl({
|
|||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const pal = usePalette('default')
|
||||
const {invitesAvailable} = store.me
|
||||
const {openModal} = useModalControls()
|
||||
const onPress = React.useCallback(() => {
|
||||
track('Menu:ItemClicked', {url: '#invite-codes'})
|
||||
setDrawerOpen(false)
|
||||
store.shell.openModal({name: 'invite-codes'})
|
||||
}, [store, track, setDrawerOpen])
|
||||
openModal({name: 'invite-codes'})
|
||||
}, [openModal, track, setDrawerOpen])
|
||||
return (
|
||||
<TouchableOpacity
|
||||
testID="menuItemInviteCodes"
|
||||
|
|
|
@ -24,12 +24,14 @@ import {styles} from './BottomBarStyles'
|
|||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||
import {useNavigationTabState} from 'lib/hooks/useNavigationTabState'
|
||||
import {UserAvatar} from 'view/com/util/UserAvatar'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds'
|
||||
|
||||
export const BottomBar = observer(function BottomBarImpl({
|
||||
navigation,
|
||||
}: BottomTabBarProps) {
|
||||
const {openModal} = useModalControls()
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const safeAreaInsets = useSafeAreaInsets()
|
||||
|
@ -72,8 +74,8 @@ export const BottomBar = observer(function BottomBarImpl({
|
|||
onPressTab('MyProfile')
|
||||
}, [onPressTab])
|
||||
const onLongPressProfile = React.useCallback(() => {
|
||||
store.shell.openModal({name: 'switch-account'})
|
||||
}, [store])
|
||||
openModal({name: 'switch-account'})
|
||||
}, [openModal])
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
|
|
|
@ -13,6 +13,7 @@ import {useStores} from 'state/index'
|
|||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {formatCount} from 'view/com/util/numeric/format'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const DesktopRightNav = observer(function DesktopRightNavImpl() {
|
||||
const store = useStores()
|
||||
|
@ -83,12 +84,13 @@ export const DesktopRightNav = observer(function DesktopRightNavImpl() {
|
|||
const InviteCodes = observer(function InviteCodesImpl() {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const {invitesAvailable} = store.me
|
||||
|
||||
const onPress = React.useCallback(() => {
|
||||
store.shell.openModal({name: 'invite-codes'})
|
||||
}, [store])
|
||||
openModal({name: 'invite-codes'})
|
||||
}, [openModal])
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.inviteCodes, pal.border]}
|
||||
|
|
|
@ -32,12 +32,14 @@ import {
|
|||
useIsDrawerSwipeDisabled,
|
||||
} from '#/state/shell'
|
||||
import {isAndroid} from 'platform/detection'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const ShellInner = observer(function ShellInnerImpl() {
|
||||
const store = useStores()
|
||||
const isDrawerOpen = useIsDrawerOpen()
|
||||
const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled()
|
||||
const setIsDrawerOpen = useSetDrawerOpen()
|
||||
const {closeModal} = useModalControls()
|
||||
useOTAUpdate() // this hook polls for OTA updates every few seconds
|
||||
const winDim = useWindowDimensions()
|
||||
const safeAreaInsets = useSafeAreaInsets()
|
||||
|
@ -60,13 +62,14 @@ const ShellInner = observer(function ShellInnerImpl() {
|
|||
if (isAndroid) {
|
||||
listener = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
setIsDrawerOpen(false)
|
||||
closeModal()
|
||||
return store.shell.closeAnyActiveElement()
|
||||
})
|
||||
}
|
||||
return () => {
|
||||
listener.remove()
|
||||
}
|
||||
}, [store, setIsDrawerOpen])
|
||||
}, [store, setIsDrawerOpen, closeModal])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -22,11 +22,13 @@ import {
|
|||
useSetDrawerOpen,
|
||||
useOnboardingState,
|
||||
} from '#/state/shell'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
const ShellInner = observer(function ShellInnerImpl() {
|
||||
const store = useStores()
|
||||
const isDrawerOpen = useIsDrawerOpen()
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const {closeModal} = useModalControls()
|
||||
const onboardingState = useOnboardingState()
|
||||
const {isDesktop, isMobile} = useWebMediaQueries()
|
||||
const navigator = useNavigation<NavigationProp>()
|
||||
|
@ -35,9 +37,10 @@ const ShellInner = observer(function ShellInnerImpl() {
|
|||
useEffect(() => {
|
||||
navigator.addListener('state', () => {
|
||||
setDrawerOpen(false)
|
||||
closeModal()
|
||||
store.shell.closeAnyActiveElement()
|
||||
})
|
||||
}, [navigator, store.shell, setDrawerOpen])
|
||||
}, [navigator, store.shell, setDrawerOpen, closeModal])
|
||||
|
||||
const showBottomBar = isMobile && !onboardingState.isActive
|
||||
const showSideNavs =
|
||||
|
|
Loading…
Reference in New Issue