Compare commits

...

2 commits

Author SHA1 Message Date
3e70ff23c9 feat: contribute 2025-05-24 14:38:55 +02:00
eac1c1d5bd chore: improve scripting 2025-05-24 13:48:05 +02:00
10 changed files with 489 additions and 2 deletions

View file

@ -1,2 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
if [ -z "$DEVICE" ]; then
echo "Error: DEVICE environment variable is not set."
echo "Usage: DEVICE=emulator-5554 ./screenshot-android.sh"
exit 1
fi
exec adb -s $DEVICE exec-out screencap -p > screenshot-emulator-$(date +%s).png exec adb -s $DEVICE exec-out screencap -p > screenshot-emulator-$(date +%s).png

View file

@ -21,6 +21,7 @@ import Relatives from "~/scenes/Relatives";
import Sheets from "~/scenes/Sheets"; import Sheets from "~/scenes/Sheets";
import AlertAggListArchived from "~/scenes/AlertAggListArchived"; import AlertAggListArchived from "~/scenes/AlertAggListArchived";
import About from "~/scenes/About"; import About from "~/scenes/About";
import Contribute from "~/scenes/Contribute";
import Location from "~/scenes/Location"; import Location from "~/scenes/Location";
import Developer from "~/scenes/Developer"; import Developer from "~/scenes/Developer";
import HelpSignal from "~/scenes/HelpSignal"; import HelpSignal from "~/scenes/HelpSignal";
@ -413,6 +414,22 @@ export default React.memo(function DrawerNav() {
}} }}
listeners={{}} listeners={{}}
/> />
<Drawer.Screen
name="Contribute"
component={Contribute}
options={{
drawerLabel: "Faire un don",
drawerIcon: ({ focused }) => (
<MaterialCommunityIcons
name="hand-heart"
{...iconProps}
{...(focused ? iconFocusedProps : {})}
/>
),
unmountOnBlur: true,
}}
listeners={{}}
/>
<Drawer.Screen <Drawer.Screen
name="Sheets" name="Sheets"
component={Sheets} component={Sheets}

View file

