Skip to content

Commit 907f4d7

Browse files
Add mobile project onboarding flow
- Add new project screens for local folders and remote repositories - Support source-control provider lookup, clone destination selection, and folder browsing - Extract shared add-project path helpers and add coverage for the new runtime logic
1 parent 1e929ad commit 907f4d7

19 files changed

Lines changed: 2189 additions & 269 deletions

apps/mobile/src/app/new/_layout.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export default function NewTaskLayout() {
1818
}}
1919
>
2020
<Stack.Screen name="index" options={{ animation: "none" }} />
21+
<Stack.Screen
22+
name="add-project"
23+
options={{ animation: "slide_from_right", headerShown: false }}
24+
/>
2125
<Stack.Screen name="draft" options={{ animation: "slide_from_right" }} />
2226
</Stack>
2327
</NewTaskFlowProvider>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Stack } from "expo-router";
2+
import { useThemeColor } from "../../../lib/useThemeColor";
3+
4+
export default function AddProjectLayout() {
5+
const sheetStyle = {
6+
backgroundColor: useThemeColor("--color-sheet"),
7+
};
8+
9+
return (
10+
<Stack screenOptions={{ headerShown: false, contentStyle: sheetStyle }}>
11+
<Stack.Screen name="index" options={{ animation: "slide_from_right" }} />
12+
<Stack.Screen name="repository" options={{ animation: "slide_from_right" }} />
13+
<Stack.Screen name="destination" options={{ animation: "slide_from_right" }} />
14+
<Stack.Screen name="local" options={{ animation: "slide_from_right" }} />
15+
</Stack>
16+
);
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Stack } from "expo-router";
2+
3+
import { AddProjectDestinationScreen } from "../../../features/projects/AddProjectScreen";
4+
5+
export default function AddProjectDestinationRoute() {
6+
return (
7+
<>
8+
<Stack.Screen options={{ headerShown: false }} />
9+
<AddProjectDestinationScreen />
10+
</>
11+
);
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Stack } from "expo-router";
2+
3+
import { AddProjectSourceScreen } from "../../../features/projects/AddProjectScreen";
4+
5+
export default function AddProjectRoute() {
6+
return (
7+
<>
8+
<Stack.Screen options={{ headerShown: false }} />
9+
<AddProjectSourceScreen />
10+
</>
11+
);
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Stack } from "expo-router";
2+
3+
import { AddProjectLocalFolderScreen } from "../../../features/projects/AddProjectScreen";
4+
5+
export default function AddProjectLocalRoute() {
6+
return (
7+
<>
8+
<Stack.Screen options={{ headerShown: false }} />
9+
<AddProjectLocalFolderScreen />
10+
</>
11+
);
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Stack } from "expo-router";
2+
3+
import { AddProjectRepositoryScreen } from "../../../features/projects/AddProjectScreen";
4+
5+
export default function AddProjectRepositoryRoute() {
6+
return (
7+
<>
8+
<Stack.Screen options={{ headerShown: false }} />
9+
<AddProjectRepositoryScreen />
10+
</>
11+
);
12+
}

