Official URL migration preserves sessions, shortcuts prefs, and last clock-in client; auth screens match web with legal links; time clock and invoice editor/send flows are updated for the new domain and UI patterns. Co-authored-by: Cursor <cursoragent@cursor.com>
140 lines
4.1 KiB
TypeScript
140 lines
4.1 KiB
TypeScript
import { Ionicons } from "@expo/vector-icons";
|
|
import * as Linking from "expo-linking";
|
|
import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
|
|
|
|
import { Button } from "@/components/ui/Button";
|
|
import { fonts, spacing } from "@/constants/theme";
|
|
import { useAppTheme } from "@/contexts/ThemeContext";
|
|
import { SHORTCUT_URLS } from "@/lib/shortcuts";
|
|
|
|
const SHORTCUT_ACTIONS = [
|
|
{ title: "Clock In", subtitle: "Start timer with your last client", url: SHORTCUT_URLS.clockIn },
|
|
{ title: "Clock Out", subtitle: "Stop the running timer", url: SHORTCUT_URLS.clockOut },
|
|
{ title: "Open Time Clock", subtitle: "Jump to the timer tab", url: SHORTCUT_URLS.openTimer },
|
|
] as const;
|
|
|
|
export function ShortcutsSetupCard() {
|
|
const { colors } = useAppTheme();
|
|
|
|
if (Platform.OS !== "ios") {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<View style={styles.stack}>
|
|
<Text style={[styles.lead, { color: colors.mutedForeground }]}>
|
|
beenvoice actions appear when you build a shortcut — they are not pre-installed in your
|
|
library. After installing a native build (not Expo Go), open the app once, then:
|
|
</Text>
|
|
|
|
<View style={[styles.steps, { borderColor: colors.border, backgroundColor: colors.muted }]}>
|
|
<Text style={[styles.step, { color: colors.foreground }]}>
|
|
1. Open the Shortcuts app → tap + → Add Action
|
|
</Text>
|
|
<Text style={[styles.step, { color: colors.foreground }]}>
|
|
2. Search <Text style={styles.emphasis}>beenvoice</Text> (or Clock In / Clock Out)
|
|
</Text>
|
|
<Text style={[styles.step, { color: colors.foreground }]}>
|
|
3. Pick Clock In, Clock Out, or Open Time Clock
|
|
</Text>
|
|
</View>
|
|
|
|
<Text style={[styles.meta, { color: colors.mutedForeground }]}>
|
|
You can also ask Siri: “Clock in with beenvoice”. Pick a client once on the Timer tab
|
|
before your first clock-in shortcut.
|
|
</Text>
|
|
|
|
<Text style={[styles.meta, { color: colors.mutedForeground }]}>
|
|
If nothing shows up, reinstall from a fresh native build (TestFlight or{" "}
|
|
<Text style={styles.emphasis}>bun run ios</Text>). Shortcuts require iOS 18+.
|
|
</Text>
|
|
|
|
<View style={styles.actions}>
|
|
{SHORTCUT_ACTIONS.map((action) => (
|
|
<Pressable
|
|
key={action.title}
|
|
accessibilityRole="button"
|
|
onPress={() => void Linking.openURL(action.url)}
|
|
style={({ pressed }) => [
|
|
styles.actionRow,
|
|
{
|
|
borderColor: colors.border,
|
|
backgroundColor: pressed ? colors.muted : "transparent",
|
|
},
|
|
]}
|
|
>
|
|
<View style={styles.actionCopy}>
|
|
<Text style={[styles.actionTitle, { color: colors.foreground }]}>{action.title}</Text>
|
|
<Text style={[styles.actionSubtitle, { color: colors.mutedForeground }]}>
|
|
{action.subtitle}
|
|
</Text>
|
|
</View>
|
|
<Ionicons name="open-outline" size={18} color={colors.mutedForeground} />
|
|
</Pressable>
|
|
))}
|
|
</View>
|
|
|
|
<Button
|
|
title="Open Shortcuts app"
|
|
variant="secondary"
|
|
onPress={() => void Linking.openURL("shortcuts://")}
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
stack: {
|
|
gap: spacing.md,
|
|
},
|
|
lead: {
|
|
fontFamily: fonts.body,
|
|
fontSize: 14,
|
|
lineHeight: 20,
|
|
},
|
|
steps: {
|
|
borderWidth: 1,
|
|
borderRadius: 12,
|
|
gap: spacing.sm,
|
|
padding: spacing.md,
|
|
},
|
|
step: {
|
|
fontFamily: fonts.body,
|
|
fontSize: 14,
|
|
lineHeight: 20,
|
|
},
|
|
emphasis: {
|
|
fontFamily: fonts.bodyMedium,
|
|
},
|
|
meta: {
|
|
fontFamily: fonts.body,
|
|
fontSize: 13,
|
|
lineHeight: 18,
|
|
},
|
|
actions: {
|
|
gap: spacing.sm,
|
|
},
|
|
actionRow: {
|
|
alignItems: "center",
|
|
borderRadius: 12,
|
|
borderWidth: 1,
|
|
flexDirection: "row",
|
|
gap: spacing.md,
|
|
paddingHorizontal: spacing.md,
|
|
paddingVertical: spacing.sm,
|
|
},
|
|
actionCopy: {
|
|
flex: 1,
|
|
gap: 2,
|
|
},
|
|
actionTitle: {
|
|
fontFamily: fonts.bodyMedium,
|
|
fontSize: 15,
|
|
},
|
|
actionSubtitle: {
|
|
fontFamily: fonts.body,
|
|
fontSize: 13,
|
|
lineHeight: 18,
|
|
},
|
|
});
|