Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 104 additions & 21 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import createWorker from "https://esm.sh/@supabase/supabase-js?worker";
import { createClient } from "https://esm.sh/@supabase/supabase-js";

// Setting up the Supabase Main Thread.
(async () => {
window.sb = createClient(
"https://qmpdruitzlownnnnjmpk.supabase.co",
Expand Down Expand Up @@ -54,12 +55,42 @@
refresh_token: session.refresh_token,
},
});

let retries = 50;
while (!window.supabase?.getUser && retries-- > 0) {
await new Promise((r) => setTimeout(r, 100));
}

if (window.supabase?.getUser) {
console.log("User Found");
// const __user = window.supabase.getUser();
// __user
// .then((resp) => {
// if (resp?.data.user) {
// console.log(
// "[DEBUG]: User object received",
// resp.data.user
// );
// if (window.JSRustResponseHandler) {
// window.JSRustResponseHandler(resp);
// } else {
// console.log("[DEBUG]: No Bindgenie - Huge problem.");
// }
// } else {
// console.log("[DEBUG]: No user in response", resp);
// }
// })
// .catch((err) => {
// console.error("[ERROR]: Failed to get user", err);
// });
} else {
console.log("No User Found");
}
} else {
console.log("[Main] Worker not found.");
}
}
else {
console.log("[Main] No session found — user is logged out.");
} else {
console.log("[Main] No session found — user is logged out.");
}
})();

