From 3cf932f5bee217799bf8a7889b71748b4b124bf0 Mon Sep 17 00:00:00 2001 From: Tasker <26747046+tmgrask@users.noreply.github.com> Date: Mon, 7 Oct 2024 12:10:22 -0400 Subject: [PATCH] Fix duplicate splash screen warning from Play console (#15) * fix all tsserver diagnostics in project * move dots into their own component; undo metro config change * fix duplicate splash screen warning in play console --- android/app/src/main/res/values/styles.xml | 4 +- babel.config.js | 2 +- src/app/(app)/onboarding.tsx | 181 ++++++++++++--------- src/app/index.tsx | 6 +- src/components/GitHash.tsx | 2 +- src/components/canvas/Phone.tsx | 2 +- src/i18n/fake-translation.js | 4 +- src/i18n/i18n.test.ts | 3 +- src/i18n/pseudo.js | 18 +- 9 files changed, 125 insertions(+), 97 deletions(-) diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 912e38b..32c1447 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -13,5 +13,7 @@ - \ No newline at end of file + diff --git a/babel.config.js b/babel.config.js index 9d89e13..68405ea 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,4 +1,4 @@ -module.exports = function (api) { +export default function (api) { api.cache(true); return { presets: ['babel-preset-expo'], diff --git a/src/app/(app)/onboarding.tsx b/src/app/(app)/onboarding.tsx index 2dbc29c..6a133d3 100644 --- a/src/app/(app)/onboarding.tsx +++ b/src/app/(app)/onboarding.tsx @@ -27,6 +27,7 @@ import { GestureHandlerRootView, } from "react-native-gesture-handler"; import Animated, { + SharedValue, runOnJS, useDerivedValue, useSharedValue, @@ -216,19 +217,6 @@ export default function OnboardingScreen() { .build(); }); - const dot0Fill = useDerivedValue(() => { - return currentView.value >= 0 ? palette.blueTint2 : palette.transparent; - }); - const dot1Fill = useDerivedValue(() => { - return currentView.value >= 1 ? palette.blueTint2 : palette.transparent; - }); - const dot2Fill = useDerivedValue(() => { - return currentView.value >= 2 ? palette.blueTint2 : palette.transparent; - }); - const dot3Fill = useDerivedValue(() => { - return currentView.value >= 3 ? palette.blueTint2 : palette.transparent; - }); - const buttonP = useDerivedValue(() => { buttonTextChanged.value; if (!fontMgr) { @@ -258,10 +246,12 @@ export default function OnboardingScreen() { const backListener = BackHandler.addEventListener( "hardwareBackPress", () => { + // when this callback returns false, the hardware back is + // actuated, when it returns true the hardware back is ignored. if (currentView.value === 0) { return false; // allow hardware back from first view only } else { - return true; // disable hardware back, we'll handle the gesture + return true; } }, ); @@ -271,30 +261,32 @@ export default function OnboardingScreen() { }; }, []); + function replaceOrGoBack() { + // this will be called in an animation callback using runOnJS, need to + // encapsulate so we can consume the output of a synchronous function. + if (router.canGoBack()) { + router.back(); + } else { + router.replace("/(app)/"); + } + } + async function goToNext() { if (currentView.value < views.length - 1) { + // continue onboarding const beforeNext = views[currentView.value].beforeNext; if (beforeNext) { await beforeNext(); } currentView.value += 1; } else { - runOnJS(goToMainApp)(); - } - } - function replaceOrGoBack() { - if (router.canGoBack()) { - router.back(); - } else { - router.replace("/(app)/"); + // onboarding done, record completion and fade to main view + await AsyncStorage.setItem("hasOnboarded", "true"); + everythingOpacity.value = withTiming(0, { duration: 500 }, () => { + runOnJS(replaceOrGoBack)(); + }); } } - async function goToMainApp() { - everythingOpacity.value = withTiming(0, { duration: 500 }, () => { - runOnJS(replaceOrGoBack)(); - }); - await AsyncStorage.setItem("hasOnboarded", "true"); - } const buttonGesture = Gesture.Tap().onEnd(goToNext).runOnJS(true); @@ -397,57 +389,9 @@ export default function OnboardingScreen() { /> - - - - - - - - @@ -503,3 +447,82 @@ export default function OnboardingScreen() { ); } + +function ProgressDots({ + dotWidth, + currentView, +}: { + dotWidth: number; + currentView: SharedValue; +}) { + // Couldn't figure out a way to avoid hardcoding these + const dot0Fill = useDerivedValue(() => { + return currentView.value >= 0 ? palette.blueTint2 : palette.transparent; + }); + const dot1Fill = useDerivedValue(() => { + return currentView.value >= 1 ? palette.blueTint2 : palette.transparent; + }); + const dot2Fill = useDerivedValue(() => { + return currentView.value >= 2 ? palette.blueTint2 : palette.transparent; + }); + const dot3Fill = useDerivedValue(() => { + return currentView.value >= 3 ? palette.blueTint2 : palette.transparent; + }); + + return ( + + + + + + + + + + + ); +} diff --git a/src/app/index.tsx b/src/app/index.tsx index 48fa640..e0b5382 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,5 +1,7 @@ +import AsyncStorage from "@react-native-async-storage/async-storage"; import { Canvas } from "@shopify/react-native-skia"; import { router } from "expo-router"; +import * as SplashScreen from "expo-splash-screen"; import React from "react"; import { View, useWindowDimensions } from "react-native"; import { runOnJS, useSharedValue, withTiming } from "react-native-reanimated"; @@ -9,7 +11,8 @@ import { timedLog } from "@/src/common/utils"; import { SafeAreaView } from "@/src/components/SafeAreaView"; import { PsiphonConduitLoading } from "@/src/components/canvas/PsiphonConduitLoading"; import { sharedStyles as ss } from "@/src/styles"; -import AsyncStorage from "@react-native-async-storage/async-storage"; + +SplashScreen.preventAutoHideAsync(); export default function Index() { const { signIn } = useAuthContext(); @@ -20,6 +23,7 @@ export default function Index() { const opacity = useSharedValue(0); async function doSignIn() { + await SplashScreen.hideAsync(); timedLog("Starting signIn"); const signInResult = await signIn(); if (signInResult instanceof Error) { diff --git a/src/components/GitHash.tsx b/src/components/GitHash.tsx index 60c8f9a..0ae53fe 100644 --- a/src/components/GitHash.tsx +++ b/src/components/GitHash.tsx @@ -31,7 +31,7 @@ export function GitHash() { ss.bodyFont, ]} > - v.{GIT_HASH.substr(0, 12)} + v.{GIT_HASH.substring(0, 12)} ); diff --git a/src/components/canvas/Phone.tsx b/src/components/canvas/Phone.tsx index a5a5a11..a9700ea 100644 --- a/src/components/canvas/Phone.tsx +++ b/src/components/canvas/Phone.tsx @@ -16,6 +16,7 @@ import { import React from "react"; import { Easing, + SharedValue, cancelAnimation, useAnimatedReaction, useSharedValue, @@ -23,7 +24,6 @@ import { withRepeat, withTiming, } from "react-native-reanimated"; -import { SharedValue } from "react-native-reanimated/lib/typescript/Animated"; import { ConduitConnectionLight } from "@/src/components/canvas/ConduitConnectionLight"; diff --git a/src/i18n/fake-translation.js b/src/i18n/fake-translation.js index 0589c24..ac4d7af 100644 --- a/src/i18n/fake-translation.js +++ b/src/i18n/fake-translation.js @@ -1,6 +1,6 @@ "use strict"; -const fs = require("fs").promises; +import { promises as fs } from "fs"; const path = require("path"); const pseudo = require("./pseudo").PSEUDO; @@ -14,7 +14,7 @@ let englishLines, async function getEnglishLines() { const data = await fs.readFile( path.resolve(__dirname, "locales/en/translation.json"), - (err, data) => { + (err, _) => { if (err) throw err; }, ); diff --git a/src/i18n/i18n.test.ts b/src/i18n/i18n.test.ts index 772650c..e42e2e1 100644 --- a/src/i18n/i18n.test.ts +++ b/src/i18n/i18n.test.ts @@ -1,11 +1,10 @@ import * as fs from "fs"; import i18n from "i18next"; +import path from "path"; import { findBestLanguageTag } from "react-native-localize"; import i18nService from "./i18n"; -const path = require("path"); - jest.mock("react-native-localize", () => ({ findBestLanguageTag: jest.fn(() => ({ languageTag: "fr" })), })); diff --git a/src/i18n/pseudo.js b/src/i18n/pseudo.js index 0d2a658..f3d3b38 100644 --- a/src/i18n/pseudo.js +++ b/src/i18n/pseudo.js @@ -67,17 +67,17 @@ function mapContent(fn, val) { return modified.join(""); } -function Pseudo(id, name, charMap, modFn) { - this.id = id; - this.translate = mapContent.bind(null, function (val) { - return replaceChars(charMap, modFn(val)); - }); - this.name = this.translate(name); +class Pseudo { + constructor(id, name, charMap, modFn) { + this.id = id; + this.translate = mapContent.bind(null, function (val) { + return replaceChars(charMap, modFn(val)); + }); + this.name = this.translate(name); + } } -var PSEUDO = { +export const PSEUDO = { xa: new Pseudo("xa", "Packaged Accented", ACCENTED_MAP, makeLonger), xb: new Pseudo("xb", "Packaged Mirrored", FLIPPED_MAP, makeRTL), }; - -exports.PSEUDO = PSEUDO;