sare26 commited on
Commit
27fdebe
·
verified ·
1 Parent(s): ba4f48e

Update artifacts/fb-publisher/src/App.tsx

Browse files
Files changed (1) hide show
  1. artifacts/fb-publisher/src/App.tsx +215 -16
artifacts/fb-publisher/src/App.tsx CHANGED
@@ -1,18 +1,41 @@
1
- import { useEffect, useState } from "react";
2
  import { createClient } from "@supabase/supabase-js";
3
  import { Auth } from "@supabase/auth-ui-react";
4
  import { ThemeSupa } from "@supabase/auth-ui-shared";
5
  import { setAuthTokenGetter } from "@workspace/api-client-react";
6
- import { Switch, Route, Router as WouterRouter, Redirect } from "wouter";
7
- import { QueryClientProvider } from "@tanstack/react-query";
 
 
 
 
 
 
8
  import { queryClient } from "./lib/queryClient";
 
 
 
9
  import Publisher from "@/pages/Publisher";
 
 
 
10
  import Admin from "@/pages/Admin";
 
 
11
 
12
  const supabaseUrl = "https://eijwjjbkluurxjiiuhay.supabase.co";
13
  const supabaseAnonKey = "sb_publishable_VZEm1rag3JF-39xXx2F5og_1oNkjR5h";
14
  const supabase = createClient(supabaseUrl, supabaseAnonKey);
15
 
 
 
 
 
 
 
 
 
 
16
  function SupabaseAuthTokenSetter() {
17
  useEffect(() => {
18
  setAuthTokenGetter(async () => {
@@ -24,7 +47,45 @@ function SupabaseAuthTokenSetter() {
24
  return null;
25
  }
26
 
27
- function HomePage() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  const [session, setSession] = useState<any>(null);
29
  const [loading, setLoading] = useState(true);
30
 
@@ -33,21 +94,53 @@ function HomePage() {
33
  setSession(session);
34
  setLoading(false);
35
  });
 
 
 
 
 
 
 
36
  }, []);
37
 
38
- if (loading) return <div className="min-h-screen bg-[#0f0f1e] flex items-center justify-center text-white">جاري التحميل...</div>;
39
  if (session) return <Redirect to="/app" />;
40
 
41
  return (
42
- <div className="min-h-screen bg-[#0f0f1e] flex items-center justify-center">
43
- <div className="w-full max-w-md p-6">
44
- <h1 className="text-white text-2xl font-bold text-center mb-6">FB Publisher PRO</h1>
45
- <Auth supabaseClient={supabase} appearance={{ theme: ThemeSupa }} providers={["google"]} redirectTo={`${window.location.origin}`} />
 
 
 
 
 
 
 
 
46
  </div>
47
  </div>
48
  );
49
  }
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  function ProtectedPublisher() {
52
  const [session, setSession] = useState<any>(null);
53
  const [loading, setLoading] = useState(true);
@@ -59,23 +152,129 @@ function ProtectedPublisher() {
59
  });
60
  }, []);
61
 
62
- if (loading) return <div className="min-h-screen bg-[#0f0f1e] flex items-center justify-center text-white">جاري التحميل...</div>;
63
- if (!session) return <Redirect to="/" />;
64
 
65
  return <Publisher />;
66
  }
67
 
68
- export default function App() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  return (
70
- <QueryClientProvider client={queryClient}>
 
71
  <SupabaseAuthTokenSetter />
72
- <WouterRouter>
 
73
  <Switch>
74
  <Route path="/" component={HomePage} />
 
 
75
  <Route path="/app" component={ProtectedPublisher} />
 
 
 
76
  <Route path="/admin" component={Admin} />
 
77
  </Switch>
78
- </WouterRouter>
79
- </QueryClientProvider>
 
 
 
 
 
 
 
 
 
 
 
 
80
  );
81
  }
 
1
+ import { useEffect, useRef, useState } from "react";
2
  import { createClient } from "@supabase/supabase-js";
3
  import { Auth } from "@supabase/auth-ui-react";
4
  import { ThemeSupa } from "@supabase/auth-ui-shared";
5
  import { setAuthTokenGetter } from "@workspace/api-client-react";
6
+ import {
7
+ Switch,
8
+ Route,
9
+ useLocation,
10
+ Router as WouterRouter,
11
+ Redirect,
12
+ } from "wouter";
13
+ import { QueryClientProvider, useQueryClient } from "@tanstack/react-query";
14
  import { queryClient } from "./lib/queryClient";
15
+ import { Toaster } from "@/components/ui/toaster";
16
+ import { TooltipProvider } from "@/components/ui/tooltip";
17
+ import Landing from "@/pages/Landing";
18
  import Publisher from "@/pages/Publisher";
19
+ import Subscription from "@/pages/Subscription";
20
+ import SubscriptionReturn from "@/pages/SubscriptionReturn";
21
+ import InstallExtension from "@/pages/InstallExtension";
22
  import Admin from "@/pages/Admin";
