Skip to content

Commit 497c024

Browse files
YuvalYuval
authored andcommitted
feat: add support for Android back button navigation and integrate capacitor-app dependency
1 parent d76c61b commit 497c024

7 files changed

Lines changed: 185 additions & 119 deletions

File tree

03-Client/android/app/capacitor.build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ android {
99

1010
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
1111
dependencies {
12+
implementation project(':capacitor-app')
1213
implementation project(':capacitor-camera')
1314
implementation project(':capacitor-filesystem')
1415
implementation project(':capacitor-geolocation')

03-Client/android/capacitor.settings.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
include ':capacitor-android'
33
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
44

5+
include ':capacitor-app'
6+
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
7+
58
include ':capacitor-camera'
69
project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')
710

03-Client/package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

03-Client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@capacitor/android": "^8.3.1",
17+
"@capacitor/app": "^8.1.0",
1718
"@capacitor/camera": "^8.1.0",
1819
"@capacitor/cli": "^8.3.1",
1920
"@capacitor/core": "^8.3.1",

03-Client/src/app/SwipeBackProvider.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { Outlet } from "react-router";
22
import { useSwipeBack } from "./hooks/useSwipeBack";
3+
import { useAndroidBackButton } from "./hooks/useAndroidBackButton";
34
import { AuthProvider } from "./context/AuthContext";
45

56
export function SwipeBackProvider() {
67
useSwipeBack();
8+
useAndroidBackButton();
79
return (
810
<AuthProvider>
911
<Outlet />

03-Client/src/app/components/LoginScreen.tsx

Lines changed: 126 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -59,146 +59,153 @@ export function LoginScreen() {
5959
}
6060

6161
return (
62-
<div className="relative w-full h-full bg-white overflow-hidden flex flex-col" dir="rtl">
62+
<div className="relative w-full h-full bg-white flex flex-col" dir="rtl">
6363
<BackgroundDecorations />
6464

65-
{/* Logo */}
65+
{/* Scrollable content column — scrolls only if a short viewport + error can't fit */}
6666
<div
67-
className="relative z-10 flex justify-center shrink-0"
68-
style={{ marginTop: "clamp(16px, 5dvh, 60px)" }}
69-
>
70-
<div
71-
className="flex items-center justify-center"
72-
style={{
73-
width: "clamp(88px, 16dvh, 128px)",
74-
height: "clamp(103px, 18.75dvh, 150px)",
75-
}}
76-
>
77-
<img src={imgLogo} alt="Logo" className="w-full h-full object-contain" />
78-
</div>
79-
</div>
80-
81-
{/* Header */}
82-
<div className="relative z-10 text-center mt-2 shrink-0">
83-
<h1
84-
className="text-[#063c55] font-['Heebo',sans-serif]"
85-
style={{ fontSize: "clamp(22px, 3.5dvh, 30px)", fontWeight: 700 }}
86-
>
87-
התחברו ל-GroundShare
88-
</h1>
89-
<p
90-
className="text-[#f68d1e] font-['Heebo',sans-serif] mt-1"
91-
style={{ fontSize: "clamp(16px, 2.4dvh, 20px)", fontWeight: 600 }}
92-
>
93-
התגעגענו!
94-
</p>
95-
</div>
96-
97-
{/* Form */}
98-
<div
99-
className="relative z-10 flex flex-col px-8 shrink-0"
67+
className="relative z-10 flex-1 min-h-0 overflow-y-auto flex flex-col"
10068
style={{
101-
marginTop: "clamp(18px, 5.5dvh, 60px)",
102-
gap: "clamp(10px, 2.2dvh, 20px)",
69+
paddingTop: "clamp(12px, 3.5dvh, 48px)",
70+
paddingBottom: "clamp(16px, 4dvh, 36px)",
10371
}}
10472
>
105-
<div className="relative">
106-
<input
107-
type="email"
108-
value={email}
109-
onChange={(e) => setEmail(e.target.value)}
110-
placeholder="מייל"
111-
dir="rtl"
112-
className="w-full bg-[#f3fbfd] border-2 border-transparent hover:border-[#063c55] focus:border-[#063c55] transition-colors duration-200 rounded-[10px] px-[35px] text-right font-['Heebo',sans-serif] text-[#626262] text-[16px] outline-none shadow-[inset_4px_14px_16.2px_0px_rgba(0,0,0,0.04)]"
113-
style={{ paddingTop: "clamp(12px, 2dvh, 18px)", paddingBottom: "clamp(12px, 2dvh, 18px)" }}
114-
/>
115-
</div>
116-
<div className="relative">
117-
<input
118-
type={showPassword ? "text" : "password"}
119-
value={password}
120-
onChange={(e) => setPassword(e.target.value)}
121-
placeholder="סיסמא"
122-
dir="rtl"
123-
className="w-full bg-[#f3fbfd] border-2 border-transparent hover:border-[#063c55] focus:border-[#063c55] transition-colors duration-200 rounded-[10px] px-[35px] text-right font-['Heebo',sans-serif] text-[#626262] text-[16px] outline-none shadow-[inset_4px_14px_16.2px_0px_rgba(0,0,0,0.04)]"
124-
style={{ paddingTop: "clamp(12px, 2dvh, 18px)", paddingBottom: "clamp(12px, 2dvh, 18px)" }}
125-
/>
126-
<button
127-
type="button"
128-
onClick={() => setShowPassword((v) => !v)}
129-
className="absolute left-[12px] top-1/2 -translate-y-1/2 p-[4px] cursor-pointer"
130-
tabIndex={-1}
73+
{/* Logo */}
74+
<div className="flex justify-center shrink-0">
75+
<div
76+
className="flex items-center justify-center"
77+
style={{
78+
width: "clamp(80px, 14dvh, 128px)",
79+
height: "clamp(94px, 16.4dvh, 150px)",
80+
}}
13181
>
132-
<EyeIcon open={showPassword} />
133-
</button>
82+
<img src={imgLogo} alt="Logo" className="w-full h-full object-contain" />
83+
</div>
13484
</div>
135-
</div>
13685

137-
{/* Forgot Password */}
138-
<div className="relative z-10 px-8 mt-3 shrink-0">
139-
<p className="text-right font-['Heebo',sans-serif] text-[#626262] text-[14px] cursor-pointer" style={{ fontWeight: 600 }}>
140-
שכחת סיסמא?
141-
</p>
142-
</div>
143-
144-
{/* Error */}
145-
{error && (
146-
<div className="relative z-10 px-8 mt-3 shrink-0">
147-
<p className="text-center font-['Heebo',sans-serif] text-[#FF616D] text-[14px]" style={{ fontWeight: 600 }}>
148-
{error}
86+
{/* Header */}
87+
<div className="text-center mt-2 shrink-0">
88+
<h1
89+
className="text-[#063c55] font-['Heebo',sans-serif]"
90+
style={{ fontSize: "clamp(22px, 3.3dvh, 30px)", fontWeight: 700 }}
91+
>
92+
התחברו ל-GroundShare
93+
</h1>
94+
<p
95+
className="text-[#f68d1e] font-['Heebo',sans-serif] mt-1"
96+
style={{ fontSize: "clamp(15px, 2.2dvh, 20px)", fontWeight: 600 }}
97+
>
98+
התגעגענו!
14999
</p>
150100
</div>
151-
)}
152101

153-
{/* Actions */}
154-
<div
155-
className="relative z-10 flex flex-col items-center px-8 shrink-0"
156-
style={{
157-
marginTop: "clamp(14px, 3dvh, 30px)",
158-
gap: "clamp(10px, 2.2dvh, 20px)",
159-
}}
160-
>
161-
<button
162-
onClick={handleLogin}
163-
disabled={loading}
164-
className="w-full bg-[#063c55] text-white rounded-[10px] font-['Heebo',sans-serif] cursor-pointer transition-transform active:scale-[0.98] shadow-[0px_10px_20px_0px_rgba(6,60,85,0.15)] disabled:opacity-60"
102+
{/* Form */}
103+
<div
104+
className="flex flex-col px-8 shrink-0"
165105
style={{
166-
fontSize: "clamp(17px, 2.3dvh, 20px)",
167-
fontWeight: 600,
168-
paddingTop: "clamp(11px, 1.7dvh, 15px)",
169-
paddingBottom: "clamp(11px, 1.7dvh, 15px)",
106+
marginTop: "clamp(14px, 4dvh, 48px)",
107+
gap: "clamp(9px, 1.8dvh, 18px)",
170108
}}
171109
>
172-
{loading ? "מתחבר..." : "התחברות"}
173-
</button>
174-
<div className="w-full flex items-center gap-[10px]" dir="rtl">
175-
<div className="flex-1 h-[1px] bg-[#063c55]/20" />
176-
<span className="font-['Heebo',sans-serif] text-[#063c55]/70 text-[12px]" style={{ fontWeight: 500 }}>או</span>
177-
<div className="flex-1 h-[1px] bg-[#063c55]/20" />
110+
<div className="relative">
111+
<input
112+
type="email"
113+
value={email}
114+
onChange={(e) => setEmail(e.target.value)}
115+
placeholder="מייל"
116+
dir="rtl"
117+
className="w-full bg-[#f3fbfd] border-2 border-transparent hover:border-[#063c55] focus:border-[#063c55] transition-colors duration-200 rounded-[10px] px-[35px] text-right font-['Heebo',sans-serif] text-[#626262] text-[16px] outline-none shadow-[inset_4px_14px_16.2px_0px_rgba(0,0,0,0.04)]"
118+
style={{ paddingTop: "clamp(11px, 1.8dvh, 16px)", paddingBottom: "clamp(11px, 1.8dvh, 16px)" }}
119+
/>
120+
</div>
121+
<div className="relative">
122+
<input
123+
type={showPassword ? "text" : "password"}
124+
value={password}
125+
onChange={(e) => setPassword(e.target.value)}
126+
placeholder="סיסמא"
127+
dir="rtl"
128+
className="w-full bg-[#f3fbfd] border-2 border-transparent hover:border-[#063c55] focus:border-[#063c55] transition-colors duration-200 rounded-[10px] px-[35px] text-right font-['Heebo',sans-serif] text-[#626262] text-[16px] outline-none shadow-[inset_4px_14px_16.2px_0px_rgba(0,0,0,0.04)]"
129+
style={{ paddingTop: "clamp(11px, 1.8dvh, 16px)", paddingBottom: "clamp(11px, 1.8dvh, 16px)" }}
130+
/>
131+
<button
132+
type="button"
133+
onClick={() => setShowPassword((v) => !v)}
134+
className="absolute left-[12px] top-1/2 -translate-y-1/2 p-[4px] cursor-pointer"
135+
tabIndex={-1}
136+
>
137+
<EyeIcon open={showPassword} />
138+
</button>
139+
</div>
140+
</div>
141+
142+
{/* Forgot Password */}
143+
<div className="px-8 mt-2 shrink-0">
144+
<p className="text-right font-['Heebo',sans-serif] text-[#626262] text-[14px] cursor-pointer" style={{ fontWeight: 600 }}>
145+
שכחת סיסמא?
146+
</p>
178147
</div>
179-
<button
180-
onClick={() => navigate("/register")}
181-
className="w-full bg-white text-[#063c55] rounded-[10px] font-['Heebo',sans-serif] cursor-pointer transition-transform active:scale-[0.98] hover:bg-[#f3fbfd]"
148+
149+
{/* Error */}
150+
{error && (
151+
<div className="px-8 mt-2 shrink-0">
152+
<p className="text-center font-['Heebo',sans-serif] text-[#FF616D] text-[14px]" style={{ fontWeight: 600 }}>
153+
{error}
154+
</p>
155+
</div>
156+
)}
157+
158+
{/* Actions */}
159+
<div
160+
className="flex flex-col items-center px-8 shrink-0"
182161
style={{
183-
fontSize: "clamp(14px, 1.9dvh, 16px)",
184-
fontWeight: 600,
185-
border: "2px solid #063c55",
186-
paddingTop: "clamp(9px, 1.5dvh, 13px)",
187-
paddingBottom: "clamp(9px, 1.5dvh, 13px)",
162+
marginTop: "clamp(10px, 2.2dvh, 22px)",
163+
gap: "clamp(8px, 1.8dvh, 16px)",
188164
}}
189165
>
190-
אני רוצה להירשם
191-
</button>
192-
</div>
166+
<button
167+
onClick={handleLogin}
168+
disabled={loading}
169+
className="w-full bg-[#063c55] text-white rounded-[10px] font-['Heebo',sans-serif] cursor-pointer transition-transform active:scale-[0.98] shadow-[0px_10px_20px_0px_rgba(6,60,85,0.15)] disabled:opacity-60"
170+
style={{
171+
fontSize: "clamp(17px, 2.3dvh, 20px)",
172+
fontWeight: 600,
173+
paddingTop: "clamp(10px, 1.5dvh, 14px)",
174+
paddingBottom: "clamp(10px, 1.5dvh, 14px)",
175+
}}
176+
>
177+
{loading ? "מתחבר..." : "התחברות"}
178+
</button>
179+
<div className="w-full flex items-center gap-[10px]" dir="rtl">
180+
<div className="flex-1 h-[1px] bg-[#063c55]/20" />
181+
<span className="font-['Heebo',sans-serif] text-[#063c55]/70 text-[12px]" style={{ fontWeight: 500 }}>או</span>
182+
<div className="flex-1 h-[1px] bg-[#063c55]/20" />
183+
</div>
184+
<button
185+
onClick={() => navigate("/register")}
186+
className="w-full bg-white text-[#063c55] rounded-[10px] font-['Heebo',sans-serif] cursor-pointer transition-transform active:scale-[0.98] hover:bg-[#f3fbfd]"
187+
style={{
188+
fontSize: "clamp(14px, 1.9dvh, 16px)",
189+
fontWeight: 600,
190+
border: "2px solid #063c55",
191+
paddingTop: "clamp(8px, 1.3dvh, 12px)",
192+
paddingBottom: "clamp(8px, 1.3dvh, 12px)",
193+
}}
194+
>
195+
אני רוצה להירשם
196+
</button>
197+
</div>
193198

194-
<div className="flex-1 min-h-[8px]" />
199+
{/* Elastic spacer — collapses when the viewport is tight */}
200+
<div className="flex-1 min-h-[12px]" />
195201

196-
{/* Social Login */}
197-
<div
198-
className="relative z-10 shrink-0"
199-
style={{ marginBottom: "clamp(16px, 4.5dvh, 40px)" }}
200-
>
201-
<SocialLoginButtons label="התחברות באמצעות" />
202+
{/* Social Login — stays inside the scroll flow so its error/loading never clips */}
203+
<div
204+
className="shrink-0"
205+
style={{ marginTop: "clamp(12px, 2.5dvh, 24px)" }}
206+
>
207+
<SocialLoginButtons label="התחברות באמצעות" />
208+
</div>
202209
</div>
203210
</div>
204211
);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useEffect } from "react";
2+
import { useNavigate, useLocation } from "react-router";
3+
import { Capacitor, type PluginListenerHandle } from "@capacitor/core";
4+
import { App } from "@capacitor/app";
5+
6+
const ROOT_PATHS = new Set<string>(["/", "/main"]);
7+
8+
export function useAndroidBackButton(): void {
9+
const navigate = useNavigate();
10+
const location = useLocation();
11+
12+
useEffect(() => {
13+
if (!Capacitor.isNativePlatform()) return;
14+
if (Capacitor.getPlatform() !== "android") return;
15+
16+
let handle: PluginListenerHandle | undefined;
17+
let cancelled = false;
18+
19+
const register = async () => {
20+
const listener = await App.addListener("backButton", ({ canGoBack }) => {
21+
const onRoot = ROOT_PATHS.has(location.pathname);
22+
if (onRoot || !canGoBack) {
23+
void App.exitApp();
24+
return;
25+
}
26+
navigate(-1);
27+
});
28+
if (cancelled) {
29+
void listener.remove();
30+
return;
31+
}
32+
handle = listener;
33+
};
34+
35+
void register();
36+
37+
return () => {
38+
cancelled = true;
39+
void handle?.remove();
40+
};
41+
}, [navigate, location.pathname]);
42+
}

0 commit comments

Comments
 (0)