[claude] Mobile: Paid job submission with inline Lightning invoice (#25) (#88)

Co-authored-by: Claude (Opus 4.6) <claude@hermes.local>
Co-committed-by: Claude (Opus 4.6) <claude@hermes.local>
This commit was merged in pull request #88.
This commit is contained in:
2026-03-23 20:20:52 +00:00
committed by rockachopa
parent 3bd67c7869
commit e41d30d308
4 changed files with 916 additions and 20 deletions

View File

@@ -14,6 +14,7 @@ import {
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ConnectionBadge } from "@/components/ConnectionBadge";
import { JobSubmissionSheet } from "@/components/JobSubmissionSheet";
import { TimmyFace } from "@/components/TimmyFace";
import { Colors } from "@/constants/colors";
import { useTimmy } from "@/context/TimmyContext";
@@ -64,6 +65,7 @@ export default function FaceScreen() {
const [isListening, setIsListening] = useState(false);
const [transcript, setTranscript] = useState("");
const [lastReply, setLastReply] = useState("");
const [jobSheetVisible, setJobSheetVisible] = useState(false);
const micScale = useRef(new Animated.Value(1)).current;
const micPulseRef = useRef<Animated.CompositeAnimation | null>(null);
const webRecognitionRef = useRef<WebSpeechRecognition | null>(null);
@@ -273,31 +275,48 @@ export default function FaceScreen() {
</View>
) : null}
{/* Mic button */}
{/* Action buttons */}
<View style={[styles.micArea, { paddingBottom: bottomPad }]}>
<Pressable
onPress={handleMicPress}
accessibilityRole="button"
accessibilityLabel={isListening ? "Stop listening" : "Start voice"}
>
<Animated.View
style={[
styles.micButton,
isListening && styles.micButtonActive,
{ transform: [{ scale: micScale }] },
]}
<View style={styles.actionRow}>
<Pressable
onPress={handleMicPress}
accessibilityRole="button"
accessibilityLabel={isListening ? "Stop listening" : "Start voice"}
>
<Ionicons
name={isListening ? "mic" : "mic-outline"}
size={32}
color={isListening ? "#fff" : C.textSecondary}
/>
</Animated.View>
</Pressable>
<Animated.View
style={[
styles.micButton,
isListening && styles.micButtonActive,
{ transform: [{ scale: micScale }] },
]}
>
<Ionicons
name={isListening ? "mic" : "mic-outline"}
size={32}
color={isListening ? "#fff" : C.textSecondary}
/>
</Animated.View>
</Pressable>
<Pressable
onPress={() => setJobSheetVisible(true)}
accessibilityRole="button"
accessibilityLabel="Submit paid job"
>
<View style={styles.jobButton}>
<Ionicons name="flash" size={26} color={C.jobStarted} />
</View>
</Pressable>
</View>
<Text style={styles.micHint}>
{isListening ? "Listening..." : "Tap to speak to Timmy"}
{isListening ? "Listening..." : "Tap mic to speak \u00B7 bolt to submit a job"}
</Text>
</View>
<JobSubmissionSheet
visible={jobSheetVisible}
onClose={() => setJobSheetVisible(false)}
/>
</View>
);
}
@@ -405,6 +424,26 @@ const styles = StyleSheet.create({
paddingTop: 16,
gap: 10,
},
actionRow: {
flexDirection: "row",
alignItems: "center",
gap: 20,
},
jobButton: {
width: 52,
height: 52,
borderRadius: 26,
backgroundColor: C.surface,
borderWidth: 1.5,
borderColor: C.jobStarted + "66",
alignItems: "center",
justifyContent: "center",
shadowColor: C.jobStarted,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 6,
elevation: 4,
},
micButton: {
width: 72,
height: 72,