23
+ import NotFound from "@/pages/not-found";
24
+ import UpdateBanner from "@/components/UpdateBanner";
25
 
26
  const supabaseUrl = "https://eijwjjbkluurxjiiuhay.supabase.co";
27
  const supabaseAnonKey = "sb_publishable_VZEm1rag3JF-39xXx2F5og_1oNkjR5h";
28
  const supabase = createClient(supabaseUrl, supabaseAnonKey);
29
 
30
+ const basePath = import.meta.env.BASE_URL.replace(/\/$/, "");
31
+
32
+ function stripBase(path: string): string {
33
+ return basePath && path.startsWith(basePath)
34
+ ? path.slice(basePath.length) || "/"
35
+ : path;
36
+ }
37
+
38
+ // ========== Supabase Auth Token Setter ==========
39
  function SupabaseAuthTokenSetter() {
40
  useEffect(() => {
41
  setAuthTokenGetter(async () => {
 
47
  return null;
48
  }
49
 
50
+ // ========== صفحات ==========
51
+ function SignInPage() {
52
+ const [session, setSession] = useState<any>(null);
53
+ const [loading, setLoading] = useState(true);
54
+
55
+ useEffect(() => {
56
+ supabase.auth.getSession().then(({ data: { session } }) => {
57
+ setSession(session);
58
+ setLoading(false);
59
+ });
60
+
61
+ // 🆕 تحويل تلقائي بعد تسجيل الدخول
62
+ const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
63
+ if (session) {
64
+ window.location.href = `${basePath}/app`;
65
+ }
66
+ });
67
+ return () => subscription.unsubscribe();
68
+ }, []);
69
+
70
+ if (loading) return <div className="min-h-screen bg-[#16162a] flex items-center justify-center text-white">جاري التحميل...</div>;
71
+ if (session) return <Redirect to="/app" />;
72
+
73
+ return (
74
+ <div className="flex min-h-[100dvh] items-center justify-center px-4 bg-[#16162a]">
75
+ <div className="w-full max-w-md">
76
+ <Auth
77
+ supabaseClient={supabase}
78
+ appearance={{ theme: ThemeSupa }}
79
+ providers={["google"]}
80
+ redirectTo={`${window.location.origin}${basePath}/app`}
81
+ view="sign_in"
82
+ />
83
+ </div>
84
+ </div>
85
+ );
86
+ }
87
+
88
+ function SignUpPage() {
89
  const [session, setSession] = useState<any>(null);
90
  const [loading, setLoading] = useState(true);
91
 
 
94
  setSession(session);
95
  setLoading(false);
96
  });
97
+
98
+ const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
99
+ if (session) {
100
+ window.location.href = `${basePath}/app`;
101
+ }
102
+ });
103
+ return () => subscription.unsubscribe();
104
  }, []);
105
 
106
+ if (loading) return <div className="min-h-screen bg-[#16162a] flex items-center justify-center text-white">جاري التحميل...</div>;
107
  if (session) return <Redirect to="/app" />;
108
 
109
  return (
110
+ <div className="flex min-h-[100dvh] items-center justify-center px-4 bg-[#16162a]">
111
+ <div className="w-full max-w-md space-y-4">
112
+ <Auth
113
+ supabaseClient={supabase}
114
+ appearance={{ theme: ThemeSupa }}
115
+ providers={["google"]}
116
+ redirectTo={`${window.location.origin}${basePath}/app`}
117
+ view="sign_up"
118
+ />
119
+ <div className="rounded-lg border border-red-500/30 bg-red-950/20 p-3 text-center text-sm text-red-300">
120
+ ⚠️ <strong>تنبيه هام لمكافحة السبام:</strong> التسجيل باستخدام البريد الإلكتروني المؤقت ممنوع تمامًا وسيؤدي إلى حظر الحساب فور اكتشافه.
121
+ </div>
122
  </div>
123
  </div>
124
  );
125
  }
126
 
127
+ function HomePage() {
128
+ const [session, setSession] = useState<any>(null);
129
+ const [loading, setLoading] = useState(true);
130
+
131
+ useEffect(() => {
132
+ supabase.auth.getSession().then(({ data: { session } }) => {
133
+ setSession(session);
134
+ setLoading(false);
135
+ });
136
+ }, []);
137
+
138
+ if (loading) return <div className="min-h-screen bg-[#16162a] flex items-center justify-center text-white">جاري التحميل...</div>;
139
+ if (session) return <Redirect to="/app" />;
140
+
141
+ return <Landing />;
142
+ }
143
+
144
  function ProtectedPublisher() {
145
  const [session, setSession] = useState<any>(null);
146
  const [loading, setLoading] = useState(true);
 
152
  });
153
  }, []);
154
 
155
+ if (loading) return <div className="min-h-screen bg-[#16162a] flex items-center justify-center text-white">جاري التحميل...</div>;
156
+ if (!session) return <Redirect to="/sign-in" />;
157
 
158
  return <Publisher />;
159
  }
160
 
