From f756bd1d82020a0ec124bc6ad1eec9fe9b1f2de7 Mon Sep 17 00:00:00 2001 From: Dominic Wrege Date: Tue, 23 Jul 2024 00:45:35 +0200 Subject: [PATCH 1/7] app anonymous metrics --- App.tsx | 2 +- functions/api.ts | 73 +++++++++++++++++++++++++++++++++++++--- functions/util.ts | 18 ++++++++-- hooks/fetchAppData.ts | 77 ++++++++++++++++++++++++++++++++++++------- state/state.ts | 1 + types/banner.ts | 8 +++++ types/metrics.ts | 16 +++++++++ 7 files changed, 175 insertions(+), 20 deletions(-) create mode 100644 types/metrics.ts diff --git a/App.tsx b/App.tsx index dae6c03..3130ee0 100755 --- a/App.tsx +++ b/App.tsx @@ -46,7 +46,7 @@ function AppWrapper(): JSX.Element { useEffect(() => { const subscription = AppState.addEventListener( "change", - onAppStateChange + onAppStateChange, ); return () => { diff --git a/functions/api.ts b/functions/api.ts index 1227f32..190b6d0 100644 --- a/functions/api.ts +++ b/functions/api.ts @@ -1,10 +1,11 @@ +import { Platform } from "react-native"; import { BannerData, ChargeConditionData } from "../state/state"; import { getBannerType, retrieveFromStorage, saveToStorage, } from "../state/storage"; -import { Banner, LadefuchsBanner } from "../types/banner"; +import { Banner, ImpressionRequest, LadefuchsBanner } from "../types/banner"; import { ChargeMode, ChargingCondition, @@ -14,7 +15,12 @@ import { import { FeedbackRequest } from "../types/feedback"; import { Operator, OperatorsResponse } from "../types/operator"; import { Tariff, TariffResponse } from "../types/tariff"; -import { fetchWithTimeout } from "./util"; +import { appVersionNumber, fetchWithTimeout, getMinutes } from "./util"; +import { + AppMetricCache, + AppMetricResponse, + AppMetricsRequest, +} from "../types/metrics"; const apiPath = "https://api.ladefuchs.app"; export const authHeader = { @@ -143,7 +149,6 @@ export async function fetchChargePriceAdBanner(): Promise { } } -// todo error handling, and forward errors and show in the UI export async function sendFeedback(request: FeedbackRequest): Promise { const response = await fetchWithTimeout(`${apiPath}/v3/feedback`, { method: "POST", @@ -154,7 +159,7 @@ export async function sendFeedback(request: FeedbackRequest): Promise { }, body: JSON.stringify(request), }); - if (response.status > 299) { + if (!response.ok) { throw Error("could not send feedback, got an bad status code"); } } @@ -164,6 +169,66 @@ const storageSet = { chargeConditionData: "chargeConditionData", }; +export async function postBannerImpression(banner: Banner): Promise { + if (!banner?.identifier) { + return; + } + const response = await fetchWithTimeout( + `${apiPath}/v3/banners/impression`, + { + method: "POST", + headers: { + ...authHeader.headers, + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + bannerId: banner.identifier, + platform: Platform.OS, + } satisfies ImpressionRequest), + }, + ); + if (!response.ok) { + throw new Error("Network response was not ok"); + } +} + +export async function postAppMetric(): Promise { + const cacheKey = "appMetric"; + const cache = await retrieveFromStorage(cacheKey); + + if (cache?.lastUpdated) { + const updatedDevice = Date.parse(cache?.lastUpdated); + const oneHourInMs = getMinutes(60); + if (Date.now() - updatedDevice < oneHourInMs) { + return; + } + } + + const response = await fetchWithTimeout(`${apiPath}/v3/app/metrics`, { + method: "POST", + headers: { + ...authHeader.headers, + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + deviceId: cache?.deviceId ?? null, + version: appVersionNumber(), + platform: Platform.OS, + } satisfies AppMetricsRequest), + }); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + const json: AppMetricResponse = await response.json(); + + await saveToStorage(cacheKey, { + deviceId: json.deviceId, + lastUpdated: new Date().toISOString(), + } satisfies AppMetricCache); +} + export async function getBanners({ writeToCache, }: { diff --git a/functions/util.ts b/functions/util.ts index 49d82fe..b8257ee 100644 --- a/functions/util.ts +++ b/functions/util.ts @@ -1,3 +1,15 @@ +import Constants from "expo-constants"; + +export const isDebug = __DEV__; + +export function getMinutes(minutes: number): number { + return minutes * 60 * 1000; +} + +export function appVersionNumber(): number { + return parseInt(Constants.expoConfig.version.replaceAll(".", "")); +} + export function fill(list1: T[], list2: T[]): [T[], T[]] { const len1 = list1.length; const len2 = list2.length; @@ -43,7 +55,7 @@ function compose(...functions: ((arg: T) => T)[]): (arg: T) => T { export const shuffleAndPickOne = compose( repeatItemsByFrequency, shuffle, - pickRandom + pickRandom, ); function repeatNTimes(element: T, times: number): T[] { @@ -51,7 +63,7 @@ function repeatNTimes(element: T, times: number): T[] { } export function repeatItemsByFrequency( - items: T[] + items: T[], ): T[] { return items.flatMap((item) => repeatNTimes(item, item.frequency)); } @@ -72,7 +84,7 @@ export function hyphenText(input: string): string { export async function fetchWithTimeout( url: string, options: RequestInit = null, - timeout = 2800 + timeout = 2700, ) { const controller = new AbortController(); options.signal = controller.signal; diff --git a/hooks/fetchAppData.ts b/hooks/fetchAppData.ts index c44aa28..71dcc53 100644 --- a/hooks/fetchAppData.ts +++ b/hooks/fetchAppData.ts @@ -1,20 +1,35 @@ -import { useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { useEffect } from "react"; -import { getAllChargeConditions, getBanners } from "../functions/api"; +import { + getAllChargeConditions, + getBanners, + postAppMetric, + postBannerImpression, +} from "../functions/api"; + import { useShallow } from "zustand/react/shallow"; import { useAppStore } from "../state/state"; +import { isDebug } from "../functions/util"; +import { AppState, AppStateStatus } from "react-native"; export function useFetchAppData(): void { - const [setAppData, setAppError, operators, setBanners, ladefuchsBanners] = - useAppStore( - useShallow((state) => [ - state.setChargeConditions, - state.setAppError, - state.operators, - state.setBanners, - state.ladefuchsBanners, - ]) - ); + const [ + setAppData, + setAppError, + operators, + setBanners, + ladefuchsBanners, + banner, + ] = useAppStore( + useShallow((state) => [ + state.setChargeConditions, + state.setAppError, + state.operators, + state.setBanners, + state.ladefuchsBanners, + state.banner, + ]), + ); const allChargeConditionsQuery = useQuery({ queryKey: ["appChargeConditions"], retry: 3, @@ -25,6 +40,21 @@ export function useFetchAppData(): void { }, }); + const sendBannerImpression = useMutation({ + mutationFn: () => postBannerImpression(banner), + retry: 1, + }); + + useEffect(() => { + if (banner?.bannerType !== "ladefuchs") { + return; + } + if (isDebug) { + return; + } + sendBannerImpression.mutateAsync(); + }, [banner?.identifier]); + const bannerQuery = useQuery({ queryKey: ["appBanners"], retry: 3, @@ -33,6 +63,29 @@ export function useFetchAppData(): void { }, }); + const sendAppMetric = useMutation({ + mutationFn: () => postAppMetric(), + retry: 1, + }); + + useEffect(() => { + const handleAppStateChange = (nextAppState: AppStateStatus) => { + if (nextAppState === "active") { + setTimeout(async () => { + await sendAppMetric.mutateAsync(); + }, 1020); + } + }; + const subscription = AppState.addEventListener( + "change", + handleAppStateChange, + ); + + return () => { + subscription.remove(); + }; + }, [sendAppMetric]); + useEffect(() => { setAppError(allChargeConditionsQuery?.error); diff --git a/state/state.ts b/state/state.ts index cb307dd..d77160b 100644 --- a/state/state.ts +++ b/state/state.ts @@ -116,6 +116,7 @@ function selectLadefuchsBanner({ return null; } return { + identifier: banner.identifier, bannerType: "ladefuchs", affiliateLinkUrl: banner.affiliateLinkUrl, imageUrl: banner.imageUrl, diff --git a/types/banner.ts b/types/banner.ts index 18f6d95..74ac8fb 100644 --- a/types/banner.ts +++ b/types/banner.ts @@ -1,3 +1,5 @@ +import { Platform, PlatformOSType } from "react-native"; + export interface LadefuchsBanner extends Banner { identifier: string; frequency: number; @@ -6,9 +8,15 @@ export interface LadefuchsBanner extends Banner { } export interface Banner { + identifier: string; affiliateLinkUrl: string; imageUrl: string; bannerType: BannerType; } export type BannerType = "ladefuchs" | "chargePrice"; + +export interface ImpressionRequest { + bannerId: string; + platform: PlatformOSType; +} diff --git a/types/metrics.ts b/types/metrics.ts new file mode 100644 index 0000000..d5b5958 --- /dev/null +++ b/types/metrics.ts @@ -0,0 +1,16 @@ +import { PlatformOSType } from "react-native"; + +export interface AppMetricsRequest { + deviceId: string | null; + platform: PlatformOSType; + version: number; +} + +export interface AppMetricResponse { + deviceId: string; +} + +export interface AppMetricCache { + deviceId: string | null; + lastUpdated: string; +} From 6c0b0a6978d44d657b0e0801dd6ddafd9e2f03ae Mon Sep 17 00:00:00 2001 From: Dominic Wrege Date: Tue, 23 Jul 2024 18:11:11 +0200 Subject: [PATCH 2/7] ios beta version 2.2.2 --- app.json | 4 ++-- hooks/fetchAppData.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app.json b/app.json index 13a7595..6117c68 100755 --- a/app.json +++ b/app.json @@ -2,7 +2,7 @@ "expo": { "name": "Ladefuchs", "slug": "ladefuchs", - "version": "2.2.0", + "version": "2.2.2", "orientation": "portrait", "scheme": "com.ladefuchs.app", "icon": "./assets/fuchs/app_icon.png", @@ -14,7 +14,7 @@ }, "assetBundlePatterns": ["**/*"], "ios": { - "buildNumber": "17", + "buildNumber": "1", "supportsTablet": false, "jsEngine": "jsc", "bundleIdentifier": "app.ladefuchs.Ladefuchs", diff --git a/hooks/fetchAppData.ts b/hooks/fetchAppData.ts index 71dcc53..706c0df 100644 --- a/hooks/fetchAppData.ts +++ b/hooks/fetchAppData.ts @@ -73,7 +73,7 @@ export function useFetchAppData(): void { if (nextAppState === "active") { setTimeout(async () => { await sendAppMetric.mutateAsync(); - }, 1020); + }, 1100); } }; const subscription = AppState.addEventListener( From be9707c4913436e6caa1d0e0bc0355f90aefbc41 Mon Sep 17 00:00:00 2001 From: Dominic Wrege Date: Tue, 23 Jul 2024 19:11:40 +0200 Subject: [PATCH 3/7] 2.2.2 build 2 --- app.json | 2 +- hooks/fetchAppData.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index 6117c68..ce31828 100755 --- a/app.json +++ b/app.json @@ -14,7 +14,7 @@ }, "assetBundlePatterns": ["**/*"], "ios": { - "buildNumber": "1", + "buildNumber": "2", "supportsTablet": false, "jsEngine": "jsc", "bundleIdentifier": "app.ladefuchs.Ladefuchs", diff --git a/hooks/fetchAppData.ts b/hooks/fetchAppData.ts index 706c0df..ca5810a 100644 --- a/hooks/fetchAppData.ts +++ b/hooks/fetchAppData.ts @@ -70,7 +70,7 @@ export function useFetchAppData(): void { useEffect(() => { const handleAppStateChange = (nextAppState: AppStateStatus) => { - if (nextAppState === "active") { + if (nextAppState === "active" && !isDebug) { setTimeout(async () => { await sendAppMetric.mutateAsync(); }, 1100); From feb83fb1fcfa734fb841b26aca131755454e6f3e Mon Sep 17 00:00:00 2001 From: Dominic Wrege Date: Tue, 23 Jul 2024 23:45:53 +0200 Subject: [PATCH 4/7] refactor app metrics to a hook --- App.tsx | 4 +- app.json | 2 +- functions/api.ts | 15 ++++-- hooks/fetchAppData.ts | 107 --------------------------------------- hooks/useAppMetrics.ts | 57 +++++++++++++++++++++ hooks/usefetchAppData.ts | 55 ++++++++++++++++++++ 6 files changed, 128 insertions(+), 112 deletions(-) delete mode 100644 hooks/fetchAppData.ts create mode 100644 hooks/useAppMetrics.ts create mode 100644 hooks/usefetchAppData.ts diff --git a/App.tsx b/App.tsx index 3130ee0..337f8e9 100755 --- a/App.tsx +++ b/App.tsx @@ -18,11 +18,12 @@ import { Tariff } from "./types/tariff"; import { CloseButton } from "./components/header/closeButton"; import { DetailHeader } from "./components/detail/detailHeader"; -import { useFetchAppData } from "./hooks/fetchAppData"; +import { useFetchAppData } from "./hooks/usefetchAppData"; import { useCustomFonts } from "./hooks/customFont"; import { scale } from "react-native-size-matters"; import { FeedbackView } from "./screens/feedbackView"; import { ToastNotification } from "./components/detail/feedbackView/toastNotification"; +import { useAopMetrics } from "./hooks/useAppMetrics"; const queryClient = new QueryClient(); const RootStack = createStackNavigator(); @@ -55,6 +56,7 @@ function AppWrapper(): JSX.Element { }, [onAppStateChange]); useFetchAppData(); + useAopMetrics(); const fontLoaded = useCustomFonts(); if (!fontLoaded) { diff --git a/app.json b/app.json index ce31828..a820d56 100755 --- a/app.json +++ b/app.json @@ -14,7 +14,7 @@ }, "assetBundlePatterns": ["**/*"], "ios": { - "buildNumber": "2", + "buildNumber": "4", "supportsTablet": false, "jsEngine": "jsc", "bundleIdentifier": "app.ladefuchs.Ladefuchs", diff --git a/functions/api.ts b/functions/api.ts index 190b6d0..6a1ffe8 100644 --- a/functions/api.ts +++ b/functions/api.ts @@ -15,7 +15,12 @@ import { import { FeedbackRequest } from "../types/feedback"; import { Operator, OperatorsResponse } from "../types/operator"; import { Tariff, TariffResponse } from "../types/tariff"; -import { appVersionNumber, fetchWithTimeout, getMinutes } from "./util"; +import { + appVersionNumber, + fetchWithTimeout, + getMinutes, + isDebug, +} from "./util"; import { AppMetricCache, AppMetricResponse, @@ -170,9 +175,10 @@ const storageSet = { }; export async function postBannerImpression(banner: Banner): Promise { - if (!banner?.identifier) { + if (isDebug) { return; } + const response = await fetchWithTimeout( `${apiPath}/v3/banners/impression`, { @@ -194,12 +200,15 @@ export async function postBannerImpression(banner: Banner): Promise { } export async function postAppMetric(): Promise { + if (isDebug) { + return; + } const cacheKey = "appMetric"; const cache = await retrieveFromStorage(cacheKey); if (cache?.lastUpdated) { const updatedDevice = Date.parse(cache?.lastUpdated); - const oneHourInMs = getMinutes(60); + const oneHourInMs = getMinutes(45); if (Date.now() - updatedDevice < oneHourInMs) { return; } diff --git a/hooks/fetchAppData.ts b/hooks/fetchAppData.ts deleted file mode 100644 index ca5810a..0000000 --- a/hooks/fetchAppData.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { useMutation, useQuery } from "@tanstack/react-query"; -import { useEffect } from "react"; -import { - getAllChargeConditions, - getBanners, - postAppMetric, - postBannerImpression, -} from "../functions/api"; - -import { useShallow } from "zustand/react/shallow"; -import { useAppStore } from "../state/state"; -import { isDebug } from "../functions/util"; -import { AppState, AppStateStatus } from "react-native"; - -export function useFetchAppData(): void { - const [ - setAppData, - setAppError, - operators, - setBanners, - ladefuchsBanners, - banner, - ] = useAppStore( - useShallow((state) => [ - state.setChargeConditions, - state.setAppError, - state.operators, - state.setBanners, - state.ladefuchsBanners, - state.banner, - ]), - ); - const allChargeConditionsQuery = useQuery({ - queryKey: ["appChargeConditions"], - retry: 3, - queryFn: async () => { - return await getAllChargeConditions({ - writeToCache: !operators.length, - }); - }, - }); - - const sendBannerImpression = useMutation({ - mutationFn: () => postBannerImpression(banner), - retry: 1, - }); - - useEffect(() => { - if (banner?.bannerType !== "ladefuchs") { - return; - } - if (isDebug) { - return; - } - sendBannerImpression.mutateAsync(); - }, [banner?.identifier]); - - const bannerQuery = useQuery({ - queryKey: ["appBanners"], - retry: 3, - queryFn: async () => { - return await getBanners({ writeToCache: !ladefuchsBanners.length }); - }, - }); - - const sendAppMetric = useMutation({ - mutationFn: () => postAppMetric(), - retry: 1, - }); - - useEffect(() => { - const handleAppStateChange = (nextAppState: AppStateStatus) => { - if (nextAppState === "active" && !isDebug) { - setTimeout(async () => { - await sendAppMetric.mutateAsync(); - }, 1100); - } - }; - const subscription = AppState.addEventListener( - "change", - handleAppStateChange, - ); - - return () => { - subscription.remove(); - }; - }, [sendAppMetric]); - - useEffect(() => { - setAppError(allChargeConditionsQuery?.error); - - if (allChargeConditionsQuery.data) { - setAppData(allChargeConditionsQuery.data); - } - }, [ - allChargeConditionsQuery.data, - allChargeConditionsQuery.error, - setAppError, - setAppData, - ]); - - useEffect(() => { - if (bannerQuery.data) { - setBanners(bannerQuery.data); - } - }, [bannerQuery.data, setBanners]); -} diff --git a/hooks/useAppMetrics.ts b/hooks/useAppMetrics.ts new file mode 100644 index 0000000..0d70eb5 --- /dev/null +++ b/hooks/useAppMetrics.ts @@ -0,0 +1,57 @@ +import { useMutation } from "@tanstack/react-query"; +import { useEffect } from "react"; +import { AppStateStatus, AppState } from "react-native"; +import { postAppMetric, postBannerImpression } from "../functions/api"; +import { useShallow } from "zustand/react/shallow"; +import { useAppStore } from "../state/state"; + +export function useAopMetrics() { + const [banner] = useAppStore(useShallow((state) => [state.banner])); + + const sendBannerImpression = useMutation({ + mutationKey: [banner?.imageUrl ?? ""], + mutationFn: async () => await postBannerImpression(banner), + retry: 1, + }); + + useEffect(() => { + if (banner?.bannerType !== "ladefuchs") { + return; + } + if (!sendBannerImpression.isIdle) { + return; + } + if (!banner?.identifier) { + return; + } + sendBannerImpression.mutateAsync(); + }, [banner?.identifier]); + + const sendAppMetric = useMutation({ + mutationFn: async () => await postAppMetric(), + retry: 1, + }); + + useEffect(() => { + const handleAppStateChange = (nextAppState: AppStateStatus) => { + if (nextAppState !== "active") { + return; + } + if (!sendAppMetric.isIdle) { + return; + } + setTimeout(async () => { + await sendAppMetric.mutateAsync(); + sendAppMetric.reset(); + }, 1000); + }; + const subscription = AppState.addEventListener( + "change", + handleAppStateChange, + ); + + return () => { + subscription.remove(); + }; + }, [sendAppMetric]); +} diff --git a/hooks/usefetchAppData.ts b/hooks/usefetchAppData.ts new file mode 100644 index 0000000..2774948 --- /dev/null +++ b/hooks/usefetchAppData.ts @@ -0,0 +1,55 @@ +import { useQuery } from "@tanstack/react-query"; +import { useEffect } from "react"; +import { getAllChargeConditions, getBanners } from "../functions/api"; + +import { useShallow } from "zustand/react/shallow"; +import { useAppStore } from "../state/state"; + +export function useFetchAppData(): void { + const [setAppData, setAppError, operators, setBanners, ladefuchsBanners] = + useAppStore( + useShallow((state) => [ + state.setChargeConditions, + state.setAppError, + state.operators, + state.setBanners, + state.ladefuchsBanners, + ]), + ); + const allChargeConditionsQuery = useQuery({ + queryKey: ["appChargeConditions"], + retry: 3, + queryFn: async () => { + return await getAllChargeConditions({ + writeToCache: !operators.length, + }); + }, + }); + + const bannerQuery = useQuery({ + queryKey: ["appBanners"], + retry: 3, + queryFn: async () => { + return await getBanners({ writeToCache: !ladefuchsBanners.length }); + }, + }); + + useEffect(() => { + setAppError(allChargeConditionsQuery?.error); + + if (allChargeConditionsQuery.data) { + setAppData(allChargeConditionsQuery.data); + } + }, [ + allChargeConditionsQuery.data, + allChargeConditionsQuery.error, + setAppError, + setAppData, + ]); + + useEffect(() => { + if (bannerQuery.data) { + setBanners(bannerQuery.data); + } + }, [bannerQuery.data, setBanners]); +} From a43290222613442c2e9e45dc7eda4b15053c7350 Mon Sep 17 00:00:00 2001 From: Dominic Wrege Date: Wed, 24 Jul 2024 22:37:06 +0200 Subject: [PATCH 5/7] changes: - larger close button and fix background - fix app reload banner - feedback add charge type --- components/detail/detailHeader.tsx | 5 ++++- components/header/appHeader.tsx | 7 +----- components/header/closeButton.tsx | 21 +++++++++++++----- functions/api.ts | 9 ++++++-- hooks/useAppMetrics.ts | 5 +---- screens/feedbackView.tsx | 31 +++++++++++++++++--------- state/state.ts | 2 +- types/feedback.ts | 35 +++++++++++++++++------------- 8 files changed, 71 insertions(+), 44 deletions(-) diff --git a/components/detail/detailHeader.tsx b/components/detail/detailHeader.tsx index e979a20..aa37489 100644 --- a/components/detail/detailHeader.tsx +++ b/components/detail/detailHeader.tsx @@ -23,7 +23,10 @@ export function DetailHeader({ tariff, navigation }: Props): JSX.Element { {tariff.providerName} - navigation.goBack()} /> + navigation.goBack()} + backgroundColor={colors.ladefuchsDarkBackground} + /> ); diff --git a/components/header/appHeader.tsx b/components/header/appHeader.tsx index b8fd661..2422cd7 100644 --- a/components/header/appHeader.tsx +++ b/components/header/appHeader.tsx @@ -19,11 +19,6 @@ export function AppHeader(): JSX.Element { const navigation = useNavigation(); const reloadBanner = useAppStore((state) => state.reloadBanner); - const handleLongPress = () => { - console.log("handleLongPress reloadBanner"); - reloadBanner(); - }; - return ( reloadBanner()} > diff --git a/components/header/closeButton.tsx b/components/header/closeButton.tsx index 8c0cb65..fc046d5 100644 --- a/components/header/closeButton.tsx +++ b/components/header/closeButton.tsx @@ -1,27 +1,38 @@ import { TouchableOpacity, ViewStyle, View } from "react-native"; import Svg, { Path } from "react-native-svg"; import { colors } from "../../theme"; +import React from "react"; +import { scale } from "react-native-size-matters"; interface Props { onPress: () => void; + backgroundColor?: string; style?: ViewStyle; } -export function CloseButton({ onPress, style }: Props): JSX.Element { +export function CloseButton({ + onPress, + style, + backgroundColor = colors.ladefuchsDarkGrayBackground, +}: Props): JSX.Element { + const size = scale(19); return ( - + { +export async function postBannerImpression( + banner: Banner | null, +): Promise { if (isDebug) { return; } + if (!banner?.identifier) { + return; + } const response = await fetchWithTimeout( `${apiPath}/v3/banners/impression`, { @@ -208,7 +213,7 @@ export async function postAppMetric(): Promise { if (cache?.lastUpdated) { const updatedDevice = Date.parse(cache?.lastUpdated); - const oneHourInMs = getMinutes(45); + const oneHourInMs = getMinutes(20); if (Date.now() - updatedDevice < oneHourInMs) { return; } diff --git a/hooks/useAppMetrics.ts b/hooks/useAppMetrics.ts index 0d70eb5..a312dce 100644 --- a/hooks/useAppMetrics.ts +++ b/hooks/useAppMetrics.ts @@ -21,11 +21,8 @@ export function useAopMetrics() { if (!sendBannerImpression.isIdle) { return; } - if (!banner?.identifier) { - return; - } sendBannerImpression.mutateAsync(); - }, [banner?.identifier]); + }, [banner]); const sendAppMetric = useMutation({ mutationFn: async () => await postAppMetric(), diff --git a/screens/feedbackView.tsx b/screens/feedbackView.tsx index 2b45544..e9ba196 100644 --- a/screens/feedbackView.tsx +++ b/screens/feedbackView.tsx @@ -11,9 +11,15 @@ import { colors, styles as themeStyle } from "../theme"; import { DetailLogos } from "../components/detail/detailLogos"; import { LadefuchsButton } from "../components/detail/ladefuchsButton"; import { Tariff } from "../types/tariff"; -import { TariffCondition } from "../types/conditions"; +import { ChargeMode, TariffCondition } from "../types/conditions"; import { ScaledSheet } from "react-native-size-matters"; -import { FeedbackContext, FeedbackRequest } from "../types/feedback"; +import { + FeedbackContext, + FeedbackRequest, + OtherFeedbackRequest, + WrongPriceFeedbackAttributes, + WrongPriceRequest, +} from "../types/feedback"; import { scale } from "react-native-size-matters"; import { PriceBox } from "../components/detail/priceBox"; @@ -67,22 +73,24 @@ export function FeedbackView(): JSX.Element { const requests = []; - const acWrongPrice = checkPriceAndPushRequest({ + const acWrongPrice = buildWrongPriceRequest({ displayedPrice: acTariffCondition.pricePerKwh, actualPrice: acPriceCounter.value, context, noteText, + chargeType: "ac", }); if (acWrongPrice) { requests.push(acWrongPrice); } - const dcWrongPrice = checkPriceAndPushRequest({ + const dcWrongPrice = buildWrongPriceRequest({ displayedPrice: dcTariffCondition.pricePerKwh, actualPrice: dcPriceCounter.value, context, noteText, + chargeType: "dc", }); if (dcWrongPrice) { requests.push(dcWrongPrice); @@ -95,7 +103,7 @@ export function FeedbackView(): JSX.Element { type: "otherFeedback", attributes: { notes: noteText }, }, - }); + } satisfies OtherFeedbackRequest); } return requests; }; @@ -119,7 +127,7 @@ export function FeedbackView(): JSX.Element { }, 500); } catch (error) { setSendButtonText("Senden"); - console.log("sedning feedback", error); + console.log("sending feedback", error); setDisableSendButton(false); Toast.show({ type: "error", @@ -255,16 +263,18 @@ const feedbackthemeStyle = ScaledSheet.create({ }, }); -function checkPriceAndPushRequest({ +function buildWrongPriceRequest({ displayedPrice, actualPrice, context, noteText, + chargeType, }: { displayedPrice: number | null; actualPrice: number; - context; - noteText; + context: FeedbackContext; + noteText: string; + chargeType: ChargeMode; }) { if ( displayedPrice && @@ -278,9 +288,10 @@ function checkPriceAndPushRequest({ notes: noteText, displayedPrice, actualPrice, + chargeType, }, }, - }; + } satisfies WrongPriceRequest; } return null; diff --git a/state/state.ts b/state/state.ts index d77160b..caf41d9 100644 --- a/state/state.ts +++ b/state/state.ts @@ -90,7 +90,7 @@ export const useAppStore = create((set, get) => { (ladefuchsBannerIndex + 1) % ladefuchsBanners.length; newBanner = ladefuchsBanners[ladefuchsBannerIndex]; } while (newBanner.imageUrl === banner?.imageUrl); - set(() => ({ banner: newBanner })); + set(() => ({ banner: { ...newBanner, bannerType: "ladefuchs" } })); }, }; }); diff --git a/types/feedback.ts b/types/feedback.ts index 08c92cb..7e5a74b 100644 --- a/types/feedback.ts +++ b/types/feedback.ts @@ -1,3 +1,5 @@ +import { ChargeMode } from "./conditions"; + export interface FeedbackContext { operatorId: string; tariffId: string; @@ -9,6 +11,7 @@ export interface WrongPriceFeedbackAttributes { notes: string; displayedPrice: number; actualPrice: number; + chargeType: ChargeMode; } export interface OtherFeedbackAttributes { @@ -17,18 +20,20 @@ export interface OtherFeedbackAttributes { export type FeedbackType = "wrongPriceFeedback" | "otherFeedback"; -export type FeedbackRequest = - | { - context: FeedbackContext; - request: { - type: "wrongPriceFeedback"; - attributes: WrongPriceFeedbackAttributes; - }; - } - | { - context: FeedbackContext; - request: { - type: "otherFeedback"; - attributes: OtherFeedbackAttributes; - }; - }; +export type WrongPriceRequest = { + context: FeedbackContext; + request: { + type: "wrongPriceFeedback"; + attributes: WrongPriceFeedbackAttributes; + }; +}; + +export type OtherFeedbackRequest = { + context: FeedbackContext; + request: { + type: "otherFeedback"; + attributes: OtherFeedbackAttributes; + }; +}; + +export type FeedbackRequest = WrongPriceRequest | OtherFeedbackRequest; From 0ba01a45beead2016b5c2d8fe23b00917ff4ce27 Mon Sep 17 00:00:00 2001 From: Dominic Wrege Date: Wed, 24 Jul 2024 23:35:43 +0200 Subject: [PATCH 6/7] new beta build 2.2.2 --- app.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index a820d56..2fa4095 100755 --- a/app.json +++ b/app.json @@ -14,7 +14,7 @@ }, "assetBundlePatterns": ["**/*"], "ios": { - "buildNumber": "4", + "buildNumber": "5", "supportsTablet": false, "jsEngine": "jsc", "bundleIdentifier": "app.ladefuchs.Ladefuchs", @@ -33,7 +33,7 @@ "resizeMode": "contain", "backgroundColor": "#F3EEE2" }, - "versionCode": 237, + "versionCode": 238, "adaptiveIcon": { "foregroundImage": "./assets/fuchs/android_logo.png", "backgroundColor": "#F3EEE2" From 20aa9a8be7651a3a4bc80a1968439cb44f13f8da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 07:04:20 +0000 Subject: [PATCH 7/7] Bump fast-xml-parser from 4.4.0 to 4.4.1 Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.4.0 to 4.4.1. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.4.0...v4.4.1) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5711dc..6281dd6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11161,9 +11161,9 @@ "license": "MIT" }, "node_modules/fast-xml-parser": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz", - "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "funding": [ { "type": "github", @@ -11174,7 +11174,6 @@ "url": "https://paypal.me/naturalintelligence" } ], - "license": "MIT", "dependencies": { "strnum": "^1.0.5" },