@ -253,6 +253,12 @@ export default function HeaderRight(props) {
}} }}
/> />
<Divider /> <Divider />
<Menu.Item
title="Faire un don"
onPress={() => {
navigateTo({ name: "Contribute" });
}}
/>
<Menu.Item <Menu.Item
title="À Propos" title="À Propos"
onPress={() => { onPress={() => {

View file

@ -39,6 +39,8 @@ function getHeaderTitle(route) {
return "Alertes archivées"; return "Alertes archivées";
case "About": case "About":
return "À Propos"; return "À Propos";
case "Contribute":
return "Faire un don";
case "Location": case "Location":
return "Ma Localisation"; return "Ma Localisation";
case "NotFound": case "NotFound":

View file

@ -1,12 +1,20 @@
import React, { useState, useCallback, useEffect } from "react"; import React, { useState, useCallback, useEffect } from "react";
import { View, Image, ScrollView } from "react-native"; import {
View,
Image,
ScrollView,
TouchableOpacity,
Linking,
Alert,
} from "react-native";
import { Button } from "react-native-paper"; import { Button } from "react-native-paper";
import { useNavigation } from "@react-navigation/native";
import * as Updates from "expo-updates"; import * as Updates from "expo-updates";
import { StatusBar } from "expo-status-bar"; import { StatusBar } from "expo-status-bar";
import { MaterialIcons, AntDesign, FontAwesome } from "@expo/vector-icons"; import { MaterialIcons, AntDesign, FontAwesome } from "@expo/vector-icons";
import Text from "~/components/Text"; import Text from "~/components/Text";
import { useTheme } from "~/theme"; import { useTheme, createStyles } from "~/theme";
import { paramsActions, useParamsState } from "~/stores"; import { paramsActions, useParamsState } from "~/stores";
@ -16,7 +24,24 @@ const logo = require("~/assets/img/logo192.png");
const version = require("../../../package.json").version; const version = require("../../../package.json").version;
const openURL = async (url) => {
try {
const supported = await Linking.canOpenURL(url);
if (supported) {
await Linking.openURL(url);
} else {
Alert.alert("Erreur", "Impossible d'ouvrir ce lien");
}
} catch (error) {
Alert.alert(
"Erreur",
"Une erreur s'est produite lors de l'ouverture du lien",
);
}
};
export default function About() { export default function About() {
const navigation = useNavigation();
const textStyle = { const textStyle = {
textAlign: "left", textAlign: "left",
fontSize: 16, fontSize: 16,
@ -153,6 +178,81 @@ export default function About() {
</Text> </Text>
</View> </View>
</View> </View>
{/* Website and Contribute Buttons */}
<View style={{ paddingHorizontal: 15, paddingVertical: 10 }}>
{/* Website Button */}
<TouchableOpacity
style={{
flexDirection: "row",
alignItems: "center",
backgroundColor: colors.surface,
borderRadius: 8,
paddingVertical: 12,
paddingHorizontal: 16,
marginBottom: 10,
shadowColor: colors.shadow,
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
}}
onPress={() => openURL("https://alerte-secours.fr")}
>
<MaterialIcons
name="language"
size={24}
color={colors.primary}
style={{ marginRight: 10 }}
/>
<Text
style={{
color: colors.onSurface,
fontSize: 16,
fontWeight: "bold",
marginLeft: 5,
}}
>
Site officiel
</Text>
</TouchableOpacity>
{/* Contribute Button */}
<TouchableOpacity
style={{
flexDirection: "row",
alignItems: "center",
backgroundColor: colors.surface,
borderRadius: 8,
paddingVertical: 12,
paddingHorizontal: 16,
shadowColor: colors.shadow,
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
}}
onPress={() => navigation.navigate("Contribute")}
>
<MaterialIcons
name="favorite"
size={24}
color={colors.primary}
style={{ marginRight: 10 }}
/>
<Text
style={{
color: colors.onSurface,
fontSize: 16,
fontWeight: "bold",
marginLeft: 5,
}}
>
Contribuer au projet
</Text>
</TouchableOpacity>
</View>
<View style={{ padding: 15, justifyContent: "center" }}> <View style={{ padding: 15, justifyContent: "center" }}>
<Button <Button
mode="text" mode="text"

View file

@ -0,0 +1,282 @@
import React from "react";
import {
View,
ScrollView,
Linking,
Alert,
TouchableOpacity,
} from "react-native";
import { MaterialIcons, AntDesign } from "@expo/vector-icons";
import Text from "~/components/Text";
import { createStyles, useTheme } from "~/theme";
const openURL = async (url) => {
try {
const supported = await Linking.canOpenURL(url);
if (supported) {
await Linking.openURL(url);
} else {
Alert.alert("Erreur", "Impossible d'ouvrir ce lien");
}
} catch (error) {
Alert.alert(
"Erreur",
"Une erreur s'est produite lors de l'ouverture du lien",
);
}
};
export default function Contribute() {
const styles = useStyles();
const { colors } = useTheme();
return (
<ScrollView style={{ flex: 1 }}>
<View style={styles.container}>
{/* Header */}
<View style={styles.header}>
<MaterialIcons name="favorite" size={28} color={colors.primary} />
<Text style={styles.title}>Contribuer au projet</Text>
</View>
{/* Description */}
<Text style={styles.description}>
Alerte-Secours est une application mobile citoyenne, gratuite, sans
publicité ni exploitation de données.
{"\n\n"}
Si vous souhaitez contribuer à son développement, sa maintenance et
son indépendance :
</Text>
{/* Liberapay Button */}
<TouchableOpacity
style={[
styles.donationButton,
styles.buttonContent,
styles.liberapayButton,
]}
onPress={() => openURL("https://liberapay.com/alerte-secours")}
activeOpacity={0.8}
>
<View style={styles.iconContainer}>
<MaterialIcons name="circle" style={styles.iconDonation} />
</View>
<View style={styles.buttonTextContainer}>
<Text style={styles.buttonLabel}>Liberapay Soutien régulier</Text>
<Text style={styles.buttonDescription}>
Pour un soutien récurrent et engagé. Chaque don contribue à
assurer la stabilité du service sur le long terme.
</Text>
</View>
</TouchableOpacity>
{/* Buy Me a Coffee Button */}
<TouchableOpacity
style={[
styles.donationButton,
styles.buttonContent,
styles.buymeacoffeeButton,
]}
onPress={() => openURL("https://buymeacoffee.com/alertesecours")}
activeOpacity={0.8}
>
<View style={styles.iconContainer}>
<MaterialIcons name="local-cafe" style={styles.iconDonation} />
</View>
<View style={styles.buttonTextContainer}>
<Text style={styles.buttonLabel}>
Buy Me a Coffee Don ponctuel
</Text>
<Text style={styles.buttonDescription}>
Pour un coup de pouce ponctuel, un café virtuel pour encourager le
travail accompli !
</Text>
</View>
</TouchableOpacity>
{/* GitHub Sponsors Button */}
<TouchableOpacity
style={[
styles.donationButton,
styles.buttonContent,
styles.githubButton,
]}
onPress={() => openURL("https://github.com/sponsors/alerte-secours")}
activeOpacity={0.8}
>
<View style={styles.iconContainer}>
<AntDesign name="github" style={styles.iconDonation} />
</View>
<View style={styles.buttonTextContainer}>
<Text style={styles.buttonLabel}>GitHub Sponsors</Text>
<Text style={styles.buttonDescription}>
Pour les développeurs et utilisateurs de GitHub : soutenez le
projet directement via votre compte.
</Text>
</View>
</TouchableOpacity>
{/* Collaboration Section */}
<View style={styles.collaborationSection}>
<Text style={styles.collaborationTitle}>
Vous souhaitez nous rejoindre
</Text>
<Text style={styles.collaborationDescription}>
Ce projet fait sens pour vous et vous souhaitez vous engager dans un
projet d'avenir ?{"\n\n"}
Alerte-Secours est à la recherche de collaborateurs avec des
compétences dans le développement, la gestion de projet, le
business, la com, le graphisme. Contactez-nous si vous souhaitez
faire partie de l'aventure.
</Text>
<TouchableOpacity
style={styles.contactButton}
onPress={() => openURL("mailto:contact@alertesecours.fr")}
activeOpacity={0.8}
>
<MaterialIcons name="email" style={styles.contactIcon} />
<Text style={styles.contactText}>contact@alertesecours.fr</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
);
}
const useStyles = createStyles(({ theme: { colors, custom } }) => ({
container: {
flex: 1,
padding: 20,
},
header: {
flexDirection: "row",
alignItems: "center",
marginBottom: 24,
},
title: {
fontSize: 26,
fontWeight: "bold",
color: colors.primary,
marginLeft: 12,
},
description: {
fontSize: 16,
lineHeight: 26,
color: colors.onBackground,
marginBottom: 36,
textAlign: "left",
},
donationButton: {
marginVertical: 10,
borderRadius: 16,
elevation: 3,
shadowColor: colors.shadow,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.15,
shadowRadius: 4,
},
buttonContent: {
minHeight: 64,
paddingVertical: 16,
paddingHorizontal: 20,
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
},
buttonLabel: {
fontSize: 17,
fontWeight: "700",
textAlign: "left",
marginBottom: 4,
color: custom.donation.onDonation,
},
buttonDescription: {
fontSize: 14,
opacity: 0.9,
textAlign: "left",
lineHeight: 20,
color: custom.donation.onDonation,
},
iconContainer: {
marginRight: 18,
width: 32,
height: 32,
alignItems: "center",
justifyContent: "center",
borderRadius: 16,
backgroundColor: "rgba(255, 255, 255, 0.2)",
},
buttonTextContainer: {
flex: 1,
alignItems: "flex-start",
},
// Icon styles
iconDonation: {
fontSize: 22,
color: custom.donation.onDonation,
},
// Button background styles
liberapayButton: {
backgroundColor: custom.donation.liberapay,
},
buymeacoffeeButton: {
backgroundColor: custom.donation.buymeacoffee,
},
githubButton: {
backgroundColor: custom.donation.github,
},
// Collaboration section styles
collaborationSection: {
marginTop: 40,
padding: 20,
backgroundColor: colors.surfaceVariant,
borderRadius: 16,
borderWidth: 1,
borderColor: colors.outline,
},
collaborationTitle: {
fontSize: 22,
fontWeight: "bold",
color: colors.primary,
marginBottom: 16,
textAlign: "center",
},
collaborationDescription: {
fontSize: 16,
lineHeight: 24,
color: colors.onSurfaceVariant,
marginBottom: 20,
textAlign: "left",
},
contactButton: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
backgroundColor: colors.primary,
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 12,
elevation: 2,
shadowColor: colors.shadow,
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.1,
shadowRadius: 2,
},
contactIcon: {
fontSize: 20,
color: colors.onPrimary,
marginRight: 8,
},
contactText: {
fontSize: 16,
fontWeight: "600",
color: colors.onPrimary,
},
}));

View file

@ -0,0 +1,58 @@
import React from "react";
import { TouchableOpacity, View } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { createStyles } from "~/theme";
import Text from "~/components/Text";
import { MaterialIcons } from "@expo/vector-icons";
export default function ContributeButton() {
const navigation = useNavigation();
const styles = useStyles();
return (
<View>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate("Contribute")}
>
<MaterialIcons
name="favorite"
size={24}
color={styles.icon.color}
style={styles.icon}
/>
<Text style={styles.buttonText}>Contribuer au projet</Text>
</TouchableOpacity>
</View>
);
}
const useStyles = createStyles(({ theme: { colors } }) => ({
button: {
flexDirection: "row",
alignItems: "center",
alignSelf: "center",
marginTop: 20,
marginBottom: 10,
backgroundColor: colors.surface,
borderRadius: 8,
width: "100%",
paddingVertical: 12,
paddingHorizontal: 16,
shadowColor: colors.shadow,
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
},
buttonText: {
color: colors.onSurface,
fontSize: 16,
fontWeight: "bold",
marginLeft: 5,
},
icon: {
color: colors.primary,
marginRight: 10,
},
}));

View file

@ -12,6 +12,7 @@ import { createStyles, fontFamily } from "~/theme";
import HelpBlock from "./HelpBlock"; import HelpBlock from "./HelpBlock";
import RegisterRelativesButton from "./RegisterRelativesButton"; import RegisterRelativesButton from "./RegisterRelativesButton";
import NotificationsButton from "./NotificationsButton"; import NotificationsButton from "./NotificationsButton";
import ContributeButton from "./ContributeButton";
export default function SendAlert() { export default function SendAlert() {
const navigation = useNavigation(); const navigation = useNavigation();
@ -215,6 +216,8 @@ export default function SendAlert() {
{capitalize(levelLabel.call)} {capitalize(levelLabel.call)}
</Button> </Button>
</View> </View>
<ContributeButton />
</View> </View>
</ScrollView> </ScrollView>
); );

View file

@ -96,6 +96,13 @@ const ThemeDark = {
call: "#4c6ef5", call: "#4c6ef5",
onColor: "#FFFFFF", onColor: "#FFFFFF",
}, },
donation: {
liberapay: "#f59f00", // Same as colors.warn
buymeacoffee: "#40c057", // Same as colors.ok
github: "#b3c4ff", // Same as colors.primary (dark theme)
onDonation: "#FFFFFF",
},
}, },
}; };
export default ThemeDark; export default ThemeDark;

View file

@ -95,6 +95,13 @@ const ThemeLight = {
call: "#4c6ef5", call: "#4c6ef5",
onColor: "#FFFFFF", onColor: "#FFFFFF",
}, },
donation: {
liberapay: "#f59f00", // Same as colors.warn
buymeacoffee: "#40c057", // Same as colors.ok
github: "#1864ab", // Same as colors.primary
onDonation: "#FFFFFF",
},
}, },
}; };