mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
feat: add catchup and unpaired reminder notifications
Add automatic push notifications for behind partners (escalating: 24h then every 3 days) and daily unpaired user reminders. Includes edge functions, migrations, notification preference toggles, and UI improvements to pairing and swipe screens.
This commit is contained in:
1 parent
8a44fef764
commit
c8aaa8967d
12 files changed
+1117
-266
No files matched your search
@@ -7,6 +7,7 @@ import {
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
Share,
|
||||
Linking,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
TouchableOpacity,
|
||||
@@ -141,12 +142,14 @@ export default function PairingScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
const shareMessage = couple?.invite_code
|
||||
? `Join me on Sauci! Download the app at https://sauci.app and use my invite code to pair up: ${couple.invite_code}`
|
||||
: "";
|
||||
|
||||
const shareCode = async () => {
|
||||
if (couple?.invite_code) {
|
||||
try {
|
||||
await Share.share({
|
||||
message: `Join me on Sauci! Download the app at https://sauci.app and use my invite code to pair up: ${couple.invite_code}`,
|
||||
});
|
||||
await Share.share({ message: shareMessage });
|
||||
Events.codeShared();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -154,6 +157,23 @@ export default function PairingScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
const shareViaWhatsApp = async () => {
|
||||
const url = `whatsapp://send?text=${encodeURIComponent(shareMessage)}`;
|
||||
const canOpen = await Linking.canOpenURL(url);
|
||||
if (canOpen) {
|
||||
await Linking.openURL(url);
|
||||
Events.codeShared();
|
||||
} else {
|
||||
shareCode();
|
||||
}
|
||||
};
|
||||
|
||||
const shareViaSMS = async () => {
|
||||
const url = `sms:&body=${encodeURIComponent(shareMessage)}`;
|
||||
await Linking.openURL(url);
|
||||
Events.codeShared();
|
||||
};
|
||||
|
||||
const handleCancelPairing = async () => {
|
||||
Alert.alert(
|
||||
"Cancel Pairing",
|
||||
@@ -256,7 +276,15 @@ export default function PairingScreen() {
|
||||
</GlassCard>
|
||||
</Animated.View>
|
||||
|
||||
{/* Share Button */}
|
||||
{/* CTA Text */}
|
||||
<Animated.View
|
||||
entering={FadeInDown.delay(350).duration(500)}
|
||||
style={styles.ctaSection}
|
||||
>
|
||||
<Text style={styles.ctaText}>Send this to your partner to get started</Text>
|
||||
</Animated.View>
|
||||
|
||||
{/* Primary Share Button */}
|
||||
<Animated.View
|
||||
entering={FadeInDown.delay(400).duration(500)}
|
||||
style={styles.section}
|
||||
@@ -264,12 +292,28 @@ export default function PairingScreen() {
|
||||
<GlassButton
|
||||
onPress={shareCode}
|
||||
fullWidth
|
||||
icon={<Ionicons name="share-outline" size={20} color={colors.text} />}
|
||||
size="lg"
|
||||
icon={<Ionicons name="share-outline" size={22} color={colors.text} />}
|
||||
>
|
||||
Share Code
|
||||
Share Invite Code
|
||||
</GlassButton>
|
||||
</Animated.View>
|
||||
|
||||
{/* Quick share buttons */}
|
||||
<Animated.View
|
||||
entering={FadeInDown.delay(450).duration(500)}
|
||||
style={styles.quickShareRow}
|
||||
>
|
||||
<TouchableOpacity style={styles.quickShareButton} onPress={shareViaSMS} activeOpacity={0.7}>
|
||||
<Ionicons name="chatbubble-outline" size={20} color={colors.text} />
|
||||
<Text style={styles.quickShareLabel}>Messages</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.quickShareButton} onPress={shareViaWhatsApp} activeOpacity={0.7}>
|
||||
<Ionicons name="logo-whatsapp" size={20} color={colors.text} />
|
||||
<Text style={styles.quickShareLabel}>WhatsApp</Text>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
|
||||
{/* Waiting indicator */}
|
||||
<Animated.View
|
||||
entering={FadeInDown.delay(500).duration(500)}
|
||||
@@ -414,61 +458,61 @@ const styles = StyleSheet.create({
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
},
|
||||
backButton: {
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
backgroundColor: colors.backgroundLight,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: spacing.md,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
// ...
|
||||
input: {
|
||||
backgroundColor: colors.background, // Flat background
|
||||
borderRadius: radius.md,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
padding: spacing.md,
|
||||
...typography.headline,
|
||||
color: colors.text,
|
||||
textAlign: "center",
|
||||
letterSpacing: 4,
|
||||
fontFamily: Platform.OS === "ios" ? "Courier" : "monospace",
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
// ...
|
||||
dividerLine: {
|
||||
flex: 1,
|
||||
height: 1,
|
||||
backgroundColor: colors.border,
|
||||
},
|
||||
// ...
|
||||
copyIcon: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
backgroundColor: colors.background,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginLeft: spacing.md,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
// ...
|
||||
waitingBadge: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: colors.backgroundLight,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
borderRadius: radius.full,
|
||||
gap: spacing.sm,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
backButton: {
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
backgroundColor: colors.backgroundLight,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: spacing.md,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
// ...
|
||||
input: {
|
||||
backgroundColor: colors.background, // Flat background
|
||||
borderRadius: radius.md,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
padding: spacing.md,
|
||||
...typography.headline,
|
||||
color: colors.text,
|
||||
textAlign: "center",
|
||||
letterSpacing: 4,
|
||||
fontFamily: Platform.OS === "ios" ? "Courier" : "monospace",
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
// ...
|
||||
dividerLine: {
|
||||
flex: 1,
|
||||
height: 1,
|
||||
backgroundColor: colors.border,
|
||||
},
|
||||
// ...
|
||||
copyIcon: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
backgroundColor: colors.background,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginLeft: spacing.md,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
// ...
|
||||
waitingBadge: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: colors.backgroundLight,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
borderRadius: radius.full,
|
||||
gap: spacing.sm,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
title: {
|
||||
...typography.title1,
|
||||
color: colors.text,
|
||||
@@ -508,13 +552,13 @@ const styles = StyleSheet.create({
|
||||
marginBottom: spacing.sm,
|
||||
marginLeft: spacing.xs,
|
||||
},
|
||||
|
||||
|
||||
divider: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginVertical: spacing.lg,
|
||||
},
|
||||
|
||||
|
||||
dividerText: {
|
||||
...typography.subhead,
|
||||
color: colors.textTertiary,
|
||||
@@ -531,7 +575,7 @@ const styles = StyleSheet.create({
|
||||
letterSpacing: 6,
|
||||
fontFamily: Platform.OS === "ios" ? "Courier" : "monospace",
|
||||
},
|
||||
|
||||
|
||||
tapToCopy: {
|
||||
...typography.caption1,
|
||||
color: colors.textTertiary,
|
||||
@@ -542,7 +586,7 @@ const styles = StyleSheet.create({
|
||||
alignItems: "center",
|
||||
marginTop: spacing.xl,
|
||||
},
|
||||
|
||||
|
||||
waitingText: {
|
||||
...typography.subhead,
|
||||
color: colors.textTertiary,
|
||||
@@ -559,4 +603,34 @@ const styles = StyleSheet.create({
|
||||
...typography.subhead,
|
||||
color: colors.error,
|
||||
},
|
||||
ctaSection: {
|
||||
alignItems: "center",
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
ctaText: {
|
||||
...typography.callout,
|
||||
color: colors.textSecondary,
|
||||
textAlign: "center",
|
||||
},
|
||||
quickShareRow: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
gap: spacing.md,
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
quickShareButton: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: spacing.sm,
|
||||
backgroundColor: colors.backgroundLight,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
borderRadius: radius.full,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
quickShareLabel: {
|
||||
...typography.subhead,
|
||||
color: colors.text,
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user