161
+ function ProtectedSubscription() {
162
+ const [session, setSession] = useState<any>(null);
163
+ const [loading, setLoading] = useState(true);
164
+
165
+ useEffect(() => {
166
+ supabase.auth.getSession().then(({ data: { session } }) => {
167
+ setSession(session);
168
+ setLoading(false);
169
+ });
170
+ }, []);
171
+
172
+ if (loading) return <div className="min-h-screen bg-[#16162a] flex items-center justify-center text-white">جاري التحميل...</div>;
173
+ if (!session) return <Redirect to="/sign-in" />;
174
+
175
+ return <Subscription />;
176
+ }
177
+
178
+ function ProtectedExtension() {
179
+ const [session, setSession] = useState<any>(null);
180
+ const [loading, setLoading] = useState(true);
181
+
182
+ useEffect(() => {
183
+ supabase.auth.getSession().then(({ data: { session } }) => {
184
+ setSession(session);
185
+ setLoading(false);
186
+ });
187
+ }, []);
188
+
189
+ if (loading) return <div className="min-h-screen bg-[#16162a] flex items-center justify-center text-white">جاري التحميل...</div>;
190
+ if (!session) return <Redirect to="/sign-in" />;
191
+
192
+ return <InstallExtension />;
193
+ }
194
+
195
+ function ServerKeepAlive() {
196
+ const [waking, setWaking] = useState(false);
197
+
198
+ useEffect(() => {
199
+ let wakingTimer: ReturnType<typeof setTimeout>;
200
+ const ping = () => {
201
+ wakingTimer = setTimeout(() => setWaking(true), 4000);
202
+ fetch("/api/healthz", { cache: "no-store" })
203
+ .then(() => { clearTimeout(wakingTimer); setWaking(false); })
204
+ .catch(() => { clearTimeout(wakingTimer); setWaking(false); });
205
+ };
206
+ ping();
207
+ const interval = setInterval(ping, 10 * 60 * 1000);
208
+ return () => { clearInterval(interval); clearTimeout(wakingTimer); };
209
+ }, []);
210
+
211
+ if (!waking) return null;
212
+
213
+ return (
214
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-[#16162a]/90 backdrop-blur-sm" dir="rtl">
215
+ <div className="bg-[#1f1f3a] border border-[#3a3a55] rounded-2xl p-8 text-center max-w-sm mx-4 shadow-2xl">
216
+ <div className="text-5xl mb-4">⚙️</div>
217
+ <h2 className="text-white text-xl font-bold mb-2">جاري تشغيل الخادم…</h2>
218
+ <p className="text-gray-400 text-sm leading-relaxed">الخادم كان في وضع السكون.<br />يستغرق الأمر بضع ثوانٍ، شكراً لصبرك.</p>
219
+ <div className="mt-5 flex justify-center gap-2">
220
+ {[0, 1, 2].map((i) => (
221
+ <div key={i} className="w-2.5 h-2.5 bg-[#bb86fc] rounded-full animate-bounce" style={{ animationDelay: `${i * 0.18}s` }} />
222
+ ))}
223
+ </div>
224
+ </div>
225
+ </div>
226
+ );
227
+ }
228
+
229
+ function SupabaseQueryClientCacheInvalidator() {
230
+ const qc = useQueryClient();
231
+ const prevUserIdRef = useRef<string | null | undefined>(undefined);
232
+
233
+ useEffect(() => {
234
+ const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
235
+ const userId = session?.user?.id ?? null;
236
+ if (prevUserIdRef.current !== undefined && prevUserIdRef.current !== userId) {
237
+ qc.clear();
238
+ }
239
+ prevUserIdRef.current = userId;
240
+ });
241
+ return () => subscription.unsubscribe();
242
+ }, [qc]);
243
+
244
+ return null;
245
+ }
246
+
247
+ function SupabaseProviderWithRoutes() {
248
  return (
249
+ <>
250
+ <ServerKeepAlive />
251
  <SupabaseAuthTokenSetter />
252
+ <SupabaseQueryClientCacheInvalidator />
253
+ <TooltipProvider>
254
  <Switch>
255
  <Route path="/" component={HomePage} />
256
+ <Route path="/sign-in/*?" component={SignInPage} />
257
+ <Route path="/sign-up/*?" component={SignUpPage} />
258
  <Route path="/app" component={ProtectedPublisher} />
259
+ <Route path="/subscription" component={ProtectedSubscription} />
260
+ <Route path="/subscription/return" component={SubscriptionReturn} />
261
+ <Route path="/extension" component={ProtectedExtension} />
262
  <Route path="/admin" component={Admin} />
263
+ <Route component={NotFound} />
264
  </Switch>
265
+ <UpdateBanner />
266
+ <Toaster />
267
+ </TooltipProvider>
268
+ </>
269
+ );
270
+ }
271
+
272
+ export default function App() {
273
+ return (
274
+ <WouterRouter base={basePath}>
275
+ <QueryClientProvider client={queryClient}>
276
+ <SupabaseProviderWithRoutes />
277
+ </QueryClientProvider>
278
+ </WouterRouter>
279
  );
280
  }