diff --git a/Dockerfile b/Dockerfile index fcd2413c..3ad05b6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN \. "$NVM_DIR/nvm.sh" && \ nvm use $NODE_VERSION && \ npm install --global yarn && \ yarn && \ - yarn intl:compile && \ + yarn intl:build && \ yarn build-web # DEBUG diff --git a/app.config.js b/app.config.js index 530e07b9..2552d348 100644 --- a/app.config.js +++ b/app.config.js @@ -153,10 +153,11 @@ module.exports = function (config) { 'expo-notifications', { icon: './assets/icon-android-notification.png', - color: '#ffffff', + color: '#1185fe', }, ], './plugins/withAndroidManifestPlugin.js', + './plugins/withAndroidManifestFCMIconPlugin.js', './plugins/withAndroidStylesWindowBackgroundPlugin.js', './plugins/shareExtension/withShareExtensions.js', ].filter(Boolean), diff --git a/bskyweb/cmd/bskyweb/mailmodo.go b/bskyweb/cmd/bskyweb/mailmodo.go deleted file mode 100644 index e892971f..00000000 --- a/bskyweb/cmd/bskyweb/mailmodo.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/json" - "fmt" - "net/http" - "time" -) - -type Mailmodo struct { - httpClient *http.Client - APIKey string - BaseURL string - ListName string -} - -func NewMailmodo(apiKey, listName string) *Mailmodo { - return &Mailmodo{ - APIKey: apiKey, - BaseURL: "https://api.mailmodo.com/api/v1", - httpClient: &http.Client{}, - ListName: listName, - } -} - -func (m *Mailmodo) request(ctx context.Context, httpMethod string, apiMethod string, data any) error { - endpoint := fmt.Sprintf("%s/%s", m.BaseURL, apiMethod) - js, err := json.Marshal(data) - if err != nil { - return fmt.Errorf("Mailmodo JSON encoding failed: %w", err) - } - req, err := http.NewRequestWithContext(ctx, httpMethod, endpoint, bytes.NewBuffer(js)) - if err != nil { - return fmt.Errorf("Mailmodo HTTP creating request %s %s failed: %w", httpMethod, apiMethod, err) - } - req.Header.Set("mmApiKey", m.APIKey) - req.Header.Set("Content-Type", "application/json") - - res, err := m.httpClient.Do(req) - if err != nil { - return fmt.Errorf("Mailmodo HTTP making request %s %s failed: %w", httpMethod, apiMethod, err) - } - defer res.Body.Close() - - status := struct { - Success bool `json:"success"` - Message string `json:"message"` - }{} - if err := json.NewDecoder(res.Body).Decode(&status); err != nil { - return fmt.Errorf("Mailmodo HTTP parsing response %s %s failed: %w", httpMethod, apiMethod, err) - } - if !status.Success { - return fmt.Errorf("Mailmodo API response %s %s failed: %s", httpMethod, apiMethod, status.Message) - } - return nil -} - -func (m *Mailmodo) AddToList(ctx context.Context, email string) error { - return m.request(ctx, "POST", "addToList", map[string]any{ - "listName": m.ListName, - "email": email, - "data": map[string]any{ - "email_hashed": fmt.Sprintf("%x", sha256.Sum256([]byte(email))), - }, - "created_at": time.Now().UTC().Format(time.RFC3339), - }) -} diff --git a/bskyweb/cmd/bskyweb/main.go b/bskyweb/cmd/bskyweb/main.go index a2952cae..5185ff57 100644 --- a/bskyweb/cmd/bskyweb/main.go +++ b/bskyweb/cmd/bskyweb/main.go @@ -40,18 +40,6 @@ func run(args []string) { // retain old PDS env var for easy transition EnvVars: []string{"ATP_APPVIEW_HOST", "ATP_PDS_HOST"}, }, - &cli.StringFlag{ - Name: "mailmodo-api-key", - Usage: "Mailmodo API key", - Required: false, - EnvVars: []string{"MAILMODO_API_KEY"}, - }, - &cli.StringFlag{ - Name: "mailmodo-list-name", - Usage: "Mailmodo contact list to add email addresses to", - Required: false, - EnvVars: []string{"MAILMODO_LIST_NAME"}, - }, &cli.StringFlag{ Name: "http-address", Usage: "Specify the local IP/port to bind to", diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 6b76acc9..e159d780 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -2,11 +2,9 @@ package main import ( "context" - "encoding/json" "errors" "fmt" "io/fs" - "io/ioutil" "net/http" "os" "os/signal" @@ -29,25 +27,19 @@ import ( ) type Server struct { - echo *echo.Echo - httpd *http.Server - mailmodo *Mailmodo - xrpcc *xrpc.Client + echo *echo.Echo + httpd *http.Server + xrpcc *xrpc.Client } func serve(cctx *cli.Context) error { debug := cctx.Bool("debug") httpAddress := cctx.String("http-address") appviewHost := cctx.String("appview-host") - mailmodoAPIKey := cctx.String("mailmodo-api-key") - mailmodoListName := cctx.String("mailmodo-list-name") // Echo e := echo.New() - // Mailmodo client. - mailmodo := NewMailmodo(mailmodoAPIKey, mailmodoListName) - // create a new session (no auth) xrpcc := &xrpc.Client{ Client: cliutil.NewHttpClient(), @@ -77,9 +69,8 @@ func serve(cctx *cli.Context) error { // server // server := &Server{ - echo: e, - mailmodo: mailmodo, - xrpcc: xrpcc, + echo: e, + xrpcc: xrpcc, } // Create the HTTP server. @@ -221,9 +212,6 @@ func serve(cctx *cli.Context) error { e.GET("/profile/:handleOrDID/post/:rkey/liked-by", server.WebGeneric) e.GET("/profile/:handleOrDID/post/:rkey/reposted-by", server.WebGeneric) - // Mailmodo - e.POST("/api/waitlist", server.apiWaitlist) - // Start the server. log.Infof("starting server address=%s", httpAddress) go func() { @@ -398,36 +386,3 @@ func (srv *Server) WebProfile(c echo.Context) error { data["requestHost"] = req.Host return c.Render(http.StatusOK, "profile.html", data) } - -func (srv *Server) apiWaitlist(c echo.Context) error { - type jsonError struct { - Error string `json:"error"` - } - - // Read the API request. - type apiRequest struct { - Email string `json:"email"` - } - - bodyReader := http.MaxBytesReader(c.Response(), c.Request().Body, 16*1024) - payload, err := ioutil.ReadAll(bodyReader) - if err != nil { - return err - } - var req apiRequest - if err := json.Unmarshal(payload, &req); err != nil { - return c.JSON(http.StatusBadRequest, jsonError{Error: "Invalid API request"}) - } - - if req.Email == "" { - return c.JSON(http.StatusBadRequest, jsonError{Error: "Please enter a valid email address."}) - } - - if err := srv.mailmodo.AddToList(c.Request().Context(), req.Email); err != nil { - log.Errorf("adding email to waitlist failed: %s", err) - return c.JSON(http.StatusBadRequest, jsonError{ - Error: "Storing email in waitlist failed. Please enter a valid email address.", - }) - } - return c.JSON(http.StatusOK, map[string]bool{"success": true}) -} diff --git a/package.json b/package.json index 59ee3319..2571fe77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bsky.app", - "version": "1.71.0", + "version": "1.72.0", "private": true, "engines": { "node": ">=18" @@ -44,7 +44,7 @@ "update-extensions": "scripts/updateExtensions.sh" }, "dependencies": { - "@atproto/api": "^0.10.4", + "@atproto/api": "^0.10.5", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@emoji-mart/react": "^1.1.1", @@ -63,7 +63,6 @@ "@react-native-camera-roll/camera-roll": "^5.2.2", "@react-native-clipboard/clipboard": "^1.10.0", "@react-native-community/blur": "^4.3.0", - "@react-native-community/datetimepicker": "7.6.1", "@react-native-masked-view/masked-view": "0.3.0", "@react-native-menu/menu": "^0.8.0", "@react-native-picker/picker": "2.6.1", @@ -152,6 +151,7 @@ "react-keyed-flatten-children": "^3.0.0", "react-native": "0.73.2", "react-native-appstate-hook": "^1.0.6", + "react-native-date-picker": "^4.4.0", "react-native-drawer-layout": "^4.0.0-alpha.3", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.14.0", diff --git a/plugins/withAndroidManifestFCMIconPlugin.js b/plugins/withAndroidManifestFCMIconPlugin.js new file mode 100644 index 00000000..066a975d --- /dev/null +++ b/plugins/withAndroidManifestFCMIconPlugin.js @@ -0,0 +1,37 @@ +const {withAndroidManifest} = require('expo/config-plugins') + +module.exports = function withAndroidManifestFCMIconPlugin(appConfig) { + return withAndroidManifest(appConfig, function (decoratedAppConfig) { + try { + function addOrModifyMetaData(metaData, name, resource) { + const elem = metaData.find(elem => elem.$['android:name'] === name) + if (elem === undefined) { + metaData.push({ + $: { + 'android:name': name, + 'android:resource': resource, + }, + }) + } else { + elem.$['android:resource'] = resource + } + } + const androidManifest = decoratedAppConfig.modResults.manifest + const metaData = androidManifest.application[0]['meta-data'] + addOrModifyMetaData( + metaData, + 'com.google.firebase.messaging.default_notification_color', + '@color/notification_icon_color', + ) + addOrModifyMetaData( + metaData, + 'com.google.firebase.messaging.default_notification_icon', + '@drawable/notification_icon', + ) + return decoratedAppConfig + } catch (e) { + console.error(`withAndroidManifestFCMIconPlugin failed`, e) + } + return decoratedAppConfig + }) +} diff --git a/src/components/Lists.tsx b/src/components/Lists.tsx index 9778545d..c879f941 100644 --- a/src/components/Lists.tsx +++ b/src/components/Lists.tsx @@ -1,6 +1,7 @@ import React from 'react' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {View} from 'react-native' +import {CenteredView} from 'view/com/util/Views' import {Loader} from '#/components/Loader' import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -145,7 +146,7 @@ export function ListMaybePlaceholder({ }) { const navigation = useNavigation() const t = useTheme() - const {gtMobile} = useBreakpoints() + const {gtMobile, gtTablet} = useBreakpoints() const {_} = useLingui() const canGoBack = navigation.canGoBack() @@ -168,14 +169,16 @@ export function ListMaybePlaceholder({ if (!isEmpty) return null return ( - + ]} + sideBorders={gtMobile} + topBorder={!gtTablet}> {isLoading ? ( @@ -244,6 +247,6 @@ export function ListMaybePlaceholder({ )} - + ) } diff --git a/src/components/forms/DateField/index.android.tsx b/src/components/forms/DateField/index.android.tsx index cddb643d..451810a5 100644 --- a/src/components/forms/DateField/index.android.tsx +++ b/src/components/forms/DateField/index.android.tsx @@ -1,8 +1,5 @@ import React from 'react' import {View, Pressable} from 'react-native' -import DateTimePicker, { - BaseProps as DateTimePickerProps, -} from '@react-native-community/datetimepicker' import {useTheme, atoms} from '#/alf' import {Text} from '#/components/Typography' @@ -15,6 +12,8 @@ import { localizeDate, toSimpleDateString, } from '#/components/forms/DateField/utils' +import DatePicker from 'react-native-date-picker' +import {isAndroid} from 'platform/detection' export * as utils from '#/components/forms/DateField/utils' export const Label = TextField.Label @@ -38,20 +37,20 @@ export function DateField({ const {chromeFocus, chromeError, chromeErrorHover} = TextField.useSharedInputStyles() - const onChangeInternal = React.useCallback< - Required['onChange'] - >( - (_event, date) => { + const onChangeInternal = React.useCallback( + (date: Date) => { setOpen(false) - if (date) { - const formatted = toSimpleDateString(date) - onChangeDate(formatted) - } + const formatted = toSimpleDateString(date) + onChangeDate(formatted) }, [onChangeDate, setOpen], ) + const onCancel = React.useCallback(() => { + setOpen(false) + }, []) + return ( {open && ( - )} diff --git a/src/components/forms/DateField/index.tsx b/src/components/forms/DateField/index.tsx index e65936e0..49e47a01 100644 --- a/src/components/forms/DateField/index.tsx +++ b/src/components/forms/DateField/index.tsx @@ -1,13 +1,11 @@ import React from 'react' import {View} from 'react-native' -import DateTimePicker, { - DateTimePickerEvent, -} from '@react-native-community/datetimepicker' import {useTheme, atoms} from '#/alf' import * as TextField from '#/components/forms/TextField' import {toSimpleDateString} from '#/components/forms/DateField/utils' import {DateFieldProps} from '#/components/forms/DateField/types' +import DatePicker from 'react-native-date-picker' export * as utils from '#/components/forms/DateField/utils' export const Label = TextField.Label @@ -28,7 +26,7 @@ export function DateField({ const t = useTheme() const onChangeInternal = React.useCallback( - (event: DateTimePickerEvent, date: Date | undefined) => { + (date: Date | undefined) => { if (date) { const formatted = toSimpleDateString(date) onChangeDate(formatted) @@ -39,17 +37,15 @@ export function DateField({ return ( - ) diff --git a/src/lib/hooks/useInitialNumToRender.ts b/src/lib/hooks/useInitialNumToRender.ts new file mode 100644 index 00000000..942f0404 --- /dev/null +++ b/src/lib/hooks/useInitialNumToRender.ts @@ -0,0 +1,11 @@ +import React from 'react' +import {Dimensions} from 'react-native' + +const MIN_POST_HEIGHT = 100 + +export function useInitialNumToRender(minItemHeight: number = MIN_POST_HEIGHT) { + return React.useMemo(() => { + const screenHeight = Dimensions.get('window').height + return Math.ceil(screenHeight / minItemHeight) + 1 + }, [minItemHeight]) +} diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts index ba2cdb39..7729e4a3 100644 --- a/src/lib/strings/url-helpers.ts +++ b/src/lib/strings/url-helpers.ts @@ -148,6 +148,11 @@ export function feedUriToHref(url: string): string { export function linkRequiresWarning(uri: string, label: string) { const labelDomain = labelToDomain(label) + // If the uri started with a / we know it is internal. + if (uri.startsWith('/')) { + return false + } + let urip try { urip = new URL(uri) @@ -156,9 +161,12 @@ export function linkRequiresWarning(uri: string, label: string) { } const host = urip.hostname.toLowerCase() - // Hosts that end with bsky.app or bsky.social should be trusted by default. - if (host.endsWith('bsky.app') || host.endsWith('bsky.social')) { + if ( + host.endsWith('bsky.app') || + host.endsWith('bsky.social') || + host.endsWith('blueskyweb.xyz') + ) { // if this is a link to internal content, // warn if it represents itself as a URL to another app return !!labelDomain && labelDomain !== host && isPossiblyAUrl(labelDomain) diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx index 09a1f282..f1b81737 100644 --- a/src/screens/Hashtag.tsx +++ b/src/screens/Hashtag.tsx @@ -1,6 +1,5 @@ import React from 'react' import {ListRenderItemInfo, Pressable} from 'react-native' -import {atoms as a, useBreakpoints} from '#/alf' import {useFocusEffect} from '@react-navigation/native' import {useSetMinimalShellMode} from 'state/shell' import {ViewHeader} from 'view/com/util/ViewHeader' @@ -19,11 +18,11 @@ import {List} from 'view/com/util/List' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {sanitizeHandle} from 'lib/strings/handles' -import {CenteredView} from 'view/com/util/Views' import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox' import {shareUrl} from 'lib/sharing' import {HITSLOP_10} from 'lib/constants' import {isNative} from 'platform/detection' +import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' const renderItem = ({item}: ListRenderItemInfo) => { return @@ -38,8 +37,8 @@ export default function HashtagScreen({ }: NativeStackScreenProps) { const {tag, author} = route.params const setMinimalShellMode = useSetMinimalShellMode() - const {gtMobile} = useBreakpoints() const {_} = useLingui() + const initialNumToRender = useInitialNumToRender() const [isPTR, setIsPTR] = React.useState(false) const fullTag = React.useMemo(() => { @@ -103,7 +102,7 @@ export default function HashtagScreen({ }, [isFetching, hasNextPage, error, fetchNextPage]) return ( - + <> } + initialNumToRender={initialNumToRender} + windowSize={11} /> )} - + ) } diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts index 25d28499..53c9e482 100644 --- a/src/state/queries/preferences/const.ts +++ b/src/state/queries/preferences/const.ts @@ -7,7 +7,7 @@ import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/ export const DEFAULT_HOME_FEED_PREFS: UsePreferencesQueryResponse['feedViewPrefs'] = { hideReplies: false, - hideRepliesByUnfollowed: false, + hideRepliesByUnfollowed: true, hideRepliesByLikeCount: 0, hideReposts: false, hideQuotePosts: false, diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index 07198de7..37ef10ae 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -169,7 +169,7 @@ export function usePreferencesSetBirthDateMutation() { return useMutation({ mutationFn: async ({birthDate}: {birthDate: Date}) => { - await getAgent().setPersonalDetails({birthDate}) + await getAgent().setPersonalDetails({birthDate: birthDate.toISOString()}) // triggers a refetch await queryClient.invalidateQueries({ queryKey: preferencesQueryKey, diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 54d8aa22..cd3e9878 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -32,6 +32,7 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {DiscoverFallbackHeader} from './DiscoverFallbackHeader' import {FALLBACK_MARKER_POST} from '#/lib/api/feed/home' +import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' const LOADING_ITEM = {_reactKey: '__loading__'} const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} @@ -84,6 +85,7 @@ let Feed = ({ const {_} = useLingui() const queryClient = useQueryClient() const {currentAccount} = useSession() + const initialNumToRender = useInitialNumToRender() const [isPTRing, setIsPTRing] = React.useState(false) const checkForNewRef = React.useRef<(() => void) | null>(null) const lastFetchRef = React.useRef(Date.now()) @@ -327,6 +329,8 @@ let Feed = ({ desktopFixedHeight={ desktopFixedHeightOffset ? desktopFixedHeightOffset : true } + initialNumToRender={initialNumToRender} + windowSize={11} /> ) diff --git a/src/view/com/util/Views.d.ts b/src/view/com/util/Views.d.ts index 6a90cc22..16713921 100644 --- a/src/view/com/util/Views.d.ts +++ b/src/view/com/util/Views.d.ts @@ -5,4 +5,6 @@ export function CenteredView({ style, sideBorders, ...props -}: React.PropsWithChildren) +}: React.PropsWithChildren< + ViewProps & {sideBorders?: boolean; topBorder?: boolean} +>) diff --git a/src/view/com/util/Views.web.tsx b/src/view/com/util/Views.web.tsx index db3b9de0..ae165077 100644 --- a/src/view/com/util/Views.web.tsx +++ b/src/view/com/util/Views.web.tsx @@ -32,8 +32,11 @@ interface AddedProps { export function CenteredView({ style, sideBorders, + topBorder, ...props -}: React.PropsWithChildren) { +}: React.PropsWithChildren< + ViewProps & {sideBorders?: boolean; topBorder?: boolean} +>) { const pal = usePalette('default') const {isMobile} = useWebMediaQueries() if (!isMobile) { @@ -46,6 +49,12 @@ export function CenteredView({ }) style = addStyle(style, pal.border) } + if (topBorder) { + style = addStyle(style, { + borderTopWidth: 1, + }) + style = addStyle(style, pal.border) + } return } diff --git a/src/view/com/util/forms/DateInput.tsx b/src/view/com/util/forms/DateInput.tsx index c5f0afc8..0104562a 100644 --- a/src/view/com/util/forms/DateInput.tsx +++ b/src/view/com/util/forms/DateInput.tsx @@ -1,8 +1,5 @@ import React, {useState, useCallback} from 'react' import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native' -import DateTimePicker, { - DateTimePickerEvent, -} from '@react-native-community/datetimepicker' import { FontAwesomeIcon, FontAwesomeIconStyle, @@ -14,6 +11,7 @@ import {TypographyVariant} from 'lib/ThemeContext' import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' import {getLocales} from 'expo-localization' +import DatePicker from 'react-native-date-picker' const LOCALE = getLocales()[0] @@ -43,11 +41,9 @@ export function DateInput(props: Props) { }, [props.handleAsUTC]) const onChangeInternal = useCallback( - (event: DateTimePickerEvent, date: Date | undefined) => { + (date: Date) => { setShow(false) - if (date) { - props.onChange(date) - } + props.onChange(date) }, [setShow, props], ) @@ -56,6 +52,10 @@ export function DateInput(props: Props) { setShow(true) }, [setShow]) + const onCancel = useCallback(() => { + setShow(false) + }, []) + return ( {isAndroid && ( @@ -80,15 +80,17 @@ export function DateInput(props: Props) { )} {(isIOS || show) && ( - } label={_(msg`Moderation`)} diff --git a/yarn.lock b/yarn.lock index 3add1af6..386bc1a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,10 +34,10 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@atproto/api@^0.10.4": - version "0.10.4" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.10.4.tgz#b73446f2344783c42c6040082756449443f15750" - integrity sha512-9gwZt4v4pngfD4mgsET9i9Ym0PpMSzftTzqBjCbFpObx15zMkFemYnLUnyT/NEww2u/aRxjAe2TeBnU0dIbbuQ== +"@atproto/api@^0.10.5": + version "0.10.5" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.10.5.tgz#e778e2843d08690df8df81f24028a7578e9b3cb4" + integrity sha512-GYdST5sPKU2JnPmm8x3KqjOSlDiYXrp4GkW7bpQTVLPabnUNq5NLN6HJEoJABjjOAsaLF12rBoV+JpRb1UjNsQ== dependencies: "@atproto/common-web" "^0.2.3" "@atproto/lexicon" "^0.3.2" @@ -4986,13 +4986,6 @@ prompts "^2.4.2" semver "^7.5.2" -"@react-native-community/datetimepicker@7.6.1": - version "7.6.1" - resolved "https://registry.yarnpkg.com/@react-native-community/datetimepicker/-/datetimepicker-7.6.1.tgz#98bdee01e3df490526ee1125e438c2030becac1f" - integrity sha512-g66Q2Kd9Uw3eRL7kkrTsGhi+eXxNoPDRFYH6z78sZQuYjPkUQgJDDMUYgBmaBsQx/fKMtemPrCj1ulGmyi0OSw== - dependencies: - invariant "^2.2.4" - "@react-native-community/eslint-config@^3.0.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@react-native-community/eslint-config/-/eslint-config-3.2.0.tgz#42f677d5fff385bccf1be1d3b8faa8c086cf998d" @@ -18563,6 +18556,11 @@ react-native-appstate-hook@^1.0.6: resolved "https://registry.yarnpkg.com/react-native-appstate-hook/-/react-native-appstate-hook-1.0.6.tgz#cbc16e7b89cfaea034cabd999f00e99053cabd06" integrity sha512-0hPVyf5yLxCSVrrNEuGqN1ZnSSj3Ye2gZex0NtcK/AHYwMc0rXWFNZjBKOoZSouspqu3hXBbQ6NOUSTzrME1AQ== +react-native-date-picker@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/react-native-date-picker/-/react-native-date-picker-4.4.0.tgz#fe5b6eb8d85a4a30b2991ada5169a30ce5023ead" + integrity sha512-Axx3byihwwhKRLRVjPAr/UaEysapkRcKmjjM8/05UaVm4Q0xDn2RFUcRdy1QAahhRcjLjlVYhepxvU5bdgy7ZQ== + react-native-dotenv@^3.3.1: version "3.4.9" resolved "https://registry.yarnpkg.com/react-native-dotenv/-/react-native-dotenv-3.4.9.tgz#621c5b0c1d0c5c7f569bfe5a1d804bec7885c010"