Expand All @@ -70,7 +101,7 @@
inject: `
const supabase = $module.createClient(
"https://qmpdruitzlownnnnjmpk.supabase.co",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFtcGRydWl0emxvd25ubm5qbXBrIiwicm9zZSI6ImFub24iLCJpYXQiOjE3NDk2NjA0NTYsImV4cCI6MjA2NTIzNjQ1Nn0.OhD3qN4dq0TMA65qVGvry_QsZEeLKK7RbwYP3QzAvcY",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFtcGRydWl0emxvd25ubm5qbXBrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDk2NjA0NTYsImV4cCI6MjA2NTIzNjQ1Nn0.OhD3qN4dq0TMA65qVGvry_QsZEeLKK7RbwYP3QzAvcY",
{
auth: {
detectSessionInUrl: false,
Expand Down Expand Up @@ -186,7 +217,7 @@
session: () => sendToWorker("session", {}),
getUser: () => sendToWorker("getUser", {}),
getProfile: (user_id) => sendToWorker("getProfile", { user_id }),
setSession: () => sendToWorker("setSession", {session}),
setSession: () => sendToWorker("setSession", { session }),
worker: supabaseWorker,
};

Expand Down Expand Up @@ -379,14 +410,14 @@
></div>
<button
onclick="closeCaptcha()"
style="margin-top: 16px; margin-right: 8px;"
style="margin-top: 16px; margin-right: 8px"
class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded shadow transition-colors duration-150"
>
Cancel
</button>
<button
onclick="resetCaptcha()"
style="margin-top: 16px;"
style="margin-top: 16px"
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded shadow transition-colors duration-150"
>
Reset Captcha
Expand All @@ -409,6 +440,53 @@
action = message.action;
}
switch (action) {
case "user": {
console.log("[Interop] - Rust Requires User");

let retries = 5;

function pollForValidUser() {
if (!window.supabase?.getUser) {
if (--retries > 0) {
return setTimeout(pollForValidUser, 1000);
}
console.warn("[Interop] - Supabase.getUser not available.");
return;
}

window.supabase
.getUser()
.then((resp) => {
if (resp?.data?.user) {
console.log(
"[DEBUG]:[INTEROP]: User object received",
resp.data.user
);

if (window.wasmBindings?.supabase_user) {
window.wasmBindings.supabase_user(resp.data.user);
} else {
console.log("[DEBUG]: No Bindgenie - Huge problem.");
}
} else {
if (--retries > 0) {
setTimeout(pollForValidUser, 100);
} else {
console.warn(
"[Interop] - Supabase session not available after retries."
);
console.log("[DEBUG]: No user in response", resp);
}
}
})
.catch((err) => {
console.error("[ERROR]: Failed to get user", err);
});
}

pollForValidUser();
break;
}
case "openCaptcha":
openCaptcha();
break;
Expand Down Expand Up @@ -444,6 +522,17 @@
if (window.JSRustResponseHandler) {
window.JSRustResponseHandler(resp);
}

if (window.wasmBindings?.supabase_user && resp.data?.user) {
window.wasmBindings.supabase_user(resp.data.user);
}

if (
window.wasmBindings?.supabase_user &&
resp.data?.session?.user
) {
window.wasmBindings.supabase_user(resp.data.session.user);
}
})
.catch((err) => {
console.error("[ERROR]: signup call failed", err);
Expand Down Expand Up @@ -498,9 +587,15 @@
});
}

if (window.JSRustResponseHandler) {
window.JSRustResponseHandler(resp);
if (window.wasmBindings?.handle_jsrust_response) {
window.wasmBindings.handle_jsrust_response(resp);
resp;
}

if (window.wasmBindings?.supabase_user && resp.data?.user) {
window.wasmBindings.supabase_user(resp.data.user);
}

})
.catch((err) => {
console.error("[ERROR]: Login failed:", err);
Expand Down Expand Up @@ -565,18 +660,6 @@
window.closeCaptcha = closeCaptcha;

// Set up a default JSRustResponseHandler to forward to WASM if available
window.JSRustResponseHandler = function (resp) {
if (window.wasmBindings && window.wasmBindings.handle_jsrust_response) {
window.wasmBindings.handle_jsrust_response(resp);
} else if (window.handle_jsrust_response) {
window.handle_jsrust_response(resp);
} else {
console.log(
"[JSRustResponseHandler] Response from JS to Rust:",
resp
);
}
};

function resetCaptcha() {
if (window.hcaptcha) {
Expand Down
20 changes: 20 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ impl TemplateApp {
app.app_state = AppState::Init;
app.db.load_from_indexeddb();
app.load_filtered_repos_from_idb(&cc.egui_ctx);
// --- Call JSRust to request user info when app is ready ---
crate::erust::uiux::javascript_interop::request_user_from_js();
app
}

Expand Down Expand Up @@ -342,6 +344,14 @@ impl TemplateApp {
ctx.open_url(egui::OpenUrl::new_tab(&logo_link));
}
ui.separator();
// --- Show welcome and email if authenticated ---
if self.user.is_logged_in() {
ui.heading("Welcome,");
if let Some(email) = &self.user.email {
ui.label(format!("Email: {}", email));
}
ui.separator();
}
ui.heading("Filtered Repositories");
let filtered = self.filtered_repos.clone().unwrap_or_default();
let current_language = self.db.get_language();
Expand Down Expand Up @@ -400,7 +410,9 @@ impl TemplateApp {
"",
"",
);
self.user = User::default(); // Reset user to blank/default
self.toast_message = Some("Sent logout request to JS".to_string());
ctx.request_repaint(); // Ensure UI updates
}
ui.label("Waffle v0.1.0");
});
Expand All @@ -418,6 +430,14 @@ impl TemplateApp {
self.auth_widget.show(ctx, ui);
});
}

// --- Check for new Supabase user and update state ---
if let Some(new_user) = crate::erust::uiux::javascript_interop::take_supabase_user() {
if new_user.is_authenticated {
self.user = new_user;
ctx.request_repaint();
}
}
}
}

Expand Down
103 changes: 103 additions & 0 deletions src/erust/uiux/javascript_interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ pub fn send_action_message(action: &str, email: &str, password: &str, captcha_to
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use js_sys::JSON;
use serde::Deserialize;

#[derive(Debug, Deserialize, Clone)]
pub struct User {
pub id: String,
pub email: String,
// Add more fields as needed
}

/// Call this during app initialization to handle JS->Rust responses
pub fn setup_jsrust_response_handler<F>(mut callback: F)
Expand All @@ -48,6 +56,46 @@ where
} else {
serde_json::Value::Null
};

// --- Login response handling ---
let mut handled = false;
if let Some(success) = resp_json.get("success").and_then(|v| v.as_bool()) {
if success {
// Try to extract user info from the response
if let Some(data) = resp_json.get("data") {
if let Some(session) = data.get("session") {
if let Some(user) = session.get("user") {
if let Ok(user_obj) = serde_json::from_value::<User>(user.clone()) {
log::info!("Login successful! User: {:?}", user_obj);
handled = true;
// Example: callback could update state
// callback(serde_json::to_value(user_obj).unwrap());
}
}
}
}
} else {
if let Some(error) = resp_json.get("error") {
log::error!("Login failed: {:?}", error);
handled = true;
}
}
}
// If not handled, log the message using the JSRust -> Log function
if !handled {
// Try to call the JS log function if available
let log_msg = format!("[JSRustInterop] Unhandled message: {:?}", resp_json);
let js_log = js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("JSRust"));
if let Ok(js_log_fn) = js_log {
if js_log_fn.is_function() {
let func = js_sys::Function::from(js_log_fn);
let _ = func.call2(&JsValue::NULL, &JsValue::from_str("log"), &JsValue::from_str(&log_msg));
}
}
// Also log to Rust console for debugging
log::info!("[JSRustInterop] Unhandled message: {:?}", resp_json);
}
// Call the user callback for further handling
callback(resp_json);
}) as Box<dyn FnMut(JsValue)>);

Expand Down Expand Up @@ -79,6 +127,16 @@ pub fn set_jsrust_response_handler(cb: &js_sys::Function) {
);
}

/// Call JSRust('user') to request user info from JS and send it to Rust handler
pub fn request_user_from_js() {
if let Ok(jsrust) = js_sys::Reflect::get(&js_sys::global(), &wasm_bindgen::JsValue::from_str("JSRust")) {
if jsrust.is_function() {
let func = js_sys::Function::from(jsrust);
let _ = func.call1(&wasm_bindgen::JsValue::NULL, &wasm_bindgen::JsValue::from_str("user"));
}
}
}

// Expand AppState for interop waiting
// In state.rs (or wherever your AppState is defined):
//
Expand All @@ -94,3 +152,48 @@ pub fn set_jsrust_response_handler(cb: &js_sys::Function) {
// self.app_state = AppState::InteropPending("Waiting for JS response...".to_string());
//
// In your interop callback, set it back to Normal or Error as appropriate.

#[wasm_bindgen]
pub fn supabase_session(session: &JsValue) {
// You can process the session object here or store it as needed
// For now, just log it for debugging
log::info!("[JSInterop] Received Supabase session: {:?}", session);
// TODO: Store or process session as needed
}

use crate::erust::uiux::supabase::SupabaseUserRaw;
use crate::erust::uiux::user::User as AppUser;
use std::cell::RefCell;

thread_local! {
static LAST_SUPABASE_USER: RefCell<Option<AppUser>> = RefCell::new(None);
}

#[wasm_bindgen]
pub fn supabase_user(user: &JsValue) {
// Use JSON::stringify and serde_json for compatibility
let user_obj: Result<SupabaseUserRaw, _> = js_sys::JSON::stringify(user)
.ok()
.and_then(|js_str| js_str.as_string())
.map(|json_str| serde_json::from_str(&json_str))
.unwrap_or_else(|| Err(serde_json::Error::io(std::io::Error::new(std::io::ErrorKind::Other, "Failed to stringify JsValue"))));
match user_obj {
Ok(raw) => {
// Map SupabaseUserRaw to our AppUser struct
let mut app_user = AppUser::new();
app_user.set_id(raw.id);
app_user.set_email(raw.email);
app_user.authenticate();
LAST_SUPABASE_USER.with(|cell| cell.replace(Some(app_user.clone())));
log::info!("[JSInterop] Stored Supabase user: {:?}", app_user);
},
Err(e) => {
log::error!("[JSInterop] Failed to parse Supabase user: {:?}", e);
}
}
}

/// Retrieve and clear the last Supabase user (for app.rs to use)
pub fn take_supabase_user() -> Option<AppUser> {
LAST_SUPABASE_USER.with(|cell| cell.borrow_mut().take())
}
Loading
Loading