apps/mobile/src/app/new/index.tsx

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,21 @@ export default function NewTaskRoute() {
142142
Add environment
143143
</Text>
144144
</Pressable>
145-
) : null}
145+
) : (
146+
<Pressable
147+
className="mt-1 rounded-full bg-primary px-4 py-2.5 active:opacity-70"
148+
onPress={() => router.push("/new/add-project")}
149+
>
150+
<Text className="text-[13px] font-t3-bold text-primary-foreground">
151+
Add new project
152+
</Text>
153+
</Pressable>
154+
)}
146155
</View>
147156
) : (
148157
<View collapsable={false} className="overflow-hidden rounded-[24px] bg-card">
149158
{items.map((item, index) => {
150159
const isFirst = index === 0;
151-
const isLast = index === items.length - 1;
152160

153161
return (
154162
<Link
@@ -172,8 +180,8 @@ export default function NewTaskRoute() {
172180
borderTopColor: borderSubtleColor,
173181
borderTopLeftRadius: isFirst ? 24 : 0,
174182
borderTopRightRadius: isFirst ? 24 : 0,
175-
borderBottomLeftRadius: isLast ? 24 : 0,
176-
borderBottomRightRadius: isLast ? 24 : 0,
183+
borderBottomLeftRadius: 0,
184+
borderBottomRightRadius: 0,
177185
}}
178186
>
179187
<View className="flex-row items-center justify-between gap-3">
@@ -198,6 +206,33 @@ export default function NewTaskRoute() {
198206
</Link>
199207
);
200208
})}
209+
<Pressable
210+
className="bg-card"
211+
style={{
212+
paddingHorizontal: 16,
213+
paddingVertical: 18,
214+
borderTopWidth: 1,
215+
borderTopColor: borderSubtleColor,
216+
borderBottomLeftRadius: 24,
217+
borderBottomRightRadius: 24,
218+
}}
219+
onPress={() => router.push("/new/add-project")}
220+
>
221+
<View className="flex-row items-center justify-between gap-3">
222+
<View className="h-[22px] w-[22px] items-center justify-center rounded-full bg-subtle">
223+
<SymbolView name="plus" size={13} tintColor={accentColor} type="monochrome" />
224+
</View>
225+
<View className="flex-1">
226+
<Text className="text-[18px] font-t3-bold">Add new project</Text>
227+
</View>
228+
<SymbolView
229+
name="chevron.right"
230+
size={14}
231+
tintColor={chevronColor}
232+
type="monochrome"
233+
/>
234+
</View>
235+
</Pressable>
201236
</View>
202237
)}
203238
</ScrollView>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import Svg, { Defs, LinearGradient, Path, Stop } from "react-native-svg";
2+
3+
export type SourceControlIconKind = "github" | "gitlab" | "bitbucket" | "azure-devops";
4+
5+
export function SourceControlIcon(props: {
6+
readonly kind: SourceControlIconKind;
7+
readonly size?: number;
8+
readonly color?: string;
9+
}) {
10+
const size = props.size ?? 18;
11+
12+
switch (props.kind) {
13+
case "github":
14+
return (
15+
<Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
16+
<Path
17+
fillRule="evenodd"
18+
clipRule="evenodd"
19+
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82A7.68 7.68 0 0 1 8.02 3.86c.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"
20+
fill={props.color ?? "#24292F"}
21+
/>
22+
</Svg>
23+
);
24+
case "gitlab":
25+
return (
26+
<Svg width={size} height={size} viewBox="0 0 32 32" fill="none">
27+
<Path
28+
d="m31.46 12.78-.04-.12-4.35-11.35A1.14 1.14 0 0 0 25.94.6c-.24 0-.47.1-.66.24-.19.15-.33.36-.39.6l-2.94 9h-11.9l-2.94-9A1.14 1.14 0 0 0 6.07.58a1.15 1.15 0 0 0-1.14.72L.58 12.68l-.05.11a8.1 8.1 0 0 0 2.68 9.34l.02.01.04.03 6.63 4.97 3.28 2.48 2 1.52a1.35 1.35 0 0 0 1.62 0l2-1.52 3.28-2.48 6.67-5h.02a8.09 8.09 0 0 0 2.7-9.36Z"
29+
fill="#E24329"
30+
/>
31+
<Path
32+
d="m31.46 12.78-.04-.12a14.75 14.75 0 0 0-5.86 2.64l-9.55 7.24 6.09 4.6 6.67-5h.02a8.09 8.09 0 0 0 2.67-9.36Z"
33+
fill="#FC6D26"
34+
/>
35+
<Path
36+
d="m9.9 27.14 3.28 2.48 2 1.52a1.35 1.35 0 0 0 1.62 0l2-1.52 3.28-2.48-6.1-4.6-6.07 4.6Z"
37+
fill="#FCA326"
38+
/>
39+
<Path
40+
d="M6.44 15.3a14.71 14.71 0 0 0-5.86-2.63l-.05.12a8.1 8.1 0 0 0 2.68 9.34l.02.01.04.03 6.63 4.97 6.1-4.6-9.56-7.24Z"
41+
fill="#FC6D26"
42+
/>
43+
</Svg>
44+
);
45+
case "azure-devops":
46+
return (
47+
<Svg width={size} height={size} viewBox="0 0 96 96">
48+
<Defs>
49+
<LinearGradient id="azure-a" x1="42.83" x2="15.79" y1="12.69" y2="92.57">
50+
<Stop offset="0" stopColor="#114A8B" />
51+
<Stop offset="1" stopColor="#0669BC" />
52+
</LinearGradient>
53+
<LinearGradient id="azure-b" x1="47.84" x2="77.52" y1="10.36" y2="89.44">
54+
<Stop offset="0" stopColor="#3CCBF4" />
55+
<Stop offset="1" stopColor="#2892DF" />
56+
</LinearGradient>
57+
</Defs>
58+
<Path
59+
fill="url(#azure-a)"
60+
d="M33.34 6.54h26.04l-27.03 80.1a4.15 4.15 0 0 1-3.94 2.81H8.15a4.14 4.14 0 0 1-3.93-5.47L29.4 9.38a4.15 4.15 0 0 1 3.94-2.83z"
61+
/>
62+
<Path
63+
fill="#0078D4"
64+
d="M71.17 60.26H29.88a1.91 1.91 0 0 0-1.3 3.31l26.53 24.76a4.17 4.17 0 0 0 2.85 1.13h23.38z"
65+
/>
66+
<Path
67+
fill="url(#azure-b)"
68+
d="M66.6 9.36a4.14 4.14 0 0 0-3.93-2.82H33.65a4.15 4.15 0 0 1 3.93 2.82l25.18 74.62a4.15 4.15 0 0 1-3.93 5.48h29.02a4.15 4.15 0 0 0 3.93-5.48z"
69+
/>
70+
</Svg>
71+
);
72+
case "bitbucket":
73+
return (
74+
<Svg width={size} height={size} viewBox="8.4 14.39 2481.29 2231.21">
75+
<Defs>
76+
<LinearGradient
77+
id="bitbucket-a"
78+
x1="945.1094"
79+
y1="1524.8389"
80+
x2="944.4923"
81+
y2="1524.1893"
82+
gradientTransform="matrix(1996.6343 0 0 -1480.3047 -1884485.625 2258195)"
83+
>
84+
<Stop offset="0.18" stopColor="#0052CC" />
85+
<Stop offset="1" stopColor="#2684FF" />
86+
</LinearGradient>
87+
</Defs>
88+
<Path
89+
fill="#2684FF"
90+
d="M88.92,14.4C45.02,13.83,8.97,48.96,8.41,92.86c-0.06,4.61,0.28,9.22,1.02,13.77l337.48,2048.72 c8.68,51.75,53.26,89.8,105.74,90.24h1619.03c39.38,0.5,73.19-27.9,79.49-66.78l337.49-2071.78c7.03-43.34-22.41-84.17-65.75-91.2 c-4.55-0.74-9.15-1.08-13.76-1.02L88.92,14.4z M1509.99,1495.09H993.24l-139.92-731h781.89L1509.99,1495.09z"
91+
/>
92+
<Path
93+
fill="url(#bitbucket-a)"
94+
d="M2379.27,763.06h-745.5l-125.12,730.42H992.31l-609.67,723.67c19.32,16.71,43.96,26,69.5,26.21h1618.13 c39.35,0.51,73.14-27.88,79.44-66.72L2379.27,763.06z"
95+
/>
96+
</Svg>
97+
);
98+
}
99+
}

0 commit comments

Comments
 (0)