mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-23 16:05:24 +00:00
Standardize profile pages layouts
This commit is contained in:
parent
600ddadbb4
commit
2920fd4f19
139
src/components/profile/DataTables/UserRelaysTable.js
Normal file
139
src/components/profile/DataTables/UserRelaysTable.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
|
import { DataTable } from "primereact/datatable";
|
||||||
|
import { Column } from "primereact/column";
|
||||||
|
import { InputText } from "primereact/inputtext";
|
||||||
|
import GenericButton from "@/components/buttons/GenericButton";
|
||||||
|
import { useToast } from "@/hooks/useToast";
|
||||||
|
import appConfig from "@/config/appConfig";
|
||||||
|
|
||||||
|
const UserRelaysTable = ({ ndk, userRelays, setUserRelays, reInitializeNDK }) => {
|
||||||
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
|
const [newRelayUrl, setNewRelayUrl] = useState("");
|
||||||
|
const { showToast } = useToast();
|
||||||
|
const [relayStatuses, setRelayStatuses] = useState({});
|
||||||
|
const [updateTrigger, setUpdateTrigger] = useState(0);
|
||||||
|
|
||||||
|
const updateRelayStatuses = useCallback(() => {
|
||||||
|
if (ndk) {
|
||||||
|
const statuses = {};
|
||||||
|
ndk.pool.relays.forEach((relay, url) => {
|
||||||
|
statuses[url] = relay.connectivity.status === 5;
|
||||||
|
});
|
||||||
|
setRelayStatuses(statuses);
|
||||||
|
}
|
||||||
|
}, [ndk]);
|
||||||
|
|
||||||
|
// Effect for periodic polling
|
||||||
|
useEffect(() => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
setUpdateTrigger(prev => prev + 1);
|
||||||
|
}, 7000);
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateRelayStatuses();
|
||||||
|
}, [updateRelayStatuses, updateTrigger]);
|
||||||
|
|
||||||
|
const addRelay = () => {
|
||||||
|
if (newRelayUrl && !userRelays.includes(newRelayUrl)) {
|
||||||
|
setUserRelays([...userRelays, newRelayUrl]);
|
||||||
|
setNewRelayUrl("");
|
||||||
|
reInitializeNDK();
|
||||||
|
setCollapsed(true);
|
||||||
|
showToast("success", "Relay added", "Relay successfully added to your list of relays.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeRelay = (url) => {
|
||||||
|
if (!appConfig.defaultRelayUrls.includes(url)) {
|
||||||
|
setUserRelays(userRelays.filter(relay => relay !== url));
|
||||||
|
reInitializeNDK();
|
||||||
|
setCollapsed(true);
|
||||||
|
showToast("success", "Relay removed", "Relay successfully removed from your list of relays.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const header = (
|
||||||
|
<div className="text-[#f8f8ff]">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-bold">Relays</h2>
|
||||||
|
<p className="text-gray-400">Manage your connected relays</p>
|
||||||
|
</div>
|
||||||
|
<GenericButton
|
||||||
|
icon="pi pi-plus"
|
||||||
|
label="Add Relay"
|
||||||
|
severity="success"
|
||||||
|
onClick={() => setCollapsed(!collapsed)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!collapsed && (
|
||||||
|
<div className="flex gap-2 mt-4">
|
||||||
|
<InputText
|
||||||
|
placeholder="Relay URL"
|
||||||
|
value={newRelayUrl}
|
||||||
|
onChange={(e) => setNewRelayUrl(e.target.value)}
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
<GenericButton
|
||||||
|
label="Add"
|
||||||
|
severity="success"
|
||||||
|
outlined
|
||||||
|
onClick={addRelay}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const relayStatusBody = (url) => {
|
||||||
|
const isConnected = relayStatuses[url];
|
||||||
|
return (
|
||||||
|
<i className={`pi ${isConnected ? 'pi-check-circle text-green-500' : 'pi-times-circle text-red-500'}`}></i>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const relayActionsBody = (rowData) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{!appConfig.defaultRelayUrls.includes(rowData) ? (
|
||||||
|
<GenericButton
|
||||||
|
icon="pi pi-trash"
|
||||||
|
className="p-button-rounded p-button-danger p-button-text"
|
||||||
|
onClick={() => removeRelay(rowData)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<GenericButton
|
||||||
|
icon="pi pi-trash"
|
||||||
|
className="p-button-rounded p-button-danger p-button-text opacity-50"
|
||||||
|
onClick={() => removeRelay(rowData)}
|
||||||
|
tooltip="Cannot remove default relays at this time (soon ™)"
|
||||||
|
tooltipOptions={{ position: 'top' }}
|
||||||
|
style={{
|
||||||
|
pointerEvents: 'none',
|
||||||
|
cursor: 'not-allowed'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-gray-800 rounded-lg border border-gray-700">
|
||||||
|
<DataTable
|
||||||
|
value={userRelays}
|
||||||
|
className="border-none"
|
||||||
|
header={header}
|
||||||
|
>
|
||||||
|
<Column field={(url) => url} header="Relay URL"></Column>
|
||||||
|
<Column body={relayStatusBody} header="Status"></Column>
|
||||||
|
<Column body={relayActionsBody} header="Actions"></Column>
|
||||||
|
</DataTable>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserRelaysTable;
|
@ -38,8 +38,11 @@ const UserProfile = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div className="w-full flex flex-row max-lap:flex-col">
|
<div className="w-full flex flex-row max-lap:flex-col">
|
||||||
<UserProfileCard user={user} />
|
<div className="w-1/4 h-full max-lap:w-full">
|
||||||
<div className="w-full flex flex-col justify-center mx-auto">
|
{user && <UserProfileCard user={user} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-3/4 flex flex-col justify-center mx-auto max-lap:w-full">
|
||||||
{account && account?.provider === "github" ? (
|
{account && account?.provider === "github" ? (
|
||||||
<CombinedContributionChart session={session} />
|
<CombinedContributionChart session={session} />
|
||||||
) : (
|
) : (
|
||||||
|
@ -46,7 +46,7 @@ const UserProfileCard = ({ user }) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const MobileProfileCard = () => (
|
const MobileProfileCard = () => (
|
||||||
<div className="w-full bg-gray-800 rounded-lg p-2 py-1 border border-gray-700 shadow-md h-[330px] flex flex-col justify-center">
|
<div className="w-full bg-gray-800 rounded-lg p-2 py-1 border border-gray-700 shadow-md h-[420px] flex flex-col justify-center items-start">
|
||||||
<div className="flex flex-col gap-2 pt-4 w-full relative">
|
<div className="flex flex-col gap-2 pt-4 w-full relative">
|
||||||
<div className="absolute top-8 right-[10px]">
|
<div className="absolute top-8 right-[10px]">
|
||||||
<i
|
<i
|
||||||
@ -66,7 +66,7 @@ const UserProfileCard = ({ user }) => {
|
|||||||
src={returnImageProxy(user.avatar, user?.pubkey || "")}
|
src={returnImageProxy(user.avatar, user?.pubkey || "")}
|
||||||
width={100}
|
width={100}
|
||||||
height={100}
|
height={100}
|
||||||
className="rounded-full mt-2 mx-auto"
|
className="rounded-full m-2 mt-0"
|
||||||
/>
|
/>
|
||||||
<h3 className="text-center">
|
<h3 className="text-center">
|
||||||
{user.username || user?.name || user?.email || "Anon"}
|
{user.username || user?.name || user?.email || "Anon"}
|
||||||
@ -84,7 +84,7 @@ const UserProfileCard = ({ user }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{user?.createdAt && (
|
{user?.createdAt && (
|
||||||
<p className="truncate text-center">
|
<p className="truncate">
|
||||||
Joined: {new Date(user.createdAt).toLocaleDateString()}
|
Joined: {new Date(user.createdAt).toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@ -119,21 +119,21 @@ const UserProfileCard = ({ user }) => {
|
|||||||
<i className="pi pi-question-circle nip05-tooltip text-xs cursor-pointer" />
|
<i className="pi pi-question-circle nip05-tooltip text-xs cursor-pointer" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
<div className="flex flex-col justify-center min-w-[140px] px-2">
|
<div className="flex flex-col justify-center min-w-[140px] px-2">
|
||||||
<button
|
<button
|
||||||
className="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full font-semibold"
|
className="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full font-semibold"
|
||||||
onClick={() => setShowBadges(true)}
|
onClick={() => setShowBadges(true)}
|
||||||
>
|
>
|
||||||
View Badges
|
View Badges
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const DesktopProfileCard = () => (
|
const DesktopProfileCard = () => (
|
||||||
<div className="w-1/4 bg-gray-800 rounded-lg p-2 py-1 border border-gray-700 shadow-md h-[330px]">
|
<div className="w-full bg-gray-800 rounded-lg p-2 py-1 border border-gray-700 shadow-md h-[330px]">
|
||||||
<div className="flex flex-row w-full justify-around">
|
<div className="flex flex-row w-full justify-around">
|
||||||
<Image
|
<Image
|
||||||
alt="user's avatar"
|
alt="user's avatar"
|
||||||
|
@ -1,37 +1,23 @@
|
|||||||
import React, { useRef, useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import GenericButton from "@/components/buttons/GenericButton";
|
import GenericButton from "@/components/buttons/GenericButton";
|
||||||
import { DataTable } from "primereact/datatable";
|
import { DataTable } from "primereact/datatable";
|
||||||
import { Column } from "primereact/column";
|
import { Column } from "primereact/column";
|
||||||
import { Menu } from "primereact/menu";
|
import UserProfileCard from "@/components/profile/UserProfileCard";
|
||||||
import { useImageProxy } from "@/hooks/useImageProxy";
|
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import { ProgressSpinner } from "primereact/progressspinner";
|
|
||||||
import { useNDKContext } from "@/context/NDKContext";
|
import { useNDKContext } from "@/context/NDKContext";
|
||||||
import useWindowWidth from "@/hooks/useWindowWidth";
|
import useWindowWidth from "@/hooks/useWindowWidth";
|
||||||
import Image from "next/image";
|
|
||||||
import PurchasedListItem from "@/components/content/lists/PurchasedListItem";
|
|
||||||
import { formatDateTime } from "@/utils/time";
|
|
||||||
import BitcoinConnectButton from "@/components/bitcoinConnect/BitcoinConnect";
|
import BitcoinConnectButton from "@/components/bitcoinConnect/BitcoinConnect";
|
||||||
import { Panel } from "primereact/panel";
|
|
||||||
import { nip19 } from "nostr-tools";
|
|
||||||
import { InputText } from "primereact/inputtext";
|
import { InputText } from "primereact/inputtext";
|
||||||
import { Tooltip } from "primereact/tooltip";
|
|
||||||
import { useToast } from "@/hooks/useToast";
|
import { useToast } from "@/hooks/useToast";
|
||||||
import SubscribeModal from "@/components/profile/subscription/SubscribeModal";
|
import SubscribeModal from "@/components/profile/subscription/SubscribeModal";
|
||||||
import appConfig from "@/config/appConfig";
|
import appConfig from "@/config/appConfig";
|
||||||
|
import UserRelaysTable from "@/components/profile/DataTables/UserRelaysTable";
|
||||||
|
|
||||||
const UserSettings = () => {
|
const UserSettings = () => {
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
const [collapsed, setCollapsed] = useState(true);
|
|
||||||
const { ndk, userRelays, setUserRelays, reInitializeNDK } = useNDKContext();
|
const { ndk, userRelays, setUserRelays, reInitializeNDK } = useNDKContext();
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const { returnImageProxy } = useImageProxy();
|
|
||||||
const menu = useRef(null);
|
|
||||||
const windowWidth = useWindowWidth();
|
const windowWidth = useWindowWidth();
|
||||||
const [newRelayUrl, setNewRelayUrl] = useState("");
|
|
||||||
const { showToast } = useToast();
|
|
||||||
const [relayStatuses, setRelayStatuses] = useState({});
|
|
||||||
const [updateTrigger, setUpdateTrigger] = useState(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
@ -39,256 +25,40 @@ const UserSettings = () => {
|
|||||||
}
|
}
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ndk) {
|
|
||||||
updateRelayStatuses();
|
|
||||||
}
|
|
||||||
}, [ndk]);
|
|
||||||
|
|
||||||
const copyToClipboard = (text) => {
|
|
||||||
navigator.clipboard.writeText(text);
|
|
||||||
showToast("success", "Copied", "Copied to clipboard");
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const updateRelayStatuses = useCallback(() => {
|
|
||||||
// export enum NDKRelayStatus {
|
|
||||||
// DISCONNECTING, // 0
|
|
||||||
// DISCONNECTED, // 1
|
|
||||||
// RECONNECTING, // 2
|
|
||||||
// FLAPPING, // 3
|
|
||||||
// CONNECTING, // 4
|
|
||||||
|
|
||||||
// // connected states
|
|
||||||
// CONNECTED, // 5
|
|
||||||
// AUTH_REQUESTED, // 6
|
|
||||||
// AUTHENTICATING, // 7
|
|
||||||
// AUTHENTICATED, // 8
|
|
||||||
// }
|
|
||||||
if (ndk) {
|
|
||||||
const statuses = {};
|
|
||||||
ndk.pool.relays.forEach((relay, url) => {
|
|
||||||
statuses[url] = relay.connectivity.status === 5;
|
|
||||||
});
|
|
||||||
setRelayStatuses(statuses);
|
|
||||||
}
|
|
||||||
}, [ndk]);
|
|
||||||
|
|
||||||
// Effect for periodic polling
|
|
||||||
useEffect(() => {
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
setUpdateTrigger(prev => prev + 1);
|
|
||||||
}, 7000); // Poll every 7 seconds
|
|
||||||
|
|
||||||
return () => clearInterval(intervalId); // Cleanup on unmount
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Effect to update on every render and when updateTrigger changes
|
|
||||||
useEffect(() => {
|
|
||||||
updateRelayStatuses();
|
|
||||||
}, [updateRelayStatuses, updateTrigger]);
|
|
||||||
|
|
||||||
const relayStatusBody = (url) => {
|
|
||||||
const isConnected = relayStatuses[url];
|
|
||||||
return (
|
|
||||||
<i className={`pi ${isConnected ? 'pi-check-circle text-green-500' : 'pi-times-circle text-red-500'}`}></i>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addRelay = () => {
|
|
||||||
if (newRelayUrl && !userRelays.includes(newRelayUrl)) {
|
|
||||||
setUserRelays([...userRelays, newRelayUrl]);
|
|
||||||
setNewRelayUrl("");
|
|
||||||
reInitializeNDK();
|
|
||||||
setCollapsed(true);
|
|
||||||
showToast("success", "Relay added", "Relay successfully added to your list of relays.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeRelay = (url) => {
|
|
||||||
if (!appConfig.defaultRelayUrls.includes(url)) {
|
|
||||||
setUserRelays(userRelays.filter(relay => relay !== url));
|
|
||||||
reInitializeNDK();
|
|
||||||
setCollapsed(true);
|
|
||||||
showToast("success", "Relay removed", "Relay successfully removed from your list of relays.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const relayActionsBody = (rowData) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{!appConfig.defaultRelayUrls.includes(rowData) ? (
|
|
||||||
<GenericButton
|
|
||||||
icon="pi pi-trash"
|
|
||||||
className="p-button-rounded p-button-danger p-button-text"
|
|
||||||
onClick={() => removeRelay(rowData)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<GenericButton
|
|
||||||
icon="pi pi-trash"
|
|
||||||
className="p-button-rounded p-button-danger p-button-text opacity-50"
|
|
||||||
onClick={() => removeRelay(rowData)}
|
|
||||||
tooltip="Cannot remove default relays at this time (soon ™)"
|
|
||||||
tooltipOptions={{ position: 'top' }}
|
|
||||||
style={{
|
|
||||||
pointerEvents: 'none',
|
|
||||||
cursor: 'not-allowed'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PanelHeader = (options) => {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-row justify-between px-4 py-[6px] bg-gray-800 rounded-t-lg border-b border-gray-700">
|
|
||||||
<p className="text-[#f8f8ff] text-900 text-xl mt-2 h-fit font-bold">Relays</p>
|
|
||||||
<GenericButton
|
|
||||||
onClick={options.onTogglerClick}
|
|
||||||
icon={options.collapsed ? "pi pi-plus" : "pi pi-minus"}
|
|
||||||
className="p-button-rounded p-button-success p-button-text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const header = (
|
|
||||||
<div className="flex flex-wrap align-items-center justify-content-between gap-2">
|
|
||||||
<span className="text-xl text-900 font-bold text-[#f8f8ff]">Purchases</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const menuItems = [
|
|
||||||
...(user?.privkey ? [{
|
|
||||||
label: 'Copy nsec',
|
|
||||||
icon: 'pi pi-key',
|
|
||||||
command: () => {
|
|
||||||
const privkeyBuffer = Buffer.from(user.privkey, 'hex');
|
|
||||||
copyToClipboard(nip19.nsecEncode(privkeyBuffer));
|
|
||||||
}
|
|
||||||
}] : []),
|
|
||||||
{
|
|
||||||
label: 'Copy npub',
|
|
||||||
icon: 'pi pi-user',
|
|
||||||
command: () => {
|
|
||||||
copyToClipboard(nip19.npubEncode(user?.pubkey));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Open Nostr Profile',
|
|
||||||
icon: 'pi pi-external-link',
|
|
||||||
command: () => window.open(`https://nostr.com/${nip19.npubEncode(user?.pubkey)}`, '_blank')
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
user && (
|
user && (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
{
|
{windowWidth < 768 && (
|
||||||
windowWidth < 768 && (
|
<h1 className="text-3xl font-bold mb-6">Settings</h1>
|
||||||
<h1 className="text-3xl font-bold mb-6">Settings</h1>
|
)}
|
||||||
)
|
<div className="w-full flex flex-row max-lap:flex-col">
|
||||||
}
|
<div className="w-1/4 h-full max-lap:w-full">
|
||||||
<div className="w-full flex flex-col justify-center mx-auto">
|
<UserProfileCard user={user} />
|
||||||
<div className="relative flex w-full items-center justify-center">
|
|
||||||
<Image
|
|
||||||
alt="user's avatar"
|
|
||||||
src={returnImageProxy(user.avatar, user?.pubkey || "")}
|
|
||||||
width={100}
|
|
||||||
height={100}
|
|
||||||
className="rounded-full my-4"
|
|
||||||
/>
|
|
||||||
<div className="absolute top-8 right-80 max-tab:right-20 max-mob:left-0">
|
|
||||||
<i
|
|
||||||
className="pi pi-ellipsis-h text-2xl cursor-pointer user-menu-trigger"
|
|
||||||
onClick={(e) => menu.current.toggle(e)}
|
|
||||||
/>
|
|
||||||
<Menu
|
|
||||||
model={menuItems}
|
|
||||||
popup
|
|
||||||
ref={menu}
|
|
||||||
id="profile-options-menu"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 className="text-center text-2xl my-2">
|
{/* Lightning Info Card */}
|
||||||
{user.username || user?.name || user?.email || "Anon"}
|
<div className="bg-gray-800 rounded-lg p-4 my-4 border border-gray-700">
|
||||||
</h1>
|
<div className="flex items-center gap-2 mb-4">
|
||||||
{user.pubkey && (
|
|
||||||
<h2 className="text-center text-xl my-2 truncate max-tab:px-4 max-mob:px-4">
|
|
||||||
<Tooltip target=".pubkey-tooltip" content={"this is your nostr npub"} />
|
|
||||||
{nip19.npubEncode(user.pubkey)} <i className="pi pi-question-circle text-xl pubkey-tooltip" />
|
|
||||||
</h2>
|
|
||||||
)}
|
|
||||||
{user?.lightningAddress && (
|
|
||||||
<h3 className="w-fit mx-auto text-center text-xl my-2 bg-gray-800 rounded-lg p-4">
|
|
||||||
<span className="font-bold">Lightning Address:</span> {user.lightningAddress.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.lightningAddress.name + "@plebdevs.com")} />
|
|
||||||
</h3>
|
|
||||||
)}
|
|
||||||
{user?.nip05 && (
|
|
||||||
<h3 className="w-fit mx-auto text-center text-xl my-2 bg-gray-800 rounded-lg p-4">
|
|
||||||
<span className="font-bold">NIP-05:</span> {user.nip05.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.nip05.name + "@plebdevs.com")} />
|
|
||||||
</h3>
|
|
||||||
)}
|
|
||||||
<div className="bg-gray-800 rounded-lg p-6 shadow-lg w-1/4 mx-auto my-4 max-mob:w-full max-tab:w-full">
|
|
||||||
<div className="flex flex-col items-center gap-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<i className="pi pi-bolt text-yellow-500 text-2xl"></i>
|
<i className="pi pi-bolt text-yellow-500 text-2xl"></i>
|
||||||
<h3 className="text-xl font-semibold max-mob:text-base max-tab:text-base">
|
<h3 className="text-xl font-semibold">Lightning Wallet Connection</h3>
|
||||||
Lightning Wallet Connection
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p className="text-gray-400 mb-4">
|
||||||
Connect your Lightning wallet for easier payments across the platform
|
Connect your Lightning wallet for easier payments across the platform
|
||||||
</p>
|
</p>
|
||||||
<BitcoinConnectButton />
|
<BitcoinConnectButton />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Subscription Modal */}
|
||||||
|
{user && <SubscribeModal user={user} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-3/4 flex flex-col justify-center mx-auto max-lap:w-full ml-2 max-lap:ml-0">
|
||||||
|
<UserRelaysTable
|
||||||
|
ndk={ndk}
|
||||||
|
userRelays={userRelays}
|
||||||
|
setUserRelays={setUserRelays}
|
||||||
|
reInitializeNDK={reInitializeNDK}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{user && (
|
|
||||||
<SubscribeModal user={user} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Panel
|
|
||||||
headerTemplate={PanelHeader}
|
|
||||||
toggleable
|
|
||||||
collapsed={collapsed}
|
|
||||||
onToggle={(e) => setCollapsed(e.value)}
|
|
||||||
>
|
|
||||||
<div className="flex flex-row justify-between">
|
|
||||||
<InputText
|
|
||||||
placeholder="Relay URL"
|
|
||||||
value={newRelayUrl}
|
|
||||||
onChange={(e) => setNewRelayUrl(e.target.value)}
|
|
||||||
/>
|
|
||||||
<GenericButton
|
|
||||||
label="Add"
|
|
||||||
severity="success"
|
|
||||||
className='w-fit px-4'
|
|
||||||
outlined
|
|
||||||
onClick={addRelay}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Panel>
|
|
||||||
<DataTable value={userRelays}
|
|
||||||
pt={{
|
|
||||||
wrapper: {
|
|
||||||
className: "rounded-lg rounded-t-none"
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
className: "rounded-t-lg"
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onValueChange={() => setUpdateTrigger(prev => prev + 1)} // Trigger update when table value changes
|
|
||||||
>
|
|
||||||
<Column field={(url) => url} header="Relay URL"></Column>
|
|
||||||
<Column body={relayStatusBody} header="Status"></Column>
|
|
||||||
<Column body={relayActionsBody} header="Actions"></Column>
|
|
||||||
</DataTable>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -148,7 +148,7 @@ const SubscribeModal = ({ user }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card title={subscriptionCardTitle} className="w-1/4 m-4 mx-auto max-mob:w-full max-tab:w-full">
|
<Card title={subscriptionCardTitle} className="w-full m-4 mx-auto border border-gray-700">
|
||||||
{subscribed && !user?.role?.nwc && (
|
{subscribed && !user?.role?.nwc && (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<Message className="w-fit" severity="success" text="Subscribed!" />
|
<Message className="w-fit" severity="success" text="Subscribed!" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user