diff --git a/apps/figgos/apps/mobile/app/(tabs)/carousel.tsx b/apps/figgos/apps/mobile/app/(tabs)/carousel.tsx
index daa92fe5e..fbbc0d4b1 100644
--- a/apps/figgos/apps/mobile/app/(tabs)/carousel.tsx
+++ b/apps/figgos/apps/mobile/app/(tabs)/carousel.tsx
@@ -11,9 +11,11 @@ import {
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useFocusEffect } from 'expo-router';
+import { Ionicons } from '@expo/vector-icons';
import type { FigureResponse, FigureRarity } from '@figgos/shared';
import { api } from '../../services/api';
import FlippableCard from '../../components/FlippableCard';
+import { shareFigure } from '../../utils/share-figure';
const { width: SCREEN_WIDTH } = Dimensions.get('window');
const CARD_WIDTH = SCREEN_WIDTH * 0.8;
@@ -185,6 +187,42 @@ export default function CarouselScreen() {
Drag to rotate ยท Double-tap to flip
+
+ shareFigure(selected)}
+ className="active:opacity-90"
+ style={{ marginTop: 24 }}
+ >
+
+
+
+
+
+ Share It
+
+
+
+
)}
diff --git a/apps/figgos/apps/mobile/app/(tabs)/collection.tsx b/apps/figgos/apps/mobile/app/(tabs)/collection.tsx
index d35d98d59..0138a9f86 100644
--- a/apps/figgos/apps/mobile/app/(tabs)/collection.tsx
+++ b/apps/figgos/apps/mobile/app/(tabs)/collection.tsx
@@ -11,9 +11,11 @@ import {
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useFocusEffect } from 'expo-router';
+import { Ionicons } from '@expo/vector-icons';
import type { FigureResponse } from '@figgos/shared';
import { api } from '../../services/api';
import FlippableCard from '../../components/FlippableCard';
+import { shareFigure } from '../../utils/share-figure';
const { width: SCREEN_WIDTH } = Dimensions.get('window');
const GAP = 10;
@@ -157,6 +159,41 @@ export default function CollectionScreen() {
Drag to rotate ยท Double-tap to flip
+ shareFigure(selected)}
+ className="active:opacity-90"
+ style={{ marginTop: 24 }}
+ >
+
+
+
+
+
+ Share It
+
+
+
+
)}
diff --git a/apps/figgos/apps/mobile/package.json b/apps/figgos/apps/mobile/package.json
index ca8391758..f70cd9fe3 100644
--- a/apps/figgos/apps/mobile/package.json
+++ b/apps/figgos/apps/mobile/package.json
@@ -22,12 +22,14 @@
"@react-navigation/native": "^7.1.8",
"expo": "~54.0.33",
"expo-constants": "~18.0.13",
+ "expo-file-system": "^19.0.21",
"expo-font": "~14.0.11",
"expo-image-manipulator": "^14.0.8",
"expo-image-picker": "^17.0.10",
"expo-linking": "~8.0.11",
"expo-router": "~6.0.23",
"expo-secure-store": "~15.0.8",
+ "expo-sharing": "~14.0.7",
"expo-splash-screen": "~31.0.13",
"expo-status-bar": "~3.0.9",
"expo-web-browser": "~15.0.10",
diff --git a/apps/figgos/apps/mobile/utils/share-figure.ts b/apps/figgos/apps/mobile/utils/share-figure.ts
new file mode 100644
index 000000000..517d43fb8
--- /dev/null
+++ b/apps/figgos/apps/mobile/utils/share-figure.ts
@@ -0,0 +1,94 @@
+import { Share, Platform, Alert } from 'react-native';
+import * as Sharing from 'expo-sharing';
+import { File, Paths } from 'expo-file-system/next';
+import type { FigureResponse, FigureRarity } from '@figgos/shared';
+
+const RARITY_EMOJI: Record = {
+ common: 'โช',
+ rare: '๐ต',
+ epic: '๐ฃ',
+ legendary: '๐ฅ',
+};
+
+const RARITY_DROP_RATE: Record = {
+ common: '60%',
+ rare: '25%',
+ epic: '12%',
+ legendary: '3%',
+};
+
+let sharing = false;
+
+export async function shareFigure(figure: FigureResponse): Promise {
+ if (!figure.imageUrl || sharing) return;
+ sharing = true;
+
+ try {
+ const ext = figure.imageUrl.endsWith('.webp') ? 'webp' : 'png';
+ const destination = new File(Paths.cache, `figgos-share-${figure.id}.${ext}`);
+
+ if (!destination.exists) {
+ await File.downloadFileAsync(figure.imageUrl, destination);
+ }
+
+ if (Platform.OS === 'ios') {
+ await Share.share({
+ message: buildShareMessage(figure),
+ url: destination.uri,
+ });
+ } else {
+ await Sharing.shareAsync(destination.uri, {
+ mimeType: ext === 'webp' ? 'image/webp' : 'image/png',
+ dialogTitle: `Share ${figure.name}`,
+ });
+ }
+ } catch {
+ Alert.alert('Share failed', 'Could not share this figure. Please try again.');
+ } finally {
+ sharing = false;
+ }
+}
+
+// Unicode Mathematical Bold map for A-Z, 0-9
+const BOLD_UPPER: Record = {
+ A: '๐', B: '๐', C: '๐', D: '๐', E: '๐', F: '๐', G: '๐', H: '๐', I: '๐',
+ J: '๐', K: '๐', L: '๐', M: '๐ ', N: '๐ก', O: '๐ข', P: '๐ฃ', Q: '๐ค', R: '๐ฅ',
+ S: '๐ฆ', T: '๐ง', U: '๐จ', V: '๐ฉ', W: '๐ช', X: '๐ซ', Y: '๐ฌ', Z: '๐ญ',
+ '0': '๐ฌ', '1': '๐ญ', '2': '๐ฎ', '3': '๐ฏ', '4': '๐ฐ',
+ '5': '๐ฑ', '6': '๐ฒ', '7': '๐ณ', '8': '๐ด', '9': '๐ต',
+};
+
+function toBold(text: string): string {
+ return [...text].map((c) => BOLD_UPPER[c] ?? c).join('');
+}
+
+function statBar(value: number): string {
+ const filled = Math.round(value / 10);
+ return 'โฐ'.repeat(filled) + 'โฑ'.repeat(10 - filled);
+}
+
+function buildShareMessage(figure: FigureResponse): string {
+ const emoji = RARITY_EMOJI[figure.rarity];
+ const rate = RARITY_DROP_RATE[figure.rarity];
+ const stats = figure.generatedProfile?.stats;
+ const serial = figure.id.split('-').pop()?.toUpperCase();
+ const line = 'โโโโโโโโโโโโโ';
+
+ let msg = line;
+ msg += `\n${emoji} ${toBold(figure.rarity.toUpperCase() + ' PULL')}`;
+ msg += `\n${line}`;
+ msg += `\n"${figure.name}"`;
+
+ if (stats) {
+ msg += `\nATK ${statBar(stats.attack)} ${stats.attack}`;
+ msg += `\nDEF ${statBar(stats.defense)} ${stats.defense}`;
+ msg += `\nSPL ${statBar(stats.special)} ${stats.special}`;
+ }
+
+ msg += `\n${serial ? `#${serial} ยท ` : ''}${rate} drop rate`;
+ msg += `\n${line}`;
+ msg += `\nThink you can pull better? โจ`;
+ msg += `\nMade with Figgos`;
+
+ return msg;
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 249a54469..874b50e42 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1603,6 +1603,9 @@ importers:
expo-constants:
specifier: ~18.0.13
version: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))
+ expo-file-system:
+ specifier: ^19.0.21
+ version: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))
expo-font:
specifier: ~14.0.11
version: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
@@ -1621,6 +1624,9 @@ importers:
expo-secure-store:
specifier: ~15.0.8
version: 15.0.8(expo@54.0.33)
+ expo-sharing:
+ specifier: ~14.0.7
+ version: 14.0.7(expo@54.0.33)
expo-splash-screen:
specifier: ~31.0.13
version: 31.0.13(expo@54.0.33)
@@ -40212,17 +40218,27 @@ snapshots:
expo: 54.0.13(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
- expo-file-system@19.0.19(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)):
+ expo-file-system@19.0.21(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)):
+ dependencies:
+ expo: 54.0.12(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
+
+ expo-file-system@19.0.21(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)):
+ dependencies:
+ expo: 54.0.13(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
+
+ expo-file-system@19.0.21(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)):
dependencies:
expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
- expo-file-system@19.0.19(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)):
+ expo-file-system@19.0.21(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)):
dependencies:
expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1)
- expo-file-system@19.0.19(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)):
+ expo-file-system@19.0.21(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)):
dependencies:
expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)
@@ -41101,6 +41117,10 @@ snapshots:
dependencies:
expo: 54.0.12(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
+ expo-sharing@14.0.7(expo@54.0.33):
+ dependencies:
+ expo: 54.0.33(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
+
expo-splash-screen@0.29.24(expo@52.0.47):
dependencies:
'@expo/prebuild-config': 8.2.0
@@ -41433,7 +41453,7 @@ snapshots:
babel-preset-expo: 54.0.7(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.12)(react-refresh@0.14.2)
expo-asset: 12.0.10(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.13(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
- expo-file-system: 19.0.19(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
+ expo-file-system: 19.0.21(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
expo-font: 14.0.11(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-keep-awake: 15.0.7(expo@54.0.12)(react@19.1.0)
expo-modules-autolinking: 3.0.14
@@ -41470,7 +41490,7 @@ snapshots:
babel-preset-expo: 54.0.7(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.13)(react-refresh@0.14.2)
expo-asset: 12.0.10(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.13(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
- expo-file-system: 19.0.19(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
+ expo-file-system: 19.0.21(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
expo-font: 14.0.11(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-keep-awake: 15.0.7(expo@54.0.13)(react@19.1.0)
expo-modules-autolinking: 3.0.15
@@ -41507,7 +41527,7 @@ snapshots:
babel-preset-expo: 54.0.7(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.25)(react-refresh@0.14.2)
expo-asset: 12.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.13(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
- expo-file-system: 19.0.19(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
+ expo-file-system: 19.0.21(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
expo-font: 14.0.11(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-keep-awake: 15.0.7(expo@54.0.25)(react@19.1.0)
expo-modules-autolinking: 3.0.22
@@ -41544,7 +41564,7 @@ snapshots:
babel-preset-expo: 54.0.7(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.25)(react-refresh@0.14.2)
expo-asset: 12.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.13(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
- expo-file-system: 19.0.19(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
+ expo-file-system: 19.0.21(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))
expo-font: 14.0.11(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0)
expo-keep-awake: 15.0.7(expo@54.0.25)(react@19.1.0)
expo-modules-autolinking: 3.0.22
@@ -41581,7 +41601,7 @@ snapshots:
babel-preset-expo: 54.0.7(@babel/core@7.28.5)(@babel/runtime@7.28.4)(expo@54.0.25)(react-refresh@0.14.2)
expo-asset: 12.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)
expo-constants: 18.0.13(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))
- expo-file-system: 19.0.19(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))
+ expo-file-system: 19.0.21(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))
expo-font: 14.0.11(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1)
expo-keep-awake: 15.0.7(expo@54.0.25)(react@18.3.1)
expo-modules-autolinking: 3.0.22