fix android layout
Seto committed
Oct 24, 2025 at 22:44 UTC
bc60b99472fe95e75c7281fe734f7d35deafc11d
5 files changed
+129
-223
src-tauri/gen-override/android/app/src/main/AndroidManifest.xml
deleted
-47
@@ -1,47 +0,0 @@
1
-<?xml version="1.0" encoding="utf-8"?>
2
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
- <uses-permission android:name="android.permission.INTERNET" />
4
-
5
- <!-- AndroidTV support -->
6
- <uses-feature android:name="android.software.leanback" android:required="false" />
7
- <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
8
- <uses-feature android:name="android.hardware.faketouch" android:required="false"/>
9
- <uses-feature android:name="android.hardware.telephony" android:required="false"/>
10
- <uses-feature android:name="android.hardware.camera" android:required="false"/>
11
- <uses-feature android:name="android.hardware.nfc" android:required="false"/>
12
- <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
13
- <uses-feature android:name="android.hardware.microphone" android:required="false"/>
14
- <uses-feature android:name="android.hardware.sensor" android:required="false"/>
15
- <!-- Some TV devices have an ethernet connection only -->
16
- <uses-feature android:name="android.hardware.wifi" android:required="false"/>
17
-
18
- <application
19
- android:icon="@mipmap/ic_launcher"
20
- android:label="@string/app_name"
21
- android:theme="@style/Theme.streetcut"
22
- android:usesCleartextTraffic="${usesCleartextTraffic}">
23
- <activity
24
- android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
25
- android:launchMode="singleTask"
26
- android:label="@string/main_activity_title"
27
- android:name=".MainActivity"
28
- android:exported="true">
29
- <intent-filter>
30
- <action android:name="android.intent.action.MAIN" />
31
- <category android:name="android.intent.category.LAUNCHER" />
32
- <!-- AndroidTV support -->
33
- <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
34
- </intent-filter>
35
- </activity>
36
-
37
- <provider
38
- android:name="androidx.core.content.FileProvider"
39
- android:authorities="${applicationId}.fileprovider"
40
- android:exported="false"
41
- android:grantUriPermissions="true">
42
- <meta-data
43
- android:name="android.support.FILE_PROVIDER_PATHS"
44
- android:resource="@xml/file_paths" />
45
- </provider>
46
- </application>
47
-</manifest>
src/components/QuestionCard.tsx
deleted
-118
@@ -1,118 +0,0 @@
1
-import { useState } from "react";
2
-import { SupportedLanguage, Translation } from "../i18n";
3
-import haptics from "./../utils/haptics";
4
-
5
-interface Choice {
6
- [key: string]: string;
7
-}
8
-
9
-interface Question {
10
- q: { [key: string]: string };
11
- choices: Choice[];
12
- answer: number;
13
-}
14
-
15
-interface Lesson {
16
- title: { [key: string]: string };
17
- questions: Question[];
18
-}
19
-
20
-interface QuestionCardProps {
21
- lesson: Lesson;
22
- lang: SupportedLanguage;
23
- t: Translation;
24
- onBack: () => void;
25
-}
26
-
27
-export default function QuestionCard({
28
- lesson,
29
- lang,
30
- t,
31
- onBack,
32
-}: QuestionCardProps) {
33
- const [step, setStep] = useState(0);
34
- const [result, setResult] = useState<boolean | null>(null);
35
- const [showResult, setShowResult] = useState(false);
36
- const [animateBtn, setAnimateBtn] = useState(-1);
37
-
38
- const isRTL = lang === "fa";
39
- const dir = isRTL ? "rtl" : "ltr";
40
- const align = isRTL ? "text-right" : "text-left";
41
-
42
- const question = lesson.questions[step];
43
-
44
- function handleChoice(idx: number) {
45
- setAnimateBtn(idx);
46
- setTimeout(() => setAnimateBtn(-1), 200);
47
- const correct = idx === question.answer;
48
- setResult(correct);
49
- setShowResult(true);
50
- setTimeout(() => {
51
- setShowResult(false);
52
- setResult(null);
53
- if (step < lesson.questions.length - 1) {
54
- setStep((s) => s + 1);
55
- }
56
- }, 950);
57
- }
58
-
59
- return (
60
- <div
61
- dir={dir}
62
- className={`bg-white/80 rounded-xl shadow-lg p-6 mb-4 flex flex-col gap-3 ${align}`}
63
- >
64
- <div className="flex items-center justify-between mb-2">
65
- <button className="text-blue-600 hover:underline" onClick={onBack}>
66
- {t.back}
67
- </button>
68
- <div className="text-slate-400 text-xs">
69
- {lesson.title[lang]} • {t.question} {step + 1} {t.of}{" "}
70
- {lesson.questions.length}
71
- </div>
72
- </div>
73
- <div className="font-bold text-lg text-blue-800 mb-2">
74
- {question.q[lang]}
75
- </div>
76
- <div className="flex flex-col gap-2 mt-2">
77
- {question.choices.map((choice, idx) => (
78
- <button
79
- key={idx}
80
- className={`border px-4 py-2 rounded-lg transition-all duration-200 ${align} text-slate-700
81
- ${animateBtn === idx ? "wiggle" : ""}
82
- ${showResult && idx === question.answer ? "bg-green-200 border-green-400 font-bold" : ""}
83
- ${showResult && idx !== question.answer && idx === animateBtn ? "bg-red-200 border-red-400" : ""}
84
- hover:bg-blue-100
85
- `}
86
- disabled={showResult}
87
- onClick={async () => {
88
- await haptics.selection();
89
- handleChoice(idx);
90
- }}
91
- >
92
- {choice[lang]}
93
- </button>
94
- ))}
95
- </div>
96
- {showResult && (
97
- <div
98
- className={`mt-4 font-semibold text-lg transition-all
99
- ${result ? "text-green-700" : "text-red-600"}`}
100
- >
101
- {result ? t.correct : t.incorrect}
102
- </div>
103
- )}
104
- {!showResult &&
105
- step === lesson.questions.length - 1 &&
106
- result === null && (
107
- <div className="mt-6 text-slate-400 text-sm">{t.afterChoice}</div>
108
- )}
109
- {step === lesson.questions.length - 1 && showResult && (
110
- <div className="mt-6 text-center">
111
- <span className="inline-block bg-blue-200 text-blue-900 px-4 py-2 rounded-xl animate-bounce shadow">
112
- {t.lessonCompleted}
113
- </span>
114
- </div>
115
- )}
116
- </div>
117
- );
118
-}
src/i18n.ts
+125
-53
@@ -2,92 +2,164 @@ export interface Translation {
2
appName: string;
3
home: string;
4
cut: string;
5
+ services: string;
6
+ bookings: string;
7
profile: string;
8
welcome: string;
9
homeDesc: string;
8
- getStarted: string;
9
- startLesson: string;
10
+ bookNow: string;
11
+ viewServices: string;
12
back: string;
11
- lessonCompleted: string;
13
+ bookingConfirmed: string;
14
user: string;
15
guest: string;
14
- progress: string;
15
- completed: string;
16
- question: string;
17
- of: string;
18
- afterChoice: string;
19
- correct: string;
20
- incorrect: string;
21
- startThisLesson: string;
16
+ myBookings: string;
17
+ noBookings: string;
18
+ selectService: string;
19
+ selectTime: string;
20
+ confirm: string;
21
+ cancel: string;
22
+ service: string;
23
+ date: string;
24
+ time: string;
25
+ price: string;
26
+ duration: string;
27
+ contactUs: string;
28
+ phoneNumber: string;
29
+ location: string;
30
+ viewOnMap: string;
31
+ aboutUs: string;
32
+ ourServices: string;
33
+ haircut: string;
34
+ beardTrim: string;
35
+ fullService: string;
36
+ kidsHaircut: string;
37
+ bookingSuccess: string;
38
+ bookingError: string;
39
+ textToBook: string;
40
}
41
42
export type SupportedLanguage = "fa" | "en" | "sv";
43
44
export const translations: Record<SupportedLanguage, Translation> = {
45
fa: {
28
- appName: "رومی",
46
+ appName: "هاگرستن استریت کات",
47
home: "خانه",
48
cut: "برش",
49
+ services: "خدمات",
50
+ bookings: "رزروها",
51
profile: "پروفایل",
32
- welcome: "به رومی خوش آمدید!",
33
- homeDesc: "روشی سرگرمکننده برای یادگیری زبان فارسی.",
34
- getStarted: "شروع یادگیری",
35
- startLesson: "شروع",
52
+ welcome: "به هاگرستن استریت کات خوش آمدید!",
53
+ homeDesc: "بهترین آرایشگاه در جنوب استکهلم",
54
+ bookNow: "رزرو نوبت",
55
+ viewServices: "مشاهده خدمات",
56
back: "← بازگشت",
37
- lessonCompleted: "درس به پایان رسید! 🎉",
57
+ bookingConfirmed: "رزرو شما تایید شد! 🎉",
58
user: "نام کاربر",
59
guest: "مهمان",
40
- progress: "پیشرفت",
41
- completed: "درس تکمیل شده",
42
- question: "سوال",
43
- of: "از",
44
- afterChoice: "پس از انتخاب، نتیجه نمایش داده میشود.",
45
- correct: "درست!",
46
- incorrect: "اشتباه!",
47
- startThisLesson: "شروع درس",
60
+ myBookings: "رزروهای من",
61
+ noBookings: "هیچ رزروی وجود ندارد",
62
+ selectService: "انتخاب خدمت",
63
+ selectTime: "انتخاب زمان",
64
+ confirm: "تایید",
65
+ cancel: "لغو",
66
+ service: "خدمت",
67
+ date: "تاریخ",
68
+ time: "زمان",
69
+ price: "قیمت",
70
+ duration: "مدت زمان",
71
+ contactUs: "تماس با ما",
72
+ phoneNumber: "+46 72-853 82 88",
73
+ location: "موقعیت",
74
+ viewOnMap: "مشاهده در نقشه",
75
+ aboutUs: "درباره ما",
76
+ ourServices: "خدمات ما",
77
+ haircut: "اصلاح مو",
78
+ beardTrim: "اصلاح ریش",
79
+ fullService: "خدمات کامل",
80
+ kidsHaircut: "اصلاح مو کودکان",
81
+ bookingSuccess: "رزرو با موفقیت انجام شد!",
82
+ bookingError: "خطا در رزرو. لطفاً دوباره تلاش کنید.",
83
+ textToBook: "برای رزرو پیامک بفرستید",
84
},
85
en: {
50
- appName: "StreetCut",
86
+ appName: "Hagersten StreetCut",
87
home: "Home",
88
cut: "Cut",
89
+ services: "Services",
90
+ bookings: "Bookings",
91
profile: "Profile",
54
- welcome: "Welcome to StreetCut!",
55
- homeDesc: "A fun way to get a fresh look.",
56
- getStarted: "Start Learning",
57
- startLesson: "Start",
92
+ welcome: "Welcome to Hagersten StreetCut!",
93
+ homeDesc: "The best barber shop in South Stockholm",
94
+ bookNow: "Book Now",
95
+ viewServices: "View Services",
96
back: "← Back",
59
- lessonCompleted: "Lesson Complete! 🎉",
97
+ bookingConfirmed: "Your booking is confirmed! 🎉",
98
user: "Username",
99
guest: "Guest",
62
- progress: "Progress",
63
- completed: "lesson(s) completed",
64
- question: "Question",
65
- of: "of",
66
- afterChoice: "The result will be shown after you select.",
67
- correct: "Correct!",
68
- incorrect: "Incorrect!",
69
- startThisLesson: "Start Lesson",
100
+ myBookings: "My Bookings",
101
+ noBookings: "No bookings yet",
102
+ selectService: "Select Service",
103
+ selectTime: "Select Time",
104
+ confirm: "Confirm",
105
+ cancel: "Cancel",
106
+ service: "Service",
107
+ date: "Date",
108
+ time: "Time",
109
+ price: "Price",
110
+ duration: "Duration",
111
+ contactUs: "Contact Us",
112
+ phoneNumber: "+46 72-853 82 88",
113
+ location: "Location",
114
+ viewOnMap: "View on Map",
115
+ aboutUs: "About Us",
116
+ ourServices: "Our Services",
117
+ haircut: "Haircut",
118
+ beardTrim: "Beard Trim",
119
+ fullService: "Full Service",
120
+ kidsHaircut: "Kids Haircut",
121
+ bookingSuccess: "Booking successful!",
122
+ bookingError: "Booking error. Please try again.",
123
+ textToBook: "Text to book",
124
},
125
sv: {
72
- appName: "StreetCut",
126
+ appName: "Hagersten StreetCut",
127
home: "Hem",
128
cut: "Cut",
129
+ services: "Tjänster",
130
+ bookings: "Bokningar",
131
profile: "Profil",
76
- welcome: "Välkommen till StreetCut!",
77
- homeDesc: "Ett roligt sätt att lära sig persiska.",
78
- getStarted: "Börja lära",
79
- startLesson: "Starta",
132
+ welcome: "Välkommen till Hagersten StreetCut!",
133
+ homeDesc: "Den bästa frisören i södra Stockholm",
134
+ bookNow: "Boka nu",
135
+ viewServices: "Visa tjänster",
136
back: "← Tillbaka",
81
- lessonCompleted: "Lektion klar! 🎉",
137
+ bookingConfirmed: "Din bokning är bekräftad! 🎉",
138
user: "Användarnamn",
139
guest: "Gäst",
84
- progress: "Framsteg",
85
- completed: "lektion(er) slutförda",
86
- question: "Fråga",
87
- of: "av",
88
- afterChoice: "Resultatet visas när du har valt.",
89
- correct: "Rätt!",
90
- incorrect: "Fel!",
91
- startThisLesson: "Starta lektionen",
140
+ myBookings: "Mina bokningar",
141
+ noBookings: "Inga bokningar ännu",
142
+ selectService: "Välj tjänst",
143
+ selectTime: "Välj tid",
144
+ confirm: "Bekräfta",
145
+ cancel: "Avbryt",
146
+ service: "Tjänst",
147
+ date: "Datum",
148
+ time: "Tid",
149
+ price: "Pris",
150
+ duration: "Varaktighet",
151
+ contactUs: "Kontakta oss",
152
+ phoneNumber: "+46 72-853 82 88",
153
+ location: "Plats",
154
+ viewOnMap: "Visa på karta",
155
+ aboutUs: "Om oss",
156
+ ourServices: "Våra tjänster",
157
+ haircut: "Klippning",
158
+ beardTrim: "Skäggtrimning",
159
+ fullService: "Fullständig service",
160
+ kidsHaircut: "Barnklippning",
161
+ bookingSuccess: "Bokning lyckades!",
162
+ bookingError: "Bokningsfel. Försök igen.",
163
+ textToBook: "SMS för att boka",
164
},
165
};
src/pages/Cut.tsx
+3
-4
@@ -25,8 +25,8 @@ const BARBERSHOPS: Barbershop[] = [
25
id: 1,
26
name: {
27
fa: "هگرستن استریت کات",
28
- en: "Hagersten Street Cut",
29
- sv: "Hagersten Street Cut",
28
+ en: "Hagersten StreetCut",
29
+ sv: "Hagersten StreetCut",
30
},
31
address: {
32
fa: "هگرستنس گاتا 15، استکهلم",
@@ -217,7 +217,7 @@ const BARBERSHOPS: Barbershop[] = [
217
218
export default function Cut() {
219
const [selectedShop, setSelectedShop] = useState<Barbershop | null>(null);
220
- const { t, lang } = useLang();
220
+ const { lang } = useLang();
221
const isRTL = lang === "fa";
222
const dir = isRTL ? "rtl" : "ltr";
223
const align = isRTL ? "text-right" : "text-left";
@@ -244,7 +244,6 @@ export default function Cut() {
244
245
return (
246
<section dir={dir} className={`pb-12 ` + align}>
247
- <h2 className="text-2xl font-bold mb-2 text-blue-700">{t.cut}</h2>
247
<div className="grid gap-4">
248
{BARBERSHOPS.map((shop) => (
249
<CutCard
src/pages/Home.tsx
+1
-1
@@ -36,7 +36,7 @@ export default function Home() {
36
}}
37
className="bg-blue-500 hover:bg-blue-600 active:scale-95 transition transform text-white px-8 py-3 rounded-xl font-semibold shadow-lg hover:shadow-xl focus:ring-2 focus:ring-blue-300 animate-pulse"
38
>
39
- {t.getStarted}
39
+ {t.bookNow}
40
</Link>
41
</section>
42
);