[APP-737] Accessible native dropdown menu (#988)
* fix comments * add zeego package * get basic native dropdown working * add separator and icon components * refined native dropdown component * add android build properties to app.json * move `PostDropdownBtn` to its own component * fix selectors issue * move `PostDropdownBtn` to its own component * fix hitslop * fix post dropdown hitslop * fix android dropdown icons * move `UserAvatar.tsx` to native dropdown * use native dropdown in `ProfileHeader.tsx` * use native dropdown in `PostThreadItem.tsx` * use native dropdown in `UserBanner.tsx` * use native dropdown in `CustomFeed.tsx` * replace `testId` with `testID` (which is what is used everywhere) * move `Settings.tsx` to use native dropdown * create jest mocks for zeego * create jest mock for `zeego/dropdown-menu` * web styles for native dropdown * remove example native dropdown * adjust web styles * fix propagation * fix pressable in `Settings.tsx` * animate dropdown on web * add keyboard nav and hover styles * add hitslop to constants * add comments to NativeDropdown component * temporarily removed android icons * add testID to PostDropdownBtn * add testID back to all NativeDropdown button implementations * add postDropdownBtn testID * add testID to dropdown items * remove testID from dropdown menu item * refactor home-screen tests for native dropdown * refactor profile-screen tests for native dropdown * refactor thread-muting tests for native dropdown * refactor thread-screen tests for native dropdown * fix dropdown color for post dropdown button * remove icons from android dropdown menu * fix `create-account.test.ts` * fix `invite-codes.test.ts`zio/stable
parent
eec300d772
commit
3b8b562268
|
@ -25,6 +25,8 @@ describe('Create account', () => {
|
||||||
await element(by.id('handleInput')).typeText('e2e-test')
|
await element(by.id('handleInput')).typeText('e2e-test')
|
||||||
await device.takeScreenshot('4- entered handle')
|
await device.takeScreenshot('4- entered handle')
|
||||||
await element(by.id('nextBtn')).tap()
|
await element(by.id('nextBtn')).tap()
|
||||||
|
await expect(element(by.id('welcomeScreen'))).toBeVisible()
|
||||||
|
await element(by.id('continueBtn')).tap()
|
||||||
await expect(element(by.id('homeScreen'))).toBeVisible()
|
await expect(element(by.id('homeScreen'))).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -55,7 +55,7 @@ describe('Home screen', () => {
|
||||||
await element(by.id('postDropdownBtn').withAncestor(carlaPosts))
|
await element(by.id('postDropdownBtn').withAncestor(carlaPosts))
|
||||||
.atIndex(0)
|
.atIndex(0)
|
||||||
.tap()
|
.tap()
|
||||||
await element(by.id('postDropdownReportBtn')).tap()
|
await element(by.text('Report post')).tap()
|
||||||
await expect(element(by.id('reportPostModal'))).toBeVisible()
|
await expect(element(by.id('reportPostModal'))).toBeVisible()
|
||||||
await element(
|
await element(
|
||||||
by.id('reportPostRadios-com.atproto.moderation.defs#reasonSpam'),
|
by.id('reportPostRadios-com.atproto.moderation.defs#reasonSpam'),
|
||||||
|
@ -84,7 +84,7 @@ describe('Home screen', () => {
|
||||||
await element(by.id('postDropdownBtn').withAncestor(alicePosts))
|
await element(by.id('postDropdownBtn').withAncestor(alicePosts))
|
||||||
.atIndex(0)
|
.atIndex(0)
|
||||||
.tap()
|
.tap()
|
||||||
await element(by.id('postDropdownDeleteBtn')).tap()
|
await element(by.text('Delete post')).tap()
|
||||||
await expect(element(by.id('confirmModal'))).toBeVisible()
|
await expect(element(by.id('confirmModal'))).toBeVisible()
|
||||||
await element(by.id('confirmBtn')).tap()
|
await element(by.id('confirmBtn')).tap()
|
||||||
await expect(
|
await expect(
|
||||||
|
|
|
@ -42,6 +42,8 @@ describe('invite-codes', () => {
|
||||||
await element(by.id('handleInput')).typeText('e2e-test')
|
await element(by.id('handleInput')).typeText('e2e-test')
|
||||||
await device.takeScreenshot('4- entered handle')
|
await device.takeScreenshot('4- entered handle')
|
||||||
await element(by.id('nextBtn')).tap()
|
await element(by.id('nextBtn')).tap()
|
||||||
|
await expect(element(by.id('welcomeScreen'))).toBeVisible()
|
||||||
|
await element(by.id('continueBtn')).tap()
|
||||||
await expect(element(by.id('homeScreen'))).toBeVisible()
|
await expect(element(by.id('homeScreen'))).toBeVisible()
|
||||||
await element(by.id('viewHeaderDrawerBtn')).tap()
|
await element(by.id('viewHeaderDrawerBtn')).tap()
|
||||||
await element(by.id('menuItemButton-Settings')).tap()
|
await element(by.id('menuItemButton-Settings')).tap()
|
||||||
|
|
|
@ -62,10 +62,10 @@ describe('Profile screen', () => {
|
||||||
await element(by.id('profileHeaderEditProfileButton')).tap()
|
await element(by.id('profileHeaderEditProfileButton')).tap()
|
||||||
await expect(element(by.id('editProfileModal'))).toBeVisible()
|
await expect(element(by.id('editProfileModal'))).toBeVisible()
|
||||||
await element(by.id('changeBannerBtn')).tap()
|
await element(by.id('changeBannerBtn')).tap()
|
||||||
await element(by.id('changeBannerLibraryBtn')).tap()
|
await element(by.text('Library')).tap()
|
||||||
await sleep(3e3)
|
await sleep(3e3)
|
||||||
await element(by.id('changeAvatarBtn')).tap()
|
await element(by.id('changeAvatarBtn')).tap()
|
||||||
await element(by.id('changeAvatarLibraryBtn')).tap()
|
await element(by.text('Library')).tap()
|
||||||
await sleep(3e3)
|
await sleep(3e3)
|
||||||
await element(by.id('editProfileSaveBtn')).tap()
|
await element(by.id('editProfileSaveBtn')).tap()
|
||||||
await expect(element(by.id('editProfileModal'))).not.toBeVisible()
|
await expect(element(by.id('editProfileModal'))).not.toBeVisible()
|
||||||
|
@ -79,9 +79,9 @@ describe('Profile screen', () => {
|
||||||
await element(by.id('profileHeaderEditProfileButton')).tap()
|
await element(by.id('profileHeaderEditProfileButton')).tap()
|
||||||
await expect(element(by.id('editProfileModal'))).toBeVisible()
|
await expect(element(by.id('editProfileModal'))).toBeVisible()
|
||||||
await element(by.id('changeBannerBtn')).tap()
|
await element(by.id('changeBannerBtn')).tap()
|
||||||
await element(by.id('changeBannerRemoveBtn')).tap()
|
await element(by.text('Remove')).tap()
|
||||||
await element(by.id('changeAvatarBtn')).tap()
|
await element(by.id('changeAvatarBtn')).tap()
|
||||||
await element(by.id('changeAvatarRemoveBtn')).tap()
|
await element(by.text('Remove')).tap()
|
||||||
await element(by.id('editProfileSaveBtn')).tap()
|
await element(by.id('editProfileSaveBtn')).tap()
|
||||||
await expect(element(by.id('editProfileModal'))).not.toBeVisible()
|
await expect(element(by.id('editProfileModal'))).not.toBeVisible()
|
||||||
await expect(element(by.id('userBannerFallback'))).toExist()
|
await expect(element(by.id('userBannerFallback'))).toExist()
|
||||||
|
@ -109,16 +109,16 @@ describe('Profile screen', () => {
|
||||||
it('Can mute/unmute another user', async () => {
|
it('Can mute/unmute another user', async () => {
|
||||||
await expect(element(by.id('profileHeaderMutedNotice'))).not.toExist()
|
await expect(element(by.id('profileHeaderMutedNotice'))).not.toExist()
|
||||||
await element(by.id('profileHeaderDropdownBtn')).tap()
|
await element(by.id('profileHeaderDropdownBtn')).tap()
|
||||||
await element(by.id('profileHeaderDropdownMuteBtn')).tap()
|
await element(by.text('Mute Account')).tap()
|
||||||
await expect(element(by.id('profileHeaderMutedNotice'))).toBeVisible()
|
await expect(element(by.id('profileHeaderMutedNotice'))).toBeVisible()
|
||||||
await element(by.id('profileHeaderDropdownBtn')).tap()
|
await element(by.id('profileHeaderDropdownBtn')).tap()
|
||||||
await element(by.id('profileHeaderDropdownMuteBtn')).tap()
|
await element(by.text('Unmute Account')).tap()
|
||||||
await expect(element(by.id('profileHeaderMutedNotice'))).not.toExist()
|
await expect(element(by.id('profileHeaderMutedNotice'))).not.toExist()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Can report another user', async () => {
|
it('Can report another user', async () => {
|
||||||
await element(by.id('profileHeaderDropdownBtn')).tap()
|
await element(by.id('profileHeaderDropdownBtn')).tap()
|
||||||
await element(by.id('profileHeaderDropdownReportBtn')).tap()
|
await element(by.text('Report Account')).tap()
|
||||||
await expect(element(by.id('reportAccountModal'))).toBeVisible()
|
await expect(element(by.id('reportAccountModal'))).toBeVisible()
|
||||||
await element(
|
await element(
|
||||||
by.id('reportAccountRadios-com.atproto.moderation.defs#reasonSpam'),
|
by.id('reportAccountRadios-com.atproto.moderation.defs#reasonSpam'),
|
||||||
|
@ -166,7 +166,7 @@ describe('Profile screen', () => {
|
||||||
it('Can report posts', async () => {
|
it('Can report posts', async () => {
|
||||||
const posts = by.id('feedItem-by-bob.test')
|
const posts = by.id('feedItem-by-bob.test')
|
||||||
await element(by.id('postDropdownBtn').withAncestor(posts)).atIndex(0).tap()
|
await element(by.id('postDropdownBtn').withAncestor(posts)).atIndex(0).tap()
|
||||||
await element(by.id('postDropdownReportBtn')).tap()
|
await element(by.text('Report post')).tap()
|
||||||
await expect(element(by.id('reportPostModal'))).toBeVisible()
|
await expect(element(by.id('reportPostModal'))).toBeVisible()
|
||||||
await element(
|
await element(
|
||||||
by.id('reportPostRadios-com.atproto.moderation.defs#reasonSpam'),
|
by.id('reportPostRadios-com.atproto.moderation.defs#reasonSpam'),
|
||||||
|
|
|
@ -45,7 +45,7 @@ describe('Thread muting', () => {
|
||||||
await element(by.id('postDropdownBtn').withAncestor(bobNotifs))
|
await element(by.id('postDropdownBtn').withAncestor(bobNotifs))
|
||||||
.atIndex(0)
|
.atIndex(0)
|
||||||
.tap()
|
.tap()
|
||||||
await element(by.id('postDropdownMuteThreadBtn')).tap()
|
await element(by.text('Mute thread')).tap()
|
||||||
// have to wait for the toast to clear
|
// have to wait for the toast to clear
|
||||||
await waitFor(element(by.id('viewHeaderDrawerBtn')))
|
await waitFor(element(by.id('viewHeaderDrawerBtn')))
|
||||||
.toBeVisible()
|
.toBeVisible()
|
||||||
|
@ -93,7 +93,7 @@ describe('Thread muting', () => {
|
||||||
await element(by.id('postDropdownBtn').withAncestor(alicePosts))
|
await element(by.id('postDropdownBtn').withAncestor(alicePosts))
|
||||||
.atIndex(0)
|
.atIndex(0)
|
||||||
.tap()
|
.tap()
|
||||||
await element(by.id('postDropdownMuteThreadBtn')).tap()
|
await element(by.text('Mute thread')).tap()
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// the swipe down to trigger PTR isnt working and I dont want to block on this
|
// the swipe down to trigger PTR isnt working and I dont want to block on this
|
||||||
|
|
|
@ -104,7 +104,7 @@ describe('Thread screen', () => {
|
||||||
it('Can report the root post', async () => {
|
it('Can report the root post', async () => {
|
||||||
const post = by.id('postThreadItem-by-bob.test')
|
const post = by.id('postThreadItem-by-bob.test')
|
||||||
await element(by.id('postDropdownBtn').withAncestor(post)).atIndex(0).tap()
|
await element(by.id('postDropdownBtn').withAncestor(post)).atIndex(0).tap()
|
||||||
await element(by.id('postDropdownReportBtn')).tap()
|
await element(by.text('Report post')).tap()
|
||||||
await expect(element(by.id('reportPostModal'))).toBeVisible()
|
await expect(element(by.id('reportPostModal'))).toBeVisible()
|
||||||
await element(
|
await element(
|
||||||
by.id('reportPostRadios-com.atproto.moderation.defs#reasonSpam'),
|
by.id('reportPostRadios-com.atproto.moderation.defs#reasonSpam'),
|
||||||
|
@ -116,7 +116,7 @@ describe('Thread screen', () => {
|
||||||
it('Can report a reply post', async () => {
|
it('Can report a reply post', async () => {
|
||||||
const post = by.id('postThreadItem-by-carla.test')
|
const post = by.id('postThreadItem-by-carla.test')
|
||||||
await element(by.id('postDropdownBtn').withAncestor(post)).atIndex(0).tap()
|
await element(by.id('postDropdownBtn').withAncestor(post)).atIndex(0).tap()
|
||||||
await element(by.id('postDropdownReportBtn')).tap()
|
await element(by.text('Report post')).tap()
|
||||||
await expect(element(by.id('reportPostModal'))).toBeVisible()
|
await expect(element(by.id('reportPostModal'))).toBeVisible()
|
||||||
await element(
|
await element(
|
||||||
by.id('reportPostRadios-com.atproto.moderation.defs#reasonSpam'),
|
by.id('reportPostRadios-com.atproto.moderation.defs#reasonSpam'),
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const DropdownMenu = jest.fn().mockImplementation(() => {})
|
||||||
|
export const create = jest.fn().mockImplementation(() => {})
|
2
app.json
2
app.json
|
@ -80,6 +80,8 @@
|
||||||
{
|
{
|
||||||
"android": {
|
"android": {
|
||||||
"compileSdkVersion": 34,
|
"compileSdkVersion": 34,
|
||||||
|
"targetSdkVersion": 34,
|
||||||
|
"buildToolsVersion": "34.0.0",
|
||||||
"kotlinVersion": "1.8.0"
|
"kotlinVersion": "1.8.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
"@react-native-clipboard/clipboard": "^1.10.0",
|
"@react-native-clipboard/clipboard": "^1.10.0",
|
||||||
"@react-native-community/blur": "^4.3.0",
|
"@react-native-community/blur": "^4.3.0",
|
||||||
"@react-native-community/datetimepicker": "6.7.3",
|
"@react-native-community/datetimepicker": "6.7.3",
|
||||||
|
"@react-native-menu/menu": "^0.8.0",
|
||||||
"@react-navigation/bottom-tabs": "^6.5.7",
|
"@react-navigation/bottom-tabs": "^6.5.7",
|
||||||
"@react-navigation/drawer": "^6.6.2",
|
"@react-navigation/drawer": "^6.6.2",
|
||||||
"@react-navigation/native": "^6.1.6",
|
"@react-navigation/native": "^6.1.6",
|
||||||
|
@ -120,6 +121,7 @@
|
||||||
"react-native-haptic-feedback": "^1.14.0",
|
"react-native-haptic-feedback": "^1.14.0",
|
||||||
"react-native-image-crop-picker": "^0.38.1",
|
"react-native-image-crop-picker": "^0.38.1",
|
||||||
"react-native-inappbrowser-reborn": "^3.6.3",
|
"react-native-inappbrowser-reborn": "^3.6.3",
|
||||||
|
"react-native-ios-context-menu": "^1.15.3",
|
||||||
"react-native-linear-gradient": "^2.6.2",
|
"react-native-linear-gradient": "^2.6.2",
|
||||||
"react-native-pager-view": "6.1.4",
|
"react-native-pager-view": "6.1.4",
|
||||||
"react-native-progress": "bluesky-social/react-native-progress",
|
"react-native-progress": "bluesky-social/react-native-progress",
|
||||||
|
@ -139,6 +141,7 @@
|
||||||
"sentry-expo": "~6.1.0",
|
"sentry-expo": "~6.1.0",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"tlds": "^1.234.0",
|
"tlds": "^1.234.0",
|
||||||
|
"zeego": "^1.6.2",
|
||||||
"zod": "^3.20.2"
|
"zod": "^3.20.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import {Insets} from 'react-native'
|
||||||
|
|
||||||
const HELP_DESK_LANG = 'en-us'
|
const HELP_DESK_LANG = 'en-us'
|
||||||
export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}`
|
export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}`
|
||||||
|
|
||||||
|
@ -134,3 +136,15 @@ export function LINK_META_PROXY(serviceUrl: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STATUS_PAGE_URL = 'https://status.bsky.app/'
|
export const STATUS_PAGE_URL = 'https://status.bsky.app/'
|
||||||
|
|
||||||
|
// Hitslop constants
|
||||||
|
export const createHitslop = (size: number): Insets => ({
|
||||||
|
top: size,
|
||||||
|
left: size,
|
||||||
|
bottom: size,
|
||||||
|
right: size,
|
||||||
|
})
|
||||||
|
export const HITSLOP_10 = createHitslop(10)
|
||||||
|
export const HITSLOP_20 = createHitslop(20)
|
||||||
|
export const HITSLOP_30 = createHitslop(30)
|
||||||
|
export const BACK_HITSLOP = HITSLOP_30
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const Welcome = ({next}: {next: () => void}) => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container]}>
|
<View style={[styles.container]}>
|
||||||
<View>
|
<View testID="welcomeScreen">
|
||||||
<Text style={[pal.text, styles.title]}>Welcome to </Text>
|
<Text style={[pal.text, styles.title]}>Welcome to </Text>
|
||||||
<Text style={[pal.text, pal.link, styles.title]}>Bluesky</Text>
|
<Text style={[pal.text, pal.link, styles.title]}>Bluesky</Text>
|
||||||
|
|
||||||
|
@ -52,7 +52,12 @@ export const Welcome = ({next}: {next: () => void}) => {
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Button onPress={next} label="Continue" labelStyle={styles.buttonText} />
|
<Button
|
||||||
|
onPress={next}
|
||||||
|
label="Continue"
|
||||||
|
testID="continueBtn"
|
||||||
|
labelStyle={styles.buttonText}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,9 @@ import {useStores} from 'state/index'
|
||||||
import {isDesktopWeb} from 'platform/detection'
|
import {isDesktopWeb} from 'platform/detection'
|
||||||
import {openCamera} from 'lib/media/picker'
|
import {openCamera} from 'lib/media/picker'
|
||||||
import {useCameraPermission} from 'lib/hooks/usePermissions'
|
import {useCameraPermission} from 'lib/hooks/usePermissions'
|
||||||
import {POST_IMG_MAX} from 'lib/constants'
|
import {HITSLOP_10, POST_IMG_MAX} from 'lib/constants'
|
||||||
import {GalleryModel} from 'state/models/media/gallery'
|
import {GalleryModel} from 'state/models/media/gallery'
|
||||||
|
|
||||||
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
gallery: GalleryModel
|
gallery: GalleryModel
|
||||||
}
|
}
|
||||||
|
@ -54,7 +52,7 @@ export function OpenCameraBtn({gallery}: Props) {
|
||||||
testID="openCameraButton"
|
testID="openCameraButton"
|
||||||
onPress={onPressTakePicture}
|
onPress={onPressTakePicture}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP_10}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel="Camera"
|
accessibilityLabel="Camera"
|
||||||
accessibilityHint="Opens camera on device">
|
accessibilityHint="Opens camera on device">
|
||||||
|
|
|
@ -9,8 +9,7 @@ import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
import {isDesktopWeb} from 'platform/detection'
|
import {isDesktopWeb} from 'platform/detection'
|
||||||
import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions'
|
import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions'
|
||||||
import {GalleryModel} from 'state/models/media/gallery'
|
import {GalleryModel} from 'state/models/media/gallery'
|
||||||
|
import {HITSLOP_10} from 'lib/constants'
|
||||||
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
gallery: GalleryModel
|
gallery: GalleryModel
|
||||||
|
@ -36,7 +35,7 @@ export function SelectPhotoBtn({gallery}: Props) {
|
||||||
testID="openGalleryBtn"
|
testID="openGalleryBtn"
|
||||||
onPress={onPressSelectPhotos}
|
onPress={onPressSelectPhotos}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP_10}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel="Gallery"
|
accessibilityLabel="Gallery"
|
||||||
accessibilityHint="Opens device photo gallery">
|
accessibilityHint="Opens device photo gallery">
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {createHitslop} from 'lib/constants'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {SafeAreaView, Text, TouchableOpacity, StyleSheet} from 'react-native'
|
import {SafeAreaView, Text, TouchableOpacity, StyleSheet} from 'react-native'
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ type Props = {
|
||||||
onRequestClose: () => void
|
onRequestClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const HIT_SLOP = {top: 16, left: 16, bottom: 16, right: 16}
|
const HIT_SLOP = createHitslop(16)
|
||||||
|
|
||||||
const ImageDefaultHeader = ({onRequestClose}: Props) => (
|
const ImageDefaultHeader = ({onRequestClose}: Props) => (
|
||||||
<SafeAreaView style={styles.root}>
|
<SafeAreaView style={styles.root}>
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {Text} from '../util/text/Text'
|
||||||
import {CogIcon} from 'lib/icons'
|
import {CogIcon} from 'lib/icons'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
|
import {HITSLOP_10} from 'lib/constants'
|
||||||
|
|
||||||
export const FeedsTabBar = observer(
|
export const FeedsTabBar = observer(
|
||||||
(
|
(
|
||||||
|
@ -54,7 +55,7 @@ export const FeedsTabBar = observer(
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel="Open navigation"
|
accessibilityLabel="Open navigation"
|
||||||
accessibilityHint="Access profile and other navigation links"
|
accessibilityHint="Access profile and other navigation links"
|
||||||
hitSlop={10}>
|
hitSlop={HITSLOP_10}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="bars"
|
icon="bars"
|
||||||
size={18}
|
size={18}
|
||||||
|
@ -68,7 +69,7 @@ export const FeedsTabBar = observer(
|
||||||
<View style={[pal.view]}>
|
<View style={[pal.view]}>
|
||||||
<Link
|
<Link
|
||||||
href="/settings/saved-feeds"
|
href="/settings/saved-feeds"
|
||||||
hitSlop={10}
|
hitSlop={HITSLOP_10}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel="Edit Saved Feeds"
|
accessibilityLabel="Edit Saved Feeds"
|
||||||
accessibilityHint="Opens screen to edit Saved Feeds">
|
accessibilityHint="Opens screen to edit Saved Feeds">
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {PostThreadItemModel} from 'state/models/content/post-thread-item'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {RichText} from '../util/text/RichText'
|
import {RichText} from '../util/text/RichText'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {PostDropdownBtn} from '../util/forms/DropdownButton'
|
import {PostDropdownBtn} from '../util/forms/PostDropdownBtn'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {PreviewableUserAvatar} from '../util/UserAvatar'
|
import {PreviewableUserAvatar} from '../util/UserAvatar'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
|
@ -202,7 +202,6 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
<View style={s.flex1} />
|
<View style={s.flex1} />
|
||||||
<PostDropdownBtn
|
<PostDropdownBtn
|
||||||
testID="postDropdownBtn"
|
testID="postDropdownBtn"
|
||||||
style={[styles.metaItem, s.mt2, s.px5]}
|
|
||||||
itemUri={itemUri}
|
itemUri={itemUri}
|
||||||
itemCid={itemCid}
|
itemCid={itemCid}
|
||||||
itemHref={itemHref}
|
itemHref={itemHref}
|
||||||
|
@ -212,13 +211,8 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
onCopyPostText={onCopyPostText}
|
onCopyPostText={onCopyPostText}
|
||||||
onOpenTranslate={onOpenTranslate}
|
onOpenTranslate={onOpenTranslate}
|
||||||
onToggleThreadMute={onToggleThreadMute}
|
onToggleThreadMute={onToggleThreadMute}
|
||||||
onDeletePost={onDeletePost}>
|
onDeletePost={onDeletePost}
|
||||||
<FontAwesomeIcon
|
|
||||||
icon="ellipsis-h"
|
|
||||||
size={14}
|
|
||||||
style={[pal.textLight]}
|
|
||||||
/>
|
/>
|
||||||
</PostDropdownBtn>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.meta}>
|
<View style={styles.meta}>
|
||||||
<Link
|
<Link
|
||||||
|
|
|
@ -17,7 +17,6 @@ import {toShareUrl} from 'lib/strings/url-helpers'
|
||||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||||
import {sanitizeHandle} from 'lib/strings/handles'
|
import {sanitizeHandle} from 'lib/strings/handles'
|
||||||
import {s, colors} from 'lib/styles'
|
import {s, colors} from 'lib/styles'
|
||||||
import {DropdownButton, DropdownItem} from '../util/forms/DropdownButton'
|
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
|
@ -36,11 +35,11 @@ import {FollowState} from 'state/models/cache/my-follows'
|
||||||
import {shareUrl} from 'lib/sharing'
|
import {shareUrl} from 'lib/sharing'
|
||||||
import {formatCount} from '../util/numeric/format'
|
import {formatCount} from '../util/numeric/format'
|
||||||
import {navigate} from '../../../Navigation'
|
import {navigate} from '../../../Navigation'
|
||||||
|
import {NativeDropdown, DropdownItem} from '../util/forms/NativeDropdown'
|
||||||
|
import {BACK_HITSLOP} from 'lib/constants'
|
||||||
import {isInvalidHandle} from 'lib/strings/handles'
|
import {isInvalidHandle} from 'lib/strings/handles'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
|
|
||||||
const BACK_HITSLOP = {left: 30, top: 30, right: 30, bottom: 30}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
view: ProfileModel
|
view: ProfileModel
|
||||||
onRefreshAll: () => void
|
onRefreshAll: () => void
|
||||||
|
@ -260,15 +259,29 @@ const ProfileHeaderLoaded = observer(
|
||||||
testID: 'profileHeaderDropdownShareBtn',
|
testID: 'profileHeaderDropdownShareBtn',
|
||||||
label: 'Share',
|
label: 'Share',
|
||||||
onPress: onPressShare,
|
onPress: onPressShare,
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'square.and.arrow.up',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_share',
|
||||||
|
web: 'share',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
if (!isMe) {
|
if (!isMe) {
|
||||||
items.push({sep: true})
|
items.push({label: 'separator'})
|
||||||
// Only add "Add to Lists" on other user's profiles, doesn't make sense to mute my own self!
|
// Only add "Add to Lists" on other user's profiles, doesn't make sense to mute my own self!
|
||||||
items.push({
|
items.push({
|
||||||
testID: 'profileHeaderDropdownListAddRemoveBtn',
|
testID: 'profileHeaderDropdownListAddRemoveBtn',
|
||||||
label: 'Add to Lists',
|
label: 'Add to Lists',
|
||||||
onPress: onPressAddRemoveLists,
|
onPress: onPressAddRemoveLists,
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'list.bullet',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_add',
|
||||||
|
web: 'list',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if (!view.viewer.blocking) {
|
if (!view.viewer.blocking) {
|
||||||
items.push({
|
items.push({
|
||||||
|
@ -277,6 +290,13 @@ const ProfileHeaderLoaded = observer(
|
||||||
onPress: view.viewer.muted
|
onPress: view.viewer.muted
|
||||||
? onPressUnmuteAccount
|
? onPressUnmuteAccount
|
||||||
: onPressMuteAccount,
|
: onPressMuteAccount,
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'speaker.slash',
|
||||||
|
},
|
||||||
|
android: 'ic_lock_silent_mode',
|
||||||
|
web: 'comment-slash',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
items.push({
|
items.push({
|
||||||
|
@ -285,11 +305,25 @@ const ProfileHeaderLoaded = observer(
|
||||||
onPress: view.viewer.blocking
|
onPress: view.viewer.blocking
|
||||||
? onPressUnblockAccount
|
? onPressUnblockAccount
|
||||||
: onPressBlockAccount,
|
: onPressBlockAccount,
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'person.fill.xmark',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_close_clear_cancel',
|
||||||
|
web: 'user-slash',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
items.push({
|
items.push({
|
||||||
testID: 'profileHeaderDropdownReportBtn',
|
testID: 'profileHeaderDropdownReportBtn',
|
||||||
label: 'Report Account',
|
label: 'Report Account',
|
||||||
onPress: onPressReportAccount,
|
onPress: onPressReportAccount,
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'exclamationmark.triangle',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_report_image',
|
||||||
|
web: 'circle-exclamation',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
|
@ -380,13 +414,17 @@ const ProfileHeaderLoaded = observer(
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{dropdownItems?.length ? (
|
{dropdownItems?.length ? (
|
||||||
<DropdownButton
|
<NativeDropdown
|
||||||
testID="profileHeaderDropdownBtn"
|
testID="profileHeaderDropdownBtn"
|
||||||
type="bare"
|
items={dropdownItems}>
|
||||||
items={dropdownItems}
|
<View style={[styles.btn, styles.secondaryBtn, pal.btn]}>
|
||||||
style={[styles.btn, styles.secondaryBtn, pal.btn]}>
|
<FontAwesomeIcon
|
||||||
<FontAwesomeIcon icon="ellipsis" style={[pal.text]} />
|
icon="ellipsis"
|
||||||
</DropdownButton>
|
size={20}
|
||||||
|
style={[pal.text]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</NativeDropdown>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
|
|
|
@ -10,8 +10,7 @@ import {useTheme} from 'lib/ThemeContext'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
|
import {HITSLOP_10} from 'lib/constants'
|
||||||
const MENU_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isInputFocused: boolean
|
isInputFocused: boolean
|
||||||
|
@ -55,7 +54,7 @@ export function HeaderWithInput({
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
testID="viewHeaderBackOrMenuBtn"
|
testID="viewHeaderBackOrMenuBtn"
|
||||||
onPress={onPressMenu}
|
onPress={onPressMenu}
|
||||||
hitSlop={MENU_HITSLOP}
|
hitSlop={HITSLOP_10}
|
||||||
style={styles.headerMenuBtn}
|
style={styles.headerMenuBtn}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel="Menu"
|
accessibilityLabel="Menu"
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React, {useMemo} from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import Svg, {Circle, Rect, Path} from 'react-native-svg'
|
import Svg, {Circle, Rect, Path} from 'react-native-svg'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {IconProp} from '@fortawesome/fontawesome-svg-core'
|
|
||||||
import {HighPriorityImage} from 'view/com/util/images/Image'
|
import {HighPriorityImage} from 'view/com/util/images/Image'
|
||||||
import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
|
import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
|
||||||
import {
|
import {
|
||||||
|
@ -11,12 +10,12 @@ import {
|
||||||
} from 'lib/hooks/usePermissions'
|
} from 'lib/hooks/usePermissions'
|
||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
import {colors} from 'lib/styles'
|
import {colors} from 'lib/styles'
|
||||||
import {DropdownButton} from './forms/DropdownButton'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {isWeb, isAndroid} from 'platform/detection'
|
import {isWeb, isAndroid} from 'platform/detection'
|
||||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||||
import {AvatarModeration} from 'lib/labeling/types'
|
import {AvatarModeration} from 'lib/labeling/types'
|
||||||
import {UserPreviewLink} from './UserPreviewLink'
|
import {UserPreviewLink} from './UserPreviewLink'
|
||||||
|
import {DropdownItem, NativeDropdown} from './forms/NativeDropdown'
|
||||||
|
|
||||||
type Type = 'user' | 'algo' | 'list'
|
type Type = 'user' | 'algo' | 'list'
|
||||||
|
|
||||||
|
@ -130,11 +129,18 @@ export function UserAvatar({
|
||||||
}, [type, size])
|
}, [type, size])
|
||||||
|
|
||||||
const dropdownItems = useMemo(
|
const dropdownItems = useMemo(
|
||||||
() => [
|
() =>
|
||||||
|
[
|
||||||
!isWeb && {
|
!isWeb && {
|
||||||
testID: 'changeAvatarCameraBtn',
|
testID: 'changeAvatarCameraBtn',
|
||||||
label: 'Camera',
|
label: 'Camera',
|
||||||
icon: 'camera' as IconProp,
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'camera',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_camera',
|
||||||
|
web: 'camera',
|
||||||
|
},
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
if (!(await requestCameraAccessIfNeeded())) {
|
if (!(await requestCameraAccessIfNeeded())) {
|
||||||
return
|
return
|
||||||
|
@ -152,7 +158,13 @@ export function UserAvatar({
|
||||||
{
|
{
|
||||||
testID: 'changeAvatarLibraryBtn',
|
testID: 'changeAvatarLibraryBtn',
|
||||||
label: 'Library',
|
label: 'Library',
|
||||||
icon: 'image' as IconProp,
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'photo.on.rectangle.angled',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_gallery',
|
||||||
|
web: 'gallery',
|
||||||
|
},
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
if (!(await requestPhotoAccessIfNeeded())) {
|
if (!(await requestPhotoAccessIfNeeded())) {
|
||||||
return
|
return
|
||||||
|
@ -174,15 +186,24 @@ export function UserAvatar({
|
||||||
onSelectNewAvatar?.(croppedImage)
|
onSelectNewAvatar?.(croppedImage)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
!!avatar && {
|
||||||
|
label: 'separator',
|
||||||
|
},
|
||||||
!!avatar && {
|
!!avatar && {
|
||||||
testID: 'changeAvatarRemoveBtn',
|
testID: 'changeAvatarRemoveBtn',
|
||||||
label: 'Remove',
|
label: 'Remove',
|
||||||
icon: ['far', 'trash-can'] as IconProp,
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'trash',
|
||||||
|
},
|
||||||
|
android: 'ic_delete',
|
||||||
|
web: 'trash',
|
||||||
|
},
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
onSelectNewAvatar?.(null)
|
onSelectNewAvatar?.(null)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
].filter(Boolean) as DropdownItem[],
|
||||||
[
|
[
|
||||||
avatar,
|
avatar,
|
||||||
onSelectNewAvatar,
|
onSelectNewAvatar,
|
||||||
|
@ -209,14 +230,7 @@ export function UserAvatar({
|
||||||
|
|
||||||
// onSelectNewAvatar is only passed as prop on the EditProfile component
|
// onSelectNewAvatar is only passed as prop on the EditProfile component
|
||||||
return onSelectNewAvatar ? (
|
return onSelectNewAvatar ? (
|
||||||
<DropdownButton
|
<NativeDropdown testID="changeAvatarBtn" items={dropdownItems}>
|
||||||
testID="changeAvatarBtn"
|
|
||||||
type="bare"
|
|
||||||
items={dropdownItems}
|
|
||||||
openToRight
|
|
||||||
rightOffset={-10}
|
|
||||||
bottomOffset={-10}
|
|
||||||
menuWidth={170}>
|
|
||||||
{avatar ? (
|
{avatar ? (
|
||||||
<HighPriorityImage
|
<HighPriorityImage
|
||||||
testID="userAvatarImage"
|
testID="userAvatarImage"
|
||||||
|
@ -234,7 +248,7 @@ export function UserAvatar({
|
||||||
color={pal.text.color as string}
|
color={pal.text.color as string}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</DropdownButton>
|
</NativeDropdown>
|
||||||
) : avatar &&
|
) : avatar &&
|
||||||
!((moderation?.blur && isAndroid) /* android crashes with blur */) ? (
|
!((moderation?.blur && isAndroid) /* android crashes with blur */) ? (
|
||||||
<View style={{width: size, height: size}}>
|
<View style={{width: size, height: size}}>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react'
|
import React, {useMemo} from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {IconProp} from '@fortawesome/fontawesome-svg-core'
|
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {colors} from 'lib/styles'
|
import {colors} from 'lib/styles'
|
||||||
import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
|
import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
|
||||||
|
@ -10,11 +9,11 @@ import {
|
||||||
usePhotoLibraryPermission,
|
usePhotoLibraryPermission,
|
||||||
useCameraPermission,
|
useCameraPermission,
|
||||||
} from 'lib/hooks/usePermissions'
|
} from 'lib/hooks/usePermissions'
|
||||||
import {DropdownButton} from './forms/DropdownButton'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {AvatarModeration} from 'lib/labeling/types'
|
import {AvatarModeration} from 'lib/labeling/types'
|
||||||
import {isWeb, isAndroid} from 'platform/detection'
|
import {isWeb, isAndroid} from 'platform/detection'
|
||||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||||
|
import {NativeDropdown, DropdownItem} from './forms/NativeDropdown'
|
||||||
|
|
||||||
export function UserBanner({
|
export function UserBanner({
|
||||||
banner,
|
banner,
|
||||||
|
@ -30,11 +29,19 @@ export function UserBanner({
|
||||||
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
||||||
const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
|
const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
|
||||||
|
|
||||||
const dropdownItems = [
|
const dropdownItems: DropdownItem[] = useMemo(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
!isWeb && {
|
!isWeb && {
|
||||||
testID: 'changeBannerCameraBtn',
|
testID: 'changeBannerCameraBtn',
|
||||||
label: 'Camera',
|
label: 'Camera',
|
||||||
icon: 'camera' as IconProp,
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'camera',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_camera',
|
||||||
|
web: 'camera',
|
||||||
|
},
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
if (!(await requestCameraAccessIfNeeded())) {
|
if (!(await requestCameraAccessIfNeeded())) {
|
||||||
return
|
return
|
||||||
|
@ -50,7 +57,13 @@ export function UserBanner({
|
||||||
{
|
{
|
||||||
testID: 'changeBannerLibraryBtn',
|
testID: 'changeBannerLibraryBtn',
|
||||||
label: 'Library',
|
label: 'Library',
|
||||||
icon: 'image' as IconProp,
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'photo.on.rectangle.angled',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_gallery',
|
||||||
|
web: 'gallery',
|
||||||
|
},
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
if (!(await requestPhotoAccessIfNeeded())) {
|
if (!(await requestPhotoAccessIfNeeded())) {
|
||||||
return
|
return
|
||||||
|
@ -70,23 +83,30 @@ export function UserBanner({
|
||||||
!!banner && {
|
!!banner && {
|
||||||
testID: 'changeBannerRemoveBtn',
|
testID: 'changeBannerRemoveBtn',
|
||||||
label: 'Remove',
|
label: 'Remove',
|
||||||
icon: ['far', 'trash-can'] as IconProp,
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'trash',
|
||||||
|
},
|
||||||
|
android: 'ic_delete',
|
||||||
|
web: 'trash',
|
||||||
|
},
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
onSelectNewBanner?.(null)
|
onSelectNewBanner?.(null)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
].filter(Boolean) as DropdownItem[],
|
||||||
|
[
|
||||||
|
banner,
|
||||||
|
onSelectNewBanner,
|
||||||
|
requestCameraAccessIfNeeded,
|
||||||
|
requestPhotoAccessIfNeeded,
|
||||||
|
store,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
// setUserBanner is only passed as prop on the EditProfile component
|
// setUserBanner is only passed as prop on the EditProfile component
|
||||||
return onSelectNewBanner ? (
|
return onSelectNewBanner ? (
|
||||||
<DropdownButton
|
<NativeDropdown testID="changeBannerBtn" items={dropdownItems}>
|
||||||
testID="changeBannerBtn"
|
|
||||||
type="bare"
|
|
||||||
items={dropdownItems}
|
|
||||||
openToRight
|
|
||||||
rightOffset={-200}
|
|
||||||
bottomOffset={-10}
|
|
||||||
menuWidth={170}>
|
|
||||||
{banner ? (
|
{banner ? (
|
||||||
<Image
|
<Image
|
||||||
testID="userBannerImage"
|
testID="userBannerImage"
|
||||||
|
@ -109,7 +129,7 @@ export function UserBanner({
|
||||||
color={pal.text.color as string}
|
color={pal.text.color as string}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</DropdownButton>
|
</NativeDropdown>
|
||||||
) : banner &&
|
) : banner &&
|
||||||
!((moderation?.blur && isAndroid) /* android crashes with blur */) ? (
|
!((moderation?.blur && isAndroid) /* android crashes with blur */) ? (
|
||||||
<Image
|
<Image
|
||||||
|
|
|
@ -14,14 +14,10 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Text} from '../text/Text'
|
import {Text} from '../text/Text'
|
||||||
import {Button, ButtonType} from './Button'
|
import {Button, ButtonType} from './Button'
|
||||||
import {colors} from 'lib/styles'
|
import {colors} from 'lib/styles'
|
||||||
import {toShareUrl} from 'lib/strings/url-helpers'
|
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useTheme} from 'lib/ThemeContext'
|
import {useTheme} from 'lib/ThemeContext'
|
||||||
import {isWeb} from 'platform/detection'
|
import {HITSLOP_10} from 'lib/constants'
|
||||||
import {shareUrl} from 'lib/sharing'
|
|
||||||
|
|
||||||
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
|
|
||||||
const ESTIMATED_BTN_HEIGHT = 50
|
const ESTIMATED_BTN_HEIGHT = 50
|
||||||
const ESTIMATED_SEP_HEIGHT = 16
|
const ESTIMATED_SEP_HEIGHT = 16
|
||||||
const ESTIMATED_HEADING_HEIGHT = 60
|
const ESTIMATED_HEADING_HEIGHT = 60
|
||||||
|
@ -140,7 +136,7 @@ export function DropdownButton({
|
||||||
testID={testID}
|
testID={testID}
|
||||||
style={style}
|
style={style}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP_10}
|
||||||
ref={ref1}
|
ref={ref1}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={accessibilityLabel || `Opens ${numItems} options`}
|
accessibilityLabel={accessibilityLabel || `Opens ${numItems} options`}
|
||||||
|
@ -163,112 +159,6 @@ export function DropdownButton({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PostDropdownBtn({
|
|
||||||
testID,
|
|
||||||
style,
|
|
||||||
children,
|
|
||||||
itemUri,
|
|
||||||
itemCid,
|
|
||||||
itemHref,
|
|
||||||
isAuthor,
|
|
||||||
isThreadMuted,
|
|
||||||
onCopyPostText,
|
|
||||||
onOpenTranslate,
|
|
||||||
onToggleThreadMute,
|
|
||||||
onDeletePost,
|
|
||||||
}: {
|
|
||||||
testID?: string
|
|
||||||
style?: StyleProp<ViewStyle>
|
|
||||||
children?: React.ReactNode
|
|
||||||
itemUri: string
|
|
||||||
itemCid: string
|
|
||||||
itemHref: string
|
|
||||||
itemTitle: string
|
|
||||||
isAuthor: boolean
|
|
||||||
isThreadMuted: boolean
|
|
||||||
onCopyPostText: () => void
|
|
||||||
onOpenTranslate: () => void
|
|
||||||
onToggleThreadMute: () => void
|
|
||||||
onDeletePost: () => void
|
|
||||||
}) {
|
|
||||||
const store = useStores()
|
|
||||||
|
|
||||||
const dropdownItems: DropdownItem[] = [
|
|
||||||
{
|
|
||||||
testID: 'postDropdownTranslateBtn',
|
|
||||||
icon: 'language',
|
|
||||||
label: 'Translate...',
|
|
||||||
onPress() {
|
|
||||||
onOpenTranslate()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testID: 'postDropdownCopyTextBtn',
|
|
||||||
icon: ['far', 'paste'],
|
|
||||||
label: 'Copy post text',
|
|
||||||
onPress() {
|
|
||||||
onCopyPostText()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testID: 'postDropdownShareBtn',
|
|
||||||
icon: 'share',
|
|
||||||
label: 'Share...',
|
|
||||||
onPress() {
|
|
||||||
const url = toShareUrl(itemHref)
|
|
||||||
shareUrl(url)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{sep: true},
|
|
||||||
{
|
|
||||||
testID: 'postDropdownMuteThreadBtn',
|
|
||||||
icon: 'comment-slash',
|
|
||||||
label: isThreadMuted ? 'Unmute thread' : 'Mute thread',
|
|
||||||
onPress() {
|
|
||||||
onToggleThreadMute()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{sep: true},
|
|
||||||
!isAuthor && {
|
|
||||||
testID: 'postDropdownReportBtn',
|
|
||||||
icon: 'circle-exclamation',
|
|
||||||
label: 'Report post',
|
|
||||||
onPress() {
|
|
||||||
store.shell.openModal({
|
|
||||||
name: 'report-post',
|
|
||||||
postUri: itemUri,
|
|
||||||
postCid: itemCid,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isAuthor && {
|
|
||||||
testID: 'postDropdownDeleteBtn',
|
|
||||||
icon: ['far', 'trash-can'],
|
|
||||||
label: 'Delete post',
|
|
||||||
onPress() {
|
|
||||||
store.shell.openModal({
|
|
||||||
name: 'confirm',
|
|
||||||
title: 'Delete this post?',
|
|
||||||
message: 'Are you sure? This can not be undone.',
|
|
||||||
onPressConfirm: onDeletePost,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
].filter(Boolean) as DropdownItem[]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DropdownButton
|
|
||||||
testID={testID}
|
|
||||||
style={style}
|
|
||||||
items={dropdownItems}
|
|
||||||
menuWidth={isWeb ? 220 : 200}
|
|
||||||
accessibilityLabel="Additional post actions"
|
|
||||||
accessibilityHint="">
|
|
||||||
{children}
|
|
||||||
</DropdownButton>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDropdownMenu(
|
function createDropdownMenu(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
|
@ -324,15 +214,16 @@ const DropdownItems = ({
|
||||||
|
|
||||||
const numItems = items.filter(isBtn).length
|
const numItems = items.filter(isBtn).length
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<TouchableWithoutFeedback
|
|
||||||
onPress={onOuterPress}
|
|
||||||
// TODO: Refactor dropdown components to:
|
// TODO: Refactor dropdown components to:
|
||||||
// - (On web, if not handled by React Native) use semantic <select />
|
// - (On web, if not handled by React Native) use semantic <select />
|
||||||
// and <option /> elements for keyboard navigation out of the box
|
// and <option /> elements for keyboard navigation out of the box
|
||||||
// - (On mobile) be buttons by default, accept `label` and `nativeID`
|
// - (On mobile) be buttons by default, accept `label` and `nativeID`
|
||||||
// props, and always have an explicit label
|
// props, and always have an explicit label
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* This TouchableWithoutFeedback renders the background so if the user clicks outside, the dropdown closes */}
|
||||||
|
<TouchableWithoutFeedback
|
||||||
|
onPress={onOuterPress}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel="Toggle dropdown"
|
accessibilityLabel="Toggle dropdown"
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
|
import {
|
||||||
|
Pressable,
|
||||||
|
StyleSheet,
|
||||||
|
Platform,
|
||||||
|
StyleProp,
|
||||||
|
ViewStyle,
|
||||||
|
} from 'react-native'
|
||||||
|
import {IconProp} from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import {MenuItemCommonProps} from 'zeego/lib/typescript/menu'
|
||||||
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {isWeb} from 'platform/detection'
|
||||||
|
import {useTheme} from 'lib/ThemeContext'
|
||||||
|
import {HITSLOP_10} from 'lib/constants'
|
||||||
|
|
||||||
|
// Custom Dropdown Menu Components
|
||||||
|
// ==
|
||||||
|
export const DropdownMenuRoot = DropdownMenu.Root
|
||||||
|
export const DropdownMenuTrigger = DropdownMenu.Trigger
|
||||||
|
export const DropdownMenuContent = DropdownMenu.Content
|
||||||
|
type ItemProps = React.ComponentProps<(typeof DropdownMenu)['Item']>
|
||||||
|
export const DropdownMenuItem = DropdownMenu.create(
|
||||||
|
(props: ItemProps & {testID?: string}) => {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
const theme = useTheme()
|
||||||
|
const [focused, setFocused] = React.useState(false)
|
||||||
|
const {borderColor: backgroundColor} =
|
||||||
|
theme.colorScheme === 'dark' ? pal.borderDark : pal.border
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
{...props}
|
||||||
|
style={[styles.item, focused && {backgroundColor: backgroundColor}]}
|
||||||
|
onFocus={() => {
|
||||||
|
setFocused(true)
|
||||||
|
props.onFocus && props.onFocus()
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
setFocused(false)
|
||||||
|
props.onBlur && props.onBlur()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'Item',
|
||||||
|
)
|
||||||
|
type TitleProps = React.ComponentProps<(typeof DropdownMenu)['ItemTitle']>
|
||||||
|
export const DropdownMenuItemTitle = DropdownMenu.create(
|
||||||
|
(props: TitleProps) => {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
return (
|
||||||
|
<DropdownMenu.ItemTitle
|
||||||
|
{...props}
|
||||||
|
style={[props.style, pal.text, styles.itemTitle]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'ItemTitle',
|
||||||
|
)
|
||||||
|
type IconProps = React.ComponentProps<(typeof DropdownMenu)['ItemIcon']>
|
||||||
|
export const DropdownMenuItemIcon = DropdownMenu.create((props: IconProps) => {
|
||||||
|
return <DropdownMenu.ItemIcon {...props} />
|
||||||
|
}, 'ItemIcon')
|
||||||
|
type SeparatorProps = React.ComponentProps<(typeof DropdownMenu)['Separator']>
|
||||||
|
export const DropdownMenuSeparator = DropdownMenu.create(
|
||||||
|
(props: SeparatorProps) => {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
const theme = useTheme()
|
||||||
|
const {borderColor: separatorColor} =
|
||||||
|
theme.colorScheme === 'dark' ? pal.borderDark : pal.border
|
||||||
|
return (
|
||||||
|
<DropdownMenu.Separator
|
||||||
|
{...props}
|
||||||
|
style={[
|
||||||
|
props.style,
|
||||||
|
styles.separator,
|
||||||
|
{backgroundColor: separatorColor},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'Separator',
|
||||||
|
)
|
||||||
|
|
||||||
|
// Types for Dropdown Menu and Items
|
||||||
|
export type DropdownItem = {
|
||||||
|
label: string | 'separator'
|
||||||
|
onPress?: () => void
|
||||||
|
testID?: string
|
||||||
|
icon?: {
|
||||||
|
ios: MenuItemCommonProps['ios']
|
||||||
|
android: string
|
||||||
|
web: IconProp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type Props = {
|
||||||
|
items: DropdownItem[]
|
||||||
|
children?: React.ReactNode
|
||||||
|
testID?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The `NativeDropdown` function uses native iOS and Android dropdown menus.
|
||||||
|
* It also creates a animated custom dropdown for web that uses
|
||||||
|
* Radix UI primitives under the hood
|
||||||
|
* @prop {DropdownItem[]} items - An array of dropdown items
|
||||||
|
* @prop {React.ReactNode} children - A custom dropdown trigger
|
||||||
|
*/
|
||||||
|
export function NativeDropdown({items, children, testID}: Props) {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
const theme = useTheme()
|
||||||
|
const dropDownBackgroundColor =
|
||||||
|
theme.colorScheme === 'dark' ? pal.btn : pal.viewLight
|
||||||
|
const defaultCtrlColor = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
color: theme.palette.default.postCtrl,
|
||||||
|
}),
|
||||||
|
[theme],
|
||||||
|
) as StyleProp<ViewStyle>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenuRoot>
|
||||||
|
<DropdownMenuTrigger action="press">
|
||||||
|
<Pressable
|
||||||
|
testID={testID}
|
||||||
|
accessibilityRole="button"
|
||||||
|
style={({pressed}) => [{opacity: pressed ? 0.5 : 1}]}
|
||||||
|
hitSlop={HITSLOP_10}>
|
||||||
|
{children ? (
|
||||||
|
children
|
||||||
|
) : (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="ellipsis"
|
||||||
|
size={20}
|
||||||
|
style={[defaultCtrlColor, styles.ellipsis]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
style={[styles.content, dropDownBackgroundColor]}
|
||||||
|
loop>
|
||||||
|
{items.map((item, index) => {
|
||||||
|
if (item.label === 'separator') {
|
||||||
|
return (
|
||||||
|
<DropdownMenuSeparator
|
||||||
|
key={getKey(item.label, index, item.testID)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (index > 1 && items[index - 1].label === 'separator') {
|
||||||
|
return (
|
||||||
|
<DropdownMenu.Group key={getKey(item.label, index, item.testID)}>
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={getKey(item.label, index, item.testID)}
|
||||||
|
onSelect={item.onPress}>
|
||||||
|
<DropdownMenuItemTitle>{item.label}</DropdownMenuItemTitle>
|
||||||
|
{item.icon && (
|
||||||
|
<DropdownMenuItemIcon
|
||||||
|
ios={item.icon.ios}
|
||||||
|
// androidIconName={item.icon.android} TODO: Add custom android icon support, because these ones are based on https://developer.android.com/reference/android/R.drawable.html and they are ugly
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={item.icon.web}
|
||||||
|
size={20}
|
||||||
|
style={[pal.text]}
|
||||||
|
/>
|
||||||
|
</DropdownMenuItemIcon>
|
||||||
|
)}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={getKey(item.label, index, item.testID)}
|
||||||
|
onSelect={item.onPress}>
|
||||||
|
<DropdownMenuItemTitle>{item.label}</DropdownMenuItemTitle>
|
||||||
|
{item.icon && (
|
||||||
|
<DropdownMenuItemIcon
|
||||||
|
ios={item.icon.ios}
|
||||||
|
// androidIconName={item.icon.android}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={item.icon.web}
|
||||||
|
size={20}
|
||||||
|
style={[pal.text]}
|
||||||
|
/>
|
||||||
|
</DropdownMenuItemIcon>
|
||||||
|
)}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenuRoot>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getKey = (label: string, index: number, id?: string) => {
|
||||||
|
if (id) {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return `${label}_${index}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
separator: {
|
||||||
|
height: 1,
|
||||||
|
marginVertical: 4,
|
||||||
|
},
|
||||||
|
ellipsis: {
|
||||||
|
padding: isWeb ? 0 : 10,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
backgroundColor: '#f0f0f0',
|
||||||
|
borderRadius: 8,
|
||||||
|
paddingVertical: 4,
|
||||||
|
paddingHorizontal: 4,
|
||||||
|
marginTop: 6,
|
||||||
|
...Platform.select({
|
||||||
|
web: {
|
||||||
|
animationDuration: '400ms',
|
||||||
|
animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
||||||
|
willChange: 'transform, opacity',
|
||||||
|
animationKeyframes: {
|
||||||
|
'0%': {opacity: 0, transform: [{scale: 0.5}]},
|
||||||
|
'100%': {opacity: 1, transform: [{scale: 1}]},
|
||||||
|
},
|
||||||
|
boxShadow:
|
||||||
|
'0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)',
|
||||||
|
transformOrigin: 'var(--radix-dropdown-menu-content-transform-origin)',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
columnGap: 20,
|
||||||
|
// @ts-ignore -web
|
||||||
|
cursor: 'pointer',
|
||||||
|
paddingVertical: 8,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
|
itemTitle: {
|
||||||
|
fontSize: 18,
|
||||||
|
},
|
||||||
|
})
|
|
@ -0,0 +1,148 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {toShareUrl} from 'lib/strings/url-helpers'
|
||||||
|
import {useStores} from 'state/index'
|
||||||
|
import {shareUrl} from 'lib/sharing'
|
||||||
|
import {
|
||||||
|
NativeDropdown,
|
||||||
|
DropdownItem as NativeDropdownItem,
|
||||||
|
} from './NativeDropdown'
|
||||||
|
import {Pressable} from 'react-native'
|
||||||
|
|
||||||
|
export function PostDropdownBtn({
|
||||||
|
testID,
|
||||||
|
itemUri,
|
||||||
|
itemCid,
|
||||||
|
itemHref,
|
||||||
|
isAuthor,
|
||||||
|
isThreadMuted,
|
||||||
|
onCopyPostText,
|
||||||
|
onOpenTranslate,
|
||||||
|
onToggleThreadMute,
|
||||||
|
onDeletePost,
|
||||||
|
}: {
|
||||||
|
testID: string
|
||||||
|
itemUri: string
|
||||||
|
itemCid: string
|
||||||
|
itemHref: string
|
||||||
|
itemTitle: string
|
||||||
|
isAuthor: boolean
|
||||||
|
isThreadMuted: boolean
|
||||||
|
onCopyPostText: () => void
|
||||||
|
onOpenTranslate: () => void
|
||||||
|
onToggleThreadMute: () => void
|
||||||
|
onDeletePost: () => void
|
||||||
|
}) {
|
||||||
|
const store = useStores()
|
||||||
|
|
||||||
|
const dropdownItems: NativeDropdownItem[] = [
|
||||||
|
{
|
||||||
|
label: 'Translate',
|
||||||
|
onPress() {
|
||||||
|
onOpenTranslate()
|
||||||
|
},
|
||||||
|
testID: 'postDropdownTranslateBtn',
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'character.book.closed',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_sort_alphabetically',
|
||||||
|
web: 'language',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Copy post text',
|
||||||
|
onPress() {
|
||||||
|
onCopyPostText()
|
||||||
|
},
|
||||||
|
testID: 'postDropdownCopyTextBtn',
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'doc.on.doc',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_edit',
|
||||||
|
web: ['far', 'paste'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Share',
|
||||||
|
onPress() {
|
||||||
|
const url = toShareUrl(itemHref)
|
||||||
|
shareUrl(url)
|
||||||
|
},
|
||||||
|
testID: 'postDropdownShareBtn',
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'square.and.arrow.up',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_share',
|
||||||
|
web: 'share',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'separator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: isThreadMuted ? 'Unmute thread' : 'Mute thread',
|
||||||
|
onPress() {
|
||||||
|
onToggleThreadMute()
|
||||||
|
},
|
||||||
|
testID: 'postDropdownMuteThreadBtn',
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'speaker.slash',
|
||||||
|
},
|
||||||
|
android: 'ic_lock_silent_mode',
|
||||||
|
web: 'comment-slash',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'separator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Report post',
|
||||||
|
onPress() {
|
||||||
|
store.shell.openModal({
|
||||||
|
name: 'report-post',
|
||||||
|
postUri: itemUri,
|
||||||
|
postCid: itemCid,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
testID: 'postDropdownReportBtn',
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'exclamationmark.triangle',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_report_image',
|
||||||
|
web: 'circle-exclamation',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isAuthor && {
|
||||||
|
label: 'separator',
|
||||||
|
},
|
||||||
|
isAuthor && {
|
||||||
|
label: 'Delete post',
|
||||||
|
onPress() {
|
||||||
|
store.shell.openModal({
|
||||||
|
name: 'confirm',
|
||||||
|
title: 'Delete this post?',
|
||||||
|
message: 'Are you sure? This can not be undone.',
|
||||||
|
onPressConfirm: onDeletePost,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
testID: 'postDropdownDeleteBtn',
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'trash',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_delete',
|
||||||
|
web: ['far', 'trash-can'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
].filter(Boolean) as NativeDropdownItem[]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable testID={testID} accessibilityRole="button">
|
||||||
|
<NativeDropdown items={dropdownItems} />
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}
|
|
@ -5,8 +5,7 @@ import {Text} from '../text/Text'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {LoadLatestBtn as LoadLatestBtnMobile} from './LoadLatestBtnMobile'
|
import {LoadLatestBtn as LoadLatestBtnMobile} from './LoadLatestBtnMobile'
|
||||||
import {isMobileWeb} from 'platform/detection'
|
import {isMobileWeb} from 'platform/detection'
|
||||||
|
import {HITSLOP_20} from 'lib/constants'
|
||||||
const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
|
|
||||||
|
|
||||||
export const LoadLatestBtn = ({
|
export const LoadLatestBtn = ({
|
||||||
onPress,
|
onPress,
|
||||||
|
@ -40,7 +39,7 @@ export const LoadLatestBtn = ({
|
||||||
minimalShellMode && styles.loadLatestCenteredMinimal,
|
minimalShellMode && styles.loadLatestCenteredMinimal,
|
||||||
]}
|
]}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP_20}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={label}
|
accessibilityLabel={label}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
|
@ -52,7 +51,7 @@ export const LoadLatestBtn = ({
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[pal.view, pal.borderDark, styles.loadLatest]}
|
style={[pal.view, pal.borderDark, styles.loadLatest]}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP_20}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={label}
|
accessibilityLabel={label}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
|
|
|
@ -7,8 +7,7 @@ import {clamp} from 'lodash'
|
||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {colors} from 'lib/styles'
|
import {colors} from 'lib/styles'
|
||||||
|
import {HITSLOP_20} from 'lib/constants'
|
||||||
const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
|
|
||||||
|
|
||||||
export const LoadLatestBtn = observer(
|
export const LoadLatestBtn = observer(
|
||||||
({
|
({
|
||||||
|
@ -35,7 +34,7 @@ export const LoadLatestBtn = observer(
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP_20}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={label}
|
accessibilityLabel={label}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
|
|
|
@ -6,17 +6,13 @@ import {
|
||||||
View,
|
View,
|
||||||
ViewStyle,
|
ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {
|
|
||||||
FontAwesomeIcon,
|
|
||||||
FontAwesomeIconStyle,
|
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
|
||||||
// DISABLED see #135
|
// DISABLED see #135
|
||||||
// import {
|
// import {
|
||||||
// TriggerableAnimated,
|
// TriggerableAnimated,
|
||||||
// TriggerableAnimatedRef,
|
// TriggerableAnimatedRef,
|
||||||
// } from './anim/TriggerableAnimated'
|
// } from './anim/TriggerableAnimated'
|
||||||
import {Text} from '../text/Text'
|
import {Text} from '../text/Text'
|
||||||
import {PostDropdownBtn} from '../forms/DropdownButton'
|
import {PostDropdownBtn} from '../forms/PostDropdownBtn'
|
||||||
import {HeartIcon, HeartIconSolid, CommentBottomArrow} from 'lib/icons'
|
import {HeartIcon, HeartIconSolid, CommentBottomArrow} from 'lib/icons'
|
||||||
import {s, colors} from 'lib/styles'
|
import {s, colors} from 'lib/styles'
|
||||||
import {pluralize} from 'lib/strings/helpers'
|
import {pluralize} from 'lib/strings/helpers'
|
||||||
|
@ -24,6 +20,7 @@ import {useTheme} from 'lib/ThemeContext'
|
||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
import {RepostButton} from './RepostButton'
|
import {RepostButton} from './RepostButton'
|
||||||
import {Haptics} from 'lib/haptics'
|
import {Haptics} from 'lib/haptics'
|
||||||
|
import {createHitslop} from 'lib/constants'
|
||||||
|
|
||||||
interface PostCtrlsOpts {
|
interface PostCtrlsOpts {
|
||||||
itemUri: string
|
itemUri: string
|
||||||
|
@ -56,7 +53,7 @@ interface PostCtrlsOpts {
|
||||||
onDeletePost: () => void
|
onDeletePost: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const HITSLOP = {top: 5, left: 5, bottom: 5, right: 5}
|
const HITSLOP = createHitslop(5)
|
||||||
|
|
||||||
// DISABLED see #135
|
// DISABLED see #135
|
||||||
/*
|
/*
|
||||||
|
@ -222,11 +219,9 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View>
|
|
||||||
{opts.big ? undefined : (
|
{opts.big ? undefined : (
|
||||||
<PostDropdownBtn
|
<PostDropdownBtn
|
||||||
testID="postDropdownBtn"
|
testID="postDropdownBtn"
|
||||||
style={styles.ctrl}
|
|
||||||
itemUri={opts.itemUri}
|
itemUri={opts.itemUri}
|
||||||
itemCid={opts.itemCid}
|
itemCid={opts.itemCid}
|
||||||
itemHref={opts.itemHref}
|
itemHref={opts.itemHref}
|
||||||
|
@ -236,22 +231,9 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
onCopyPostText={opts.onCopyPostText}
|
onCopyPostText={opts.onCopyPostText}
|
||||||
onOpenTranslate={opts.onOpenTranslate}
|
onOpenTranslate={opts.onOpenTranslate}
|
||||||
onToggleThreadMute={opts.onToggleThreadMute}
|
onToggleThreadMute={opts.onToggleThreadMute}
|
||||||
onDeletePost={opts.onDeletePost}>
|
onDeletePost={opts.onDeletePost}
|
||||||
<FontAwesomeIcon
|
|
||||||
icon="ellipsis-h"
|
|
||||||
size={18}
|
|
||||||
style={[
|
|
||||||
s.mt2,
|
|
||||||
s.mr5,
|
|
||||||
{
|
|
||||||
color:
|
|
||||||
theme.colorScheme === 'light' ? colors.gray4 : colors.gray5,
|
|
||||||
} as FontAwesomeIconStyle,
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
</PostDropdownBtn>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
|
||||||
{/* used for adding pad to the right side */}
|
{/* used for adding pad to the right side */}
|
||||||
<View />
|
<View />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -6,8 +6,9 @@ import {useTheme} from 'lib/ThemeContext'
|
||||||
import {Text} from '../text/Text'
|
import {Text} from '../text/Text'
|
||||||
import {pluralize} from 'lib/strings/helpers'
|
import {pluralize} from 'lib/strings/helpers'
|
||||||
import {useStores} from 'state/index'
|
import {useStores} from 'state/index'
|
||||||
|
import {createHitslop} from 'lib/constants'
|
||||||
|
|
||||||
const HITSLOP = {top: 5, left: 5, bottom: 5, right: 5}
|
const HITSLOP = createHitslop(5)
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isReposted: boolean
|
isReposted: boolean
|
||||||
|
|
|
@ -29,10 +29,10 @@ import {Haptics} from 'lib/haptics'
|
||||||
import {ComposeIcon2} from 'lib/icons'
|
import {ComposeIcon2} from 'lib/icons'
|
||||||
import {FAB} from '../com/util/fab/FAB'
|
import {FAB} from '../com/util/fab/FAB'
|
||||||
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
||||||
import {DropdownButton, DropdownItem} from 'view/com/util/forms/DropdownButton'
|
|
||||||
import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
|
import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
|
||||||
import {EmptyState} from 'view/com/util/EmptyState'
|
import {EmptyState} from 'view/com/util/EmptyState'
|
||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
|
import {NativeDropdown, DropdownItem} from 'view/com/util/forms/NativeDropdown'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'CustomFeed'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'CustomFeed'>
|
||||||
|
@ -121,11 +121,25 @@ export const CustomFeedScreen = withAuthRequired(
|
||||||
testID: 'feedHeaderDropdownRemoveBtn',
|
testID: 'feedHeaderDropdownRemoveBtn',
|
||||||
label: 'Remove from my feeds',
|
label: 'Remove from my feeds',
|
||||||
onPress: onToggleSaved,
|
onPress: onToggleSaved,
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'trash',
|
||||||
|
},
|
||||||
|
android: 'ic_delete',
|
||||||
|
web: 'trash',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testID: 'feedHeaderDropdownShareBtn',
|
testID: 'feedHeaderDropdownShareBtn',
|
||||||
label: 'Share link',
|
label: 'Share link',
|
||||||
onPress: onPressShare,
|
onPress: onPressShare,
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'square.and.arrow.up',
|
||||||
|
},
|
||||||
|
android: 'ic_menu_share',
|
||||||
|
web: 'share',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
return items
|
return items
|
||||||
|
@ -163,17 +177,10 @@ export const CustomFeedScreen = withAuthRequired(
|
||||||
</Button>
|
</Button>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
{currentFeed?.isSaved ? (
|
{currentFeed?.isSaved ? (
|
||||||
<DropdownButton
|
<NativeDropdown
|
||||||
testID="feedHeaderDropdownBtn"
|
testID="feedHeaderDropdownBtn"
|
||||||
type="default-light"
|
|
||||||
items={dropdownItems}
|
items={dropdownItems}
|
||||||
menuWidth={250}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon="ellipsis"
|
|
||||||
color={pal.colors.textLight}
|
|
||||||
size={18}
|
|
||||||
/>
|
/>
|
||||||
</DropdownButton>
|
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
type="default-light"
|
type="default-light"
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Linking,
|
Linking,
|
||||||
Platform,
|
Platform,
|
||||||
|
Pressable,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
|
@ -30,7 +31,6 @@ import {Link} from '../com/util/Link'
|
||||||
import {Text} from '../com/util/text/Text'
|
import {Text} from '../com/util/text/Text'
|
||||||
import * as Toast from '../com/util/Toast'
|
import * as Toast from '../com/util/Toast'
|
||||||
import {UserAvatar} from '../com/util/UserAvatar'
|
import {UserAvatar} from '../com/util/UserAvatar'
|
||||||
import {DropdownButton} from 'view/com/util/forms/DropdownButton'
|
|
||||||
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
|
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
|
||||||
import {SelectableBtn} from 'view/com/util/forms/SelectableBtn'
|
import {SelectableBtn} from 'view/com/util/forms/SelectableBtn'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
@ -50,6 +50,7 @@ import {makeProfileLink} from 'lib/routes/links'
|
||||||
// -prf
|
// -prf
|
||||||
import {useDebugHeaderSetting} from 'lib/api/debug-appview-proxy-header'
|
import {useDebugHeaderSetting} from 'lib/api/debug-appview-proxy-header'
|
||||||
import {STATUS_PAGE_URL} from 'lib/constants'
|
import {STATUS_PAGE_URL} from 'lib/constants'
|
||||||
|
import {DropdownItem, NativeDropdown} from 'view/com/util/forms/NativeDropdown'
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
|
||||||
export const SettingsScreen = withAuthRequired(
|
export const SettingsScreen = withAuthRequired(
|
||||||
|
@ -565,24 +566,31 @@ export const SettingsScreen = withAuthRequired(
|
||||||
function AccountDropdownBtn({handle}: {handle: string}) {
|
function AccountDropdownBtn({handle}: {handle: string}) {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const items = [
|
const items: DropdownItem[] = [
|
||||||
{
|
{
|
||||||
label: 'Remove account',
|
label: 'Remove account',
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
store.session.removeAccount(handle)
|
store.session.removeAccount(handle)
|
||||||
Toast.show('Account removed from quick access')
|
Toast.show('Account removed from quick access')
|
||||||
},
|
},
|
||||||
|
icon: {
|
||||||
|
ios: {
|
||||||
|
name: 'trash',
|
||||||
|
},
|
||||||
|
android: 'ic_delete',
|
||||||
|
web: 'trash',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
return (
|
return (
|
||||||
<View style={s.pl10}>
|
<Pressable accessibilityRole="button" style={s.pl10}>
|
||||||
<DropdownButton type="bare" items={items}>
|
<NativeDropdown testID="accountSettingsDropdownBtn" items={items}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="ellipsis-h"
|
icon="ellipsis-h"
|
||||||
style={pal.textLight as FontAwesomeIconStyle}
|
style={pal.textLight as FontAwesomeIconStyle}
|
||||||
/>
|
/>
|
||||||
</DropdownButton>
|
</NativeDropdown>
|
||||||
</View>
|
</Pressable>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
374
yarn.lock
374
yarn.lock
|
@ -2586,6 +2586,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.11"
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.13.10":
|
||||||
|
version "7.22.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
|
||||||
|
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
"@babel/template@^7.0.0", "@babel/template@^7.22.5", "@babel/template@^7.3.3":
|
"@babel/template@^7.0.0", "@babel/template@^7.22.5", "@babel/template@^7.3.3":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
|
||||||
|
@ -2833,6 +2840,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
||||||
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
||||||
|
|
||||||
|
"@dominicstop/ts-event-emitter@^1.1.0":
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@dominicstop/ts-event-emitter/-/ts-event-emitter-1.1.0.tgz#1f3d3fa878a1ccab686931280757954719cf88e4"
|
||||||
|
integrity sha512-CcxmJIvUb1vsFheuGGVSQf4KdPZC44XolpUT34+vlal+LyQoBUOn31pjFET5M9ctOxEpt8xa0M3/2M7uUiAoJw==
|
||||||
|
|
||||||
"@egjs/hammerjs@^2.0.17":
|
"@egjs/hammerjs@^2.0.17":
|
||||||
version "2.0.17"
|
version "2.0.17"
|
||||||
resolved "https://registry.yarnpkg.com/@egjs/hammerjs/-/hammerjs-2.0.17.tgz#5dc02af75a6a06e4c2db0202cae38c9263895124"
|
resolved "https://registry.yarnpkg.com/@egjs/hammerjs/-/hammerjs-2.0.17.tgz#5dc02af75a6a06e4c2db0202cae38c9263895124"
|
||||||
|
@ -3241,6 +3253,25 @@
|
||||||
find-up "^5.0.0"
|
find-up "^5.0.0"
|
||||||
js-yaml "^4.1.0"
|
js-yaml "^4.1.0"
|
||||||
|
|
||||||
|
"@floating-ui/core@^1.3.1":
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.3.1.tgz#4d795b649cc3b1cbb760d191c80dcb4353c9a366"
|
||||||
|
integrity sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==
|
||||||
|
|
||||||
|
"@floating-ui/dom@^1.3.0":
|
||||||
|
version "1.4.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.4.5.tgz#336dfb9870c98b471ff5802002982e489b8bd1c5"
|
||||||
|
integrity sha512-96KnRWkRnuBSSFbj0sFGwwOUd8EkiecINVl0O9wiZlZ64EkpyAOG3Xc2vKKNJmru0Z7RqWNymA+6b8OZqjgyyw==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/core" "^1.3.1"
|
||||||
|
|
||||||
|
"@floating-ui/react-dom@^2.0.0":
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.1.tgz#7972a4fc488a8c746cded3cfe603b6057c308a91"
|
||||||
|
integrity sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/dom" "^1.3.0"
|
||||||
|
|
||||||
"@fortawesome/fontawesome-common-types@6.4.0":
|
"@fortawesome/fontawesome-common-types@6.4.0":
|
||||||
version "6.4.0"
|
version "6.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz#88da2b70d6ca18aaa6ed3687832e11f39e80624b"
|
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz#88da2b70d6ca18aaa6ed3687832e11f39e80624b"
|
||||||
|
@ -3992,6 +4023,261 @@
|
||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
|
||||||
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
||||||
|
|
||||||
|
"@radix-ui/primitive@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
|
||||||
|
integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-arrow@1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d"
|
||||||
|
integrity sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
|
||||||
|
"@radix-ui/react-collection@1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
|
||||||
|
integrity sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-context" "1.0.1"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-slot" "1.0.2"
|
||||||
|
|
||||||
|
"@radix-ui/react-compose-refs@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989"
|
||||||
|
integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-context-menu@^2.0.1":
|
||||||
|
version "2.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-2.1.4.tgz#250420d259d3cebe026b7658414c516a1204de3f"
|
||||||
|
integrity sha512-HVHLUtZOBiR2Fh5l07qQ9y0IgX4dGZF0S9Gwdk4CVA+DL9afSphvFNa4nRiw6RNgb6quwLV4dLPF/gFDvNaOcQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/primitive" "1.0.1"
|
||||||
|
"@radix-ui/react-context" "1.0.1"
|
||||||
|
"@radix-ui/react-menu" "2.0.5"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-context@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
|
||||||
|
integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-direction@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
|
||||||
|
integrity sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-dismissable-layer@1.0.4":
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz#883a48f5f938fa679427aa17fcba70c5494c6978"
|
||||||
|
integrity sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/primitive" "1.0.1"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
"@radix-ui/react-use-escape-keydown" "1.0.3"
|
||||||
|
|
||||||
|
"@radix-ui/react-dropdown-menu@^2.0.1":
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.5.tgz#19bf4de8ffa348b4eb6a86842f14eff93d741170"
|
||||||
|
integrity sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/primitive" "1.0.1"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-context" "1.0.1"
|
||||||
|
"@radix-ui/react-id" "1.0.1"
|
||||||
|
"@radix-ui/react-menu" "2.0.5"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-focus-guards@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz#1ea7e32092216b946397866199d892f71f7f98ad"
|
||||||
|
integrity sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-focus-scope@1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz#9c2e8d4ed1189a1d419ee61edd5c1828726472f9"
|
||||||
|
integrity sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-id@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0"
|
||||||
|
integrity sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-menu@2.0.5":
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.0.5.tgz#a7d78b0808c4d38269240bf5d5c7ffea3e225e16"
|
||||||
|
integrity sha512-Gw4f9pwdH+w5w+49k0gLjN0PfRDHvxmAgG16AbyJZ7zhwZ6PBHKtWohvnSwfusfnK3L68dpBREHpVkj8wEM7ZA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/primitive" "1.0.1"
|
||||||
|
"@radix-ui/react-collection" "1.0.3"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-context" "1.0.1"
|
||||||
|
"@radix-ui/react-direction" "1.0.1"
|
||||||
|
"@radix-ui/react-dismissable-layer" "1.0.4"
|
||||||
|
"@radix-ui/react-focus-guards" "1.0.1"
|
||||||
|
"@radix-ui/react-focus-scope" "1.0.3"
|
||||||
|
"@radix-ui/react-id" "1.0.1"
|
||||||
|
"@radix-ui/react-popper" "1.1.2"
|
||||||
|
"@radix-ui/react-portal" "1.0.3"
|
||||||
|
"@radix-ui/react-presence" "1.0.1"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-roving-focus" "1.0.4"
|
||||||
|
"@radix-ui/react-slot" "1.0.2"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
aria-hidden "^1.1.1"
|
||||||
|
react-remove-scroll "2.5.5"
|
||||||
|
|
||||||
|
"@radix-ui/react-popper@1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.2.tgz#4c0b96fcd188dc1f334e02dba2d538973ad842e9"
|
||||||
|
integrity sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@floating-ui/react-dom" "^2.0.0"
|
||||||
|
"@radix-ui/react-arrow" "1.0.3"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-context" "1.0.1"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
"@radix-ui/react-use-rect" "1.0.1"
|
||||||
|
"@radix-ui/react-use-size" "1.0.1"
|
||||||
|
"@radix-ui/rect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-portal@1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.3.tgz#ffb961244c8ed1b46f039e6c215a6c4d9989bda1"
|
||||||
|
integrity sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
|
||||||
|
"@radix-ui/react-presence@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba"
|
||||||
|
integrity sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-primitive@1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0"
|
||||||
|
integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-slot" "1.0.2"
|
||||||
|
|
||||||
|
"@radix-ui/react-roving-focus@1.0.4":
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974"
|
||||||
|
integrity sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/primitive" "1.0.1"
|
||||||
|
"@radix-ui/react-collection" "1.0.3"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-context" "1.0.1"
|
||||||
|
"@radix-ui/react-direction" "1.0.1"
|
||||||
|
"@radix-ui/react-id" "1.0.1"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-slot@1.0.2":
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"
|
||||||
|
integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-callback-ref@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"
|
||||||
|
integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-controllable-state@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286"
|
||||||
|
integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-escape-keydown@1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz#217b840c250541609c66f67ed7bab2b733620755"
|
||||||
|
integrity sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-layout-effect@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399"
|
||||||
|
integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-rect@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz#fde50b3bb9fd08f4a1cd204572e5943c244fcec2"
|
||||||
|
integrity sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/rect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-size@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
|
||||||
|
integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/rect@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.1.tgz#bf8e7d947671996da2e30f4904ece343bc4a883f"
|
||||||
|
integrity sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
"@react-native-async-storage/async-storage@^1.15.15", "@react-native-async-storage/async-storage@^1.17.6":
|
"@react-native-async-storage/async-storage@^1.15.15", "@react-native-async-storage/async-storage@^1.17.6":
|
||||||
version "1.18.2"
|
version "1.18.2"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.18.2.tgz#ec8fd487a0b6c9500b43ece4b8779d1561f12e91"
|
resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.18.2.tgz#ec8fd487a0b6c9500b43ece4b8779d1561f12e91"
|
||||||
|
@ -4219,6 +4505,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz#9e558170c106bbafaa1ef502bd8e6d4651012bf9"
|
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz#9e558170c106bbafaa1ef502bd8e6d4651012bf9"
|
||||||
integrity sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==
|
integrity sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==
|
||||||
|
|
||||||
|
"@react-native-menu/menu@^0.8.0":
|
||||||
|
version "0.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-native-menu/menu/-/menu-0.8.0.tgz#dbf227c2081e5ffd3d2073ee68ecc84cf8639727"
|
||||||
|
integrity sha512-kxiT6ySZsDbBvNWovrKVAfs4AQvAytKIf0f8KQLkVO6eNYMUmONBQPzi6onTTbVujXtZHambo7qr/PcedaR8Tg==
|
||||||
|
|
||||||
"@react-native/assets@1.0.0":
|
"@react-native/assets@1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"
|
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"
|
||||||
|
@ -6850,6 +7141,13 @@ argparse@^2.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||||
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||||
|
|
||||||
|
aria-hidden@^1.1.1:
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954"
|
||||||
|
integrity sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
aria-query@^5.1.3:
|
aria-query@^5.1.3:
|
||||||
version "5.3.0"
|
version "5.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
|
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
|
||||||
|
@ -8978,6 +9276,11 @@ detect-newline@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
||||||
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
||||||
|
|
||||||
|
detect-node-es@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
|
||||||
|
integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
|
||||||
|
|
||||||
detect-node@^2.0.4:
|
detect-node@^2.0.4:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
|
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
|
||||||
|
@ -10819,6 +11122,11 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@
|
||||||
has-proto "^1.0.1"
|
has-proto "^1.0.1"
|
||||||
has-symbols "^1.0.3"
|
has-symbols "^1.0.3"
|
||||||
|
|
||||||
|
get-nonce@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
|
||||||
|
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
|
||||||
|
|
||||||
get-own-enumerable-property-symbols@^3.0.0:
|
get-own-enumerable-property-symbols@^3.0.0:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
|
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
|
||||||
|
@ -16750,6 +17058,13 @@ react-native-inappbrowser-reborn@^3.6.3:
|
||||||
invariant "^2.2.4"
|
invariant "^2.2.4"
|
||||||
opencollective-postinstall "^2.0.3"
|
opencollective-postinstall "^2.0.3"
|
||||||
|
|
||||||
|
react-native-ios-context-menu@^1.15.3:
|
||||||
|
version "1.15.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-ios-context-menu/-/react-native-ios-context-menu-1.15.3.tgz#c02e6a7af2df8c08d0b3e1c8f3395484b3c9c760"
|
||||||
|
integrity sha512-UNkVl7ocvSpNaEpvBvE1aHOfDy/DFdZ5I+ElfnTXFsRxrVZmxLtST0b1q2wSWGWDmd2Ig2AYd7GRbYtcY222Ag==
|
||||||
|
dependencies:
|
||||||
|
"@dominicstop/ts-event-emitter" "^1.1.0"
|
||||||
|
|
||||||
react-native-linear-gradient@^2.6.2:
|
react-native-linear-gradient@^2.6.2:
|
||||||
version "2.7.3"
|
version "2.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.7.3.tgz#f77b71ed7c955e033f9cba5fc8478df57953eb27"
|
resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.7.3.tgz#f77b71ed7c955e033f9cba5fc8478df57953eb27"
|
||||||
|
@ -16892,6 +17207,25 @@ react-refresh@^0.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.3.tgz#966f1750c191672e76e16c2efa569150cc73ab53"
|
||||||
integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==
|
integrity sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==
|
||||||
|
|
||||||
|
react-remove-scroll-bar@^2.3.3:
|
||||||
|
version "2.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9"
|
||||||
|
integrity sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==
|
||||||
|
dependencies:
|
||||||
|
react-style-singleton "^2.2.1"
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
|
react-remove-scroll@2.5.5:
|
||||||
|
version "2.5.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz#1e31a1260df08887a8a0e46d09271b52b3a37e77"
|
||||||
|
integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==
|
||||||
|
dependencies:
|
||||||
|
react-remove-scroll-bar "^2.3.3"
|
||||||
|
react-style-singleton "^2.2.1"
|
||||||
|
tslib "^2.1.0"
|
||||||
|
use-callback-ref "^1.3.0"
|
||||||
|
use-sidecar "^1.1.2"
|
||||||
|
|
||||||
react-responsive@^9.0.2:
|
react-responsive@^9.0.2:
|
||||||
version "9.0.2"
|
version "9.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-responsive/-/react-responsive-9.0.2.tgz#34531ca77a61e7a8775714016d21241df7e4205c"
|
resolved "https://registry.yarnpkg.com/react-responsive/-/react-responsive-9.0.2.tgz#34531ca77a61e7a8775714016d21241df7e4205c"
|
||||||
|
@ -16965,6 +17299,15 @@ react-shallow-renderer@^16.15.0:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
|
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
|
||||||
|
react-style-singleton@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4"
|
||||||
|
integrity sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==
|
||||||
|
dependencies:
|
||||||
|
get-nonce "^1.0.0"
|
||||||
|
invariant "^2.2.4"
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
react-test-renderer@18.2.0:
|
react-test-renderer@18.2.0:
|
||||||
version "18.2.0"
|
version "18.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
|
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
|
||||||
|
@ -17758,6 +18101,11 @@ setprototypeof@1.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||||
|
|
||||||
|
sf-symbols-typescript@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sf-symbols-typescript/-/sf-symbols-typescript-1.0.0.tgz#94e9210bf27e7583f9749a0d07bd4f4937ea488f"
|
||||||
|
integrity sha512-DkS7q3nN68dEMb4E18HFPDAvyrjDZK9YAQQF2QxeFu9gp2xRDXFMF8qLJ1EmQ/qeEGQmop4lmMM1WtYJTIcCMw==
|
||||||
|
|
||||||
shallow-clone@^3.0.0:
|
shallow-clone@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
||||||
|
@ -18966,7 +19314,7 @@ tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||||
|
|
||||||
tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0:
|
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0:
|
||||||
version "2.6.0"
|
version "2.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
|
||||||
integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==
|
integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==
|
||||||
|
@ -19289,11 +19637,26 @@ url-parse@^1.5.3, url-parse@^1.5.9:
|
||||||
querystringify "^2.1.1"
|
querystringify "^2.1.1"
|
||||||
requires-port "^1.0.0"
|
requires-port "^1.0.0"
|
||||||
|
|
||||||
|
use-callback-ref@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5"
|
||||||
|
integrity sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
use-latest-callback@^0.1.5:
|
use-latest-callback@^0.1.5:
|
||||||
version "0.1.6"
|
version "0.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.1.6.tgz#3fa6e7babbb5f9bfa24b5094b22939e1e92ebcf6"
|
resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.1.6.tgz#3fa6e7babbb5f9bfa24b5094b22939e1e92ebcf6"
|
||||||
integrity sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==
|
integrity sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==
|
||||||
|
|
||||||
|
use-sidecar@^1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"
|
||||||
|
integrity sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==
|
||||||
|
dependencies:
|
||||||
|
detect-node-es "^1.1.0"
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
use-sync-external-store@^1.0.0:
|
use-sync-external-store@^1.0.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||||
|
@ -20202,6 +20565,15 @@ yocto-queue@^0.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||||
|
|
||||||
|
zeego@^1.6.2:
|
||||||
|
version "1.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/zeego/-/zeego-1.6.2.tgz#6051ecc99cd82ced2f49ab14b167398323f8618c"
|
||||||
|
integrity sha512-6SSKzW69Z0Px2v3kF5lsoVZeBOLf22Xl38XLsvIrdQS2Mq9WZOrEvLU4JA6pxagKw54f6LLHyqtiWAAM9U/9pg==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-context-menu" "^2.0.1"
|
||||||
|
"@radix-ui/react-dropdown-menu" "^2.0.1"
|
||||||
|
sf-symbols-typescript "^1.0.0"
|
||||||
|
|
||||||
zod@^3.14.2, zod@^3.20.2, zod@^3.21.4:
|
zod@^3.14.2, zod@^3.20.2, zod@^3.21.4:
|
||||||
version "3.21.4"
|
version "3.21.4"
|
||||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
|
||||||
|
|
Loading…
Reference in New Issue