Mobile - 0.3.1
All checks were successful
Docker Deploy / build-and-push (push) Successful in 56s

This commit is contained in:
Atridad Lahiji 2025-01-27 01:59:07 -06:00
parent 622bf8eb0d
commit 45267c7e4e
Signed by: atridad
SSH key fingerprint: SHA256:LGomp8Opq0jz+7kbwNcdfTcuaLRb5Nh0k5AchDDb438
6 changed files with 37 additions and 51 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "pdsmanager", "name": "pdsmanager",
"type": "module", "type": "module",
"version": "0.3.0", "version": "0.3.1",
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"build": "astro build", "build": "astro build",

View file

@ -57,8 +57,8 @@ export default function AuthWrapper() {
<RefreshContext.Provider value={{ refresh, lastUpdate }}> <RefreshContext.Provider value={{ refresh, lastUpdate }}>
<div className="w-full flex flex-col"> <div className="w-full flex flex-col">
<Header /> <Header />
<div className="min-h-[calc(100vh-64px)] p-4"> <div className="min-h-[calc(100vh-64px)] p-2 sm:p-4">
<div className="container mx-auto"> <div className="container mx-auto max-w-full">
<TabView /> <TabView />
</div> </div>
</div> </div>

View file

@ -185,12 +185,12 @@ export default function InviteCodes() {
return ( return (
<div className="card bg-base-100 shadow-xl"> <div className="card bg-base-100 shadow-xl">
<div className="card-body"> <div className="card-body p-2 sm:p-8">
<div className="flex justify-between items-center mb-4"> <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 sm:gap-4 mb-4">
<div className="flex items-center gap-4"> <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2 sm:gap-4">
<h1 className="card-title text-2xl">Invite Codes</h1> <h1 className="card-title text-xl sm:text-2xl">Invite Codes</h1>
<label className="cursor-pointer label gap-2"> <label className="cursor-pointer label gap-2">
<span className="label-text">Show Disabled Codes</span> <span className="label-text">Show Disabled</span>
<input <input
type="checkbox" type="checkbox"
className="toggle toggle-sm toggle-primary" className="toggle toggle-sm toggle-primary"
@ -202,7 +202,7 @@ export default function InviteCodes() {
<button <button
onClick={createCode} onClick={createCode}
disabled={loading} disabled={loading}
className="btn btn-primary" className="btn btn-primary w-full sm:w-auto"
> >
{loading ? ( {loading ? (
<span className="loading loading-spinner loading-sm"></span> <span className="loading loading-spinner loading-sm"></span>
@ -249,21 +249,18 @@ export default function InviteCodes() {
className={`card ${code.disabled ? "bg-base-300 opacity-75" : "bg-base-200" className={`card ${code.disabled ? "bg-base-300 opacity-75" : "bg-base-200"
} hover:shadow-md transition-shadow`} } hover:shadow-md transition-shadow`}
> >
<div className="card-body"> <div className="card-body p-3 sm:p-6">
<div className="flex justify-between items-start"> <div className="flex flex-col sm:flex-row justify-between items-start gap-4">
<div <div className="font-mono text-base sm:text-lg bg-base-100 p-2 sm:p-3 rounded-box select-all w-full sm:flex-1 break-all">
className={`font-mono text-lg bg-base-100 p-3 rounded-box select-all flex-1 mr-4 ${code.disabled ? "opacity-50" : ""
}`}
>
{code.code} {code.code}
</div> </div>
<div className="flex flex-col items-end gap-2"> <div className="flex flex-row sm:flex-col items-center sm:items-end gap-2 w-full sm:w-auto">
{code.disabled ? ( {code.disabled ? (
<span className="badge badge-error">Disabled</span> <span className="badge badge-error w-full sm:w-auto justify-center">Disabled</span>
) : ( ) : (
<button <button
onClick={() => disableCode(code.code)} onClick={() => disableCode(code.code)}
className="btn btn-sm btn-warning" className="btn btn-sm btn-warning w-full sm:w-auto"
title="Disable code" title="Disable code"
> >
Disable Disable
@ -271,7 +268,7 @@ export default function InviteCodes() {
)} )}
</div> </div>
</div> </div>
<div className="flex flex-wrap gap-4 mt-2 text-sm"> <div className="flex flex-wrap gap-2 mt-2 text-sm">
{!code.disabled && ( {!code.disabled && (
<div className="badge badge-neutral"> <div className="badge badge-neutral">
Uses: {code.uses.length} / {code.available} Uses: {code.uses.length} / {code.available}
@ -282,16 +279,12 @@ export default function InviteCodes() {
</div> </div>
{code.uses.length > 0 && ( {code.uses.length > 0 && (
<div className="badge badge-info"> <div className="badge badge-info">
{code.uses.length}{" "} {code.uses.length} {code.uses.length === 1 ? "use" : "uses"}
{code.uses.length === 1 ? "use" : "uses"}
</div> </div>
)} )}
</div> </div>
{code.uses.length > 0 && ( {code.uses.length > 0 && (
<div <div className="text-sm mt-2 break-all">
className={`text-sm mt-2 ${code.disabled ? "opacity-75" : "opacity-70"
}`}
>
<span className="font-semibold">Used by:</span>{" "} <span className="font-semibold">Used by:</span>{" "}
{code.uses.map((use) => use.usedBy).join(", ")} {code.uses.map((use) => use.usedBy).join(", ")}
</div> </div>
@ -299,11 +292,9 @@ export default function InviteCodes() {
</div> </div>
</div> </div>
))} ))}
{!loading && codes.length === 0 && ( {!loading && filterAndSortCodes(codes).length === 0 && (
<div className="text-center p-8 bg-base-200 rounded-box"> <div className="text-center p-8 bg-base-200 rounded-box">
<div className="text-base-content/70"> <div className="text-base-content/70">No invite codes found</div>
No invite codes found
</div>
</div> </div>
)} )}
</div> </div>

View file

@ -120,7 +120,6 @@ export default function UserList() {
"Content-Type": "application/json", "Content-Type": "application/json",
}; };
// First get the account info which includes email
const accountResponse = await fetch( const accountResponse = await fetch(
`${baseUrl}/xrpc/com.atproto.admin.getAccountInfo?did=${did}`, `${baseUrl}/xrpc/com.atproto.admin.getAccountInfo?did=${did}`,
{ headers } { headers }
@ -132,7 +131,6 @@ export default function UserList() {
const accountData = await accountResponse.json(); const accountData = await accountResponse.json();
// Then get the profile data as before
const profileResponse = await fetch( const profileResponse = await fetch(
`${baseUrl}/xrpc/com.atproto.repo.getRecord?collection=app.bsky.actor.profile&repo=${did}&rkey=self`, `${baseUrl}/xrpc/com.atproto.repo.getRecord?collection=app.bsky.actor.profile&repo=${did}&rkey=self`,
{ headers } { headers }
@ -146,7 +144,7 @@ export default function UserList() {
return { return {
did, did,
email: accountData.email, // Add email from account info email: accountData.email,
handle: profileData.value.handle, handle: profileData.value.handle,
displayName: profileData.value.displayName, displayName: profileData.value.displayName,
description: profileData.value.description, description: profileData.value.description,
@ -284,7 +282,7 @@ export default function UserList() {
); );
} }
await fetchUsers(); // Refresh the user list after successful update await fetchUsers();
} catch (err) { } catch (err) {
console.error("Update handle error:", err); console.error("Update handle error:", err);
setError(err instanceof Error ? err.message : "Failed to update handle"); setError(err instanceof Error ? err.message : "Failed to update handle");
@ -418,7 +416,7 @@ export default function UserList() {
if (!settingsLoaded) { if (!settingsLoaded) {
return ( return (
<div className="card bg-base-100 shadow-xl"> <div className="card bg-base-100 shadow-xl">
<div className="card-body"> <div className="card-body p-2 sm:p-8">
<div className="flex justify-center p-8"> <div className="flex justify-center p-8">
<span className="loading loading-spinner loading-lg"></span> <span className="loading loading-spinner loading-lg"></span>
</div> </div>
@ -430,7 +428,7 @@ export default function UserList() {
if (!hasValidSettings) { if (!hasValidSettings) {
return ( return (
<div className="card bg-base-100 shadow-xl"> <div className="card bg-base-100 shadow-xl">
<div className="card-body"> <div className="card-body p-2 sm:p-8">
<div className="alert alert-warning"> <div className="alert alert-warning">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -459,13 +457,13 @@ export default function UserList() {
return ( return (
<div className="card bg-base-100 shadow-xl"> <div className="card bg-base-100 shadow-xl">
<div className="card-body"> <div className="card-body p-2 sm:p-8">
<div className="flex justify-between items-center mb-4"> <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 sm:gap-4 mb-4">
<h1 className="card-title text-2xl">Users</h1> <h1 className="card-title text-xl sm:text-2xl">Users</h1>
<button <button
onClick={() => fetchUsers()} onClick={() => fetchUsers()}
disabled={loading} disabled={loading}
className="btn btn-primary" className="btn btn-primary w-full sm:w-auto"
> >
{loading ? ( {loading ? (
<span className="loading loading-spinner loading-sm"></span> <span className="loading loading-spinner loading-sm"></span>
@ -708,17 +706,17 @@ export default function UserList() {
key={user.did} key={user.did}
className="card bg-base-200 hover:shadow-md transition-shadow" className="card bg-base-200 hover:shadow-md transition-shadow"
> >
<div className="card-body"> <div className="card-body p-3 sm:p-6">
<div className="flex items-start gap-4"> <div className="flex flex-col sm:flex-row items-start gap-4">
<UserAvatar user={user} /> <UserAvatar user={user} />
<div className="flex-1"> <div className="flex-1 w-full">
<h2 className="text-lg font-semibold"> <h2 className="text-lg font-semibold break-all">
{user.displayName || "Anonymous"} {user.displayName || "Anonymous"}
</h2> </h2>
<div className="font-mono text-sm opacity-70 mt-1"> <div className="font-mono text-sm opacity-70 mt-1 break-all">
{user.handle} {user.handle}
</div> </div>
<div className="font-mono text-sm opacity-70 mt-1"> <div className="font-mono text-sm opacity-70 mt-1 break-all">
{user.did} {user.did}
</div> </div>
{user.description && ( {user.description && (
@ -727,8 +725,7 @@ export default function UserList() {
</p> </p>
)} )}
</div> </div>
<div className="grid grid-cols-2 sm:grid-cols-1 gap-2 mt-4 sm:mt-0">
<div className="flex flex-col gap-2">
<button <button
onClick={() => setEditModal({ onClick={() => setEditModal({
show: true, show: true,
@ -793,4 +790,5 @@ export default function UserList() {
</div> </div>
</div> </div>
); );
} }

View file

@ -6,7 +6,7 @@ import Footer from "../components/navigation/Footer.astro";
<html data-theme="dark"> <html data-theme="dark">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link <link
rel="icon" rel="icon"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🦋</text></svg>" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🦋</text></svg>"

View file

@ -26,7 +26,6 @@ export class Settings {
); );
resolve(key); resolve(key);
} else { } else {
// Generate new key if none exists
const key = await window.crypto.subtle.generateKey( const key = await window.crypto.subtle.generateKey(
{ {
name: "AES-GCM", name: "AES-GCM",
@ -36,13 +35,11 @@ export class Settings {
["encrypt", "decrypt"], ["encrypt", "decrypt"],
); );
// Export and store the key
const exportedKey = await window.crypto.subtle.exportKey( const exportedKey = await window.crypto.subtle.exportKey(
"raw", "raw",
key, key,
); );
// Create a new transaction for storing the key
const newTransaction = db.transaction(this.STORE_NAME, "readwrite"); const newTransaction = db.transaction(this.STORE_NAME, "readwrite");
const newStore = newTransaction.objectStore(this.STORE_NAME); const newStore = newTransaction.objectStore(this.STORE_NAME);
newStore.put(exportedKey, this.ENCRYPTION_KEY_NAME); newStore.put(exportedKey, this.ENCRYPTION_KEY_NAME);