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 );