diff --git a/app/components/december-challenge/card.tsx b/app/components/december-challenge/card.tsx
new file mode 100644
index 0000000000..8fdbbc7ffb
--- /dev/null
+++ b/app/components/december-challenge/card.tsx
@@ -0,0 +1,106 @@
+import { View } from "react-native"
+import { Text, makeStyles, useTheme } from "@rneui/themed"
+import { useI18nContext } from "@app/i18n/i18n-react"
+import { GaloyIcon } from "../atomic/galoy-icon"
+import { useEffect, useState } from "react"
+import { DecemberChallengeModal } from "./modal"
+import { PressableCard } from "../pressable-card"
+
+import { JAN_1_2024_12_AM_UTC_MINUS_6, DEC_1_12_AM_UTC_MINUS_6 } from "./dates"
+
+function secondsToDDMMSS(totalSeconds: number) {
+ if (totalSeconds < 0) return ""
+
+ const days = Math.floor(totalSeconds / 86400) // There are 86400 seconds in a day
+ const hours = Math.floor((totalSeconds - days * 86400) / 3600) // 3600 seconds in an hour
+ const minutes = Math.floor((totalSeconds - days * 86400 - hours * 3600) / 60)
+ const seconds = Math.floor(totalSeconds - days * 86400 - hours * 3600 - minutes * 60)
+
+ const formattedDays = days.toString().padStart(2, "0")
+ const formattedHours = hours.toString().padStart(2, "0")
+ const formattedMinutes = minutes.toString().padStart(2, "0")
+ const formattedSeconds = seconds.toString().padStart(2, "0")
+
+ return `${formattedDays}:${formattedHours}:${formattedMinutes}:${formattedSeconds}`
+}
+
+const getTimeLeft = () => {
+ const dateNow = Date.now()
+ if (dateNow > JAN_1_2024_12_AM_UTC_MINUS_6 || dateNow < DEC_1_12_AM_UTC_MINUS_6)
+ return ""
+
+ const sLeft = (JAN_1_2024_12_AM_UTC_MINUS_6 - dateNow) / 1000
+ return secondsToDDMMSS(sLeft)
+}
+
+export const DecemberChallengeCard: React.FC = () => {
+ const [modalIsOpen, setModalIsOpen] = useState(false)
+ const openModal = () => setModalIsOpen(true)
+
+ const {
+ theme: { colors },
+ } = useTheme()
+ const styles = useStyles()
+ const { LL } = useI18nContext()
+
+ const [countDown, setCountDown] = useState(getTimeLeft())
+
+ useEffect(() => {
+ const dateNow = Date.now()
+ if (dateNow > JAN_1_2024_12_AM_UTC_MINUS_6) return
+
+ const t = setInterval(() => {
+ setCountDown(getTimeLeft())
+ }, 1000)
+
+ return () => clearInterval(t)
+ }, [setCountDown])
+
+ const currentTime = Date.now()
+ if (currentTime > JAN_1_2024_12_AM_UTC_MINUS_6 || currentTime < DEC_1_12_AM_UTC_MINUS_6)
+ return <>>
+
+ return (
+
+
+
+
+
+
+ {LL.Circles.decemberChallenge.title()}
+
+ {countDown}
+
+ {LL.Circles.decemberChallenge.description()}
+
+
+
+
+
+
+ )
+}
+
+const useStyles = makeStyles(({ colors }) => ({
+ card: {
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ padding: 16,
+ borderRadius: 10,
+ backgroundColor: colors.grey5,
+ },
+ textContainer: {
+ flex: 1,
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "center",
+ rowGap: 6,
+ },
+ beside: {
+ display: "flex",
+ flexDirection: "row",
+ alignItems: "center",
+ columnGap: 10,
+ },
+}))
diff --git a/app/components/december-challenge/dates.ts b/app/components/december-challenge/dates.ts
new file mode 100644
index 0000000000..d9ec41255f
--- /dev/null
+++ b/app/components/december-challenge/dates.ts
@@ -0,0 +1,5 @@
+export const DEC_1_12_AM_UTC_MINUS_6 = new Date(Date.UTC(2023, 11, 1, 6, 0, 0)).getTime()
+
+export const JAN_1_2024_12_AM_UTC_MINUS_6 = new Date(
+ Date.UTC(2024, 0, 1, 6, 0, 0),
+).getTime()
diff --git a/app/components/december-challenge/index.tsx b/app/components/december-challenge/index.tsx
new file mode 100644
index 0000000000..cfa5107fa6
--- /dev/null
+++ b/app/components/december-challenge/index.tsx
@@ -0,0 +1,2 @@
+export * from "./modal"
+export * from "./card"
diff --git a/app/components/december-challenge/modal.tsx b/app/components/december-challenge/modal.tsx
new file mode 100644
index 0000000000..57bbdc9edb
--- /dev/null
+++ b/app/components/december-challenge/modal.tsx
@@ -0,0 +1,131 @@
+import * as React from "react"
+import { Linking, View } from "react-native"
+import Modal from "react-native-modal"
+
+import { useI18nContext } from "@app/i18n/i18n-react"
+import { makeStyles, useTheme, Text } from "@rneui/themed"
+
+import { GaloyIconButton } from "../atomic/galoy-icon-button"
+import { GaloyToast } from "../galoy-toast"
+import { GaloyIcon } from "../atomic/galoy-icon"
+import { useCirclesCard } from "@app/screens/people-screen/circles/use-circles-card"
+import { GaloyPrimaryButton } from "../atomic/galoy-primary-button"
+
+const CHALLENGE_PAGE = "blink.sv/circles"
+const CHALLENGE_PAGE_URL = "https://www.blink.sv/circles"
+const SOCIAL_LINK_TREE = "https://linktr.ee/blinkbtc"
+
+type Props = {
+ isVisible: boolean
+ setIsVisible: (isVisible: boolean) => void
+}
+
+export const DecemberChallengeModal: React.FC = ({ isVisible, setIsVisible }) => {
+ const { LL } = useI18nContext()
+
+ const {
+ theme: { colors },
+ } = useTheme()
+ const styles = useStyles()
+
+ const acknowledgeModal = () => {
+ setIsVisible(false)
+ }
+
+ const { ShareImg, share } = useCirclesCard()
+
+ return (
+
+
+
+
+
+
+ {LL.Circles.decemberChallenge.title()}
+
+
+ {LL.Circles.decemberChallenge.details()}
+
+ {ShareImg}
+
+
+ {LL.Circles.octoberChallenge.connectOnSocial()}
+ Linking.openURL(SOCIAL_LINK_TREE)}
+ >
+ {SOCIAL_LINK_TREE}
+
+
+
+ {LL.Circles.octoberChallenge.fullDetails()}
+ Linking.openURL(CHALLENGE_PAGE_URL)}
+ >
+ {CHALLENGE_PAGE}
+
+
+
+
+
+
+ )
+}
+
+const useStyles = makeStyles(({ colors }) => ({
+ container: {
+ paddingHorizontal: 12,
+ display: "flex",
+ flexDirection: "column",
+ rowGap: 20,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ cross: {
+ position: "absolute",
+ right: 20,
+ top: -10,
+ },
+ top: { marginTop: 40 },
+ modalCard: {
+ backgroundColor: colors.grey5,
+ borderRadius: 16,
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ paddingVertical: 30,
+ },
+ details: {
+ textAlign: "center",
+ paddingHorizontal: 20,
+ },
+ reminder: {
+ textAlign: "center",
+ paddingHorizontal: 20,
+ color: colors.grey2,
+ },
+ underline: {
+ textDecorationLine: "underline",
+ },
+ containerData: {
+ display: "flex",
+ flexDirection: "column",
+ rowGap: 2,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+}))
diff --git a/app/i18n/en/index.ts b/app/i18n/en/index.ts
index 5b19b9ec36..065b2bf1dc 100644
--- a/app/i18n/en/index.ts
+++ b/app/i18n/en/index.ts
@@ -2796,6 +2796,11 @@ const en: BaseTranslation = {
description: "Earn 2,100 sats for every person you welcome to Blink in November!",
details: "Earn 2,100 sats for every person you welcome to Blink in November!\n\nTo claim your sats, simply share your Circles on social any time during November with the tag `#blinkcircles`.\n\nYour sats will be paid out on December 1, 2023."
},
+ decemberChallenge: {
+ title: "December Challenge!",
+ description: "+10 inner circle for a chance at $100!",
+ details: "Grow your inner circle by 10 people in December for a chance at $100!\n\nTo participate, share your circles on social once you've reached 10 for the month and tag #blinkcircles.\n\nOn January 1, three people will be chosen at random to receive $100!"
+ },
},
FullOnboarding: {
title: "Full onboarding",
diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts
index 33a4fa1f66..d2e7ce7b60 100644
--- a/app/i18n/i18n-types.ts
+++ b/app/i18n/i18n-types.ts
@@ -8725,6 +8725,24 @@ type RootTranslation = {
*/
details: string
}
+ decemberChallenge: {
+ /**
+ * December Challenge!
+ */
+ title: string
+ /**
+ * +10 inner circle for a chance at $100!
+ */
+ description: string
+ /**
+ * Grow your inner circle by 10 people in December for a chance at $100!
+
+ To participate, share your circles on social once you've reached 10 for the month and tag #blinkcircles.
+
+ On January 1, three people will be chosen at random to receive $100!
+ */
+ details: string
+ }
}
FullOnboarding: {
/**
@@ -17408,6 +17426,24 @@ export type TranslationFunctions = {
*/
details: () => LocalizedString
}
+ decemberChallenge: {
+ /**
+ * December Challenge!
+ */
+ title: () => LocalizedString
+ /**
+ * +10 inner circle for a chance at $100!
+ */
+ description: () => LocalizedString
+ /**
+ * Grow your inner circle by 10 people in December for a chance at $100!
+
+ To participate, share your circles on social once you've reached 10 for the month and tag #blinkcircles.
+
+ On January 1, three people will be chosen at random to receive $100!
+ */
+ details: () => LocalizedString
+ }
}
FullOnboarding: {
/**
diff --git a/app/i18n/raw-i18n/source/en.json b/app/i18n/raw-i18n/source/en.json
index 9e6bf635d4..980e27b5fb 100644
--- a/app/i18n/raw-i18n/source/en.json
+++ b/app/i18n/raw-i18n/source/en.json
@@ -2671,6 +2671,11 @@
"title": "November Challenge!",
"description": "Earn 2,100 sats for every person you welcome to Blink in November!",
"details": "Earn 2,100 sats for every person you welcome to Blink in November!\n\nTo claim your sats, simply share your Circles on social any time during November with the tag `#blinkcircles`.\n\nYour sats will be paid out on December 1, 2023."
+ },
+ "decemberChallenge": {
+ "title": "December Challenge!",
+ "description": "+10 inner circle for a chance at $100!",
+ "details": "Grow your inner circle by 10 people in December for a chance at $100!\n\nTo participate, share your circles on social once you've reached 10 for the month and tag #blinkcircles.\n\nOn January 1, three people will be chosen at random to receive $100!"
}
},
"FullOnboarding": {
diff --git a/app/screens/people-screen/circles/circles-dashboard-screen.tsx b/app/screens/people-screen/circles/circles-dashboard-screen.tsx
index 28da626488..d174da47c2 100644
--- a/app/screens/people-screen/circles/circles-dashboard-screen.tsx
+++ b/app/screens/people-screen/circles/circles-dashboard-screen.tsx
@@ -17,6 +17,7 @@ import { Screen } from "../../../components/screen"
import { IntroducingCirclesModal } from "@app/components/introducing-circles-modal"
import { NovemberChallengeCard } from "@app/components/november-challenge"
import { ScrollView } from "react-native-gesture-handler"
+import { DecemberChallengeCard } from "@app/components/december-challenge"
gql`
query Circles {
@@ -154,6 +155,7 @@ export const CirclesDashboardScreen: React.FC = () => {
)}
+
{isLonely ? : }