2022-12-29 15:36:40 +00:00
|
|
|
import "./Layout.css";
|
2023-01-30 19:58:49 +00:00
|
|
|
import { useEffect } from "react"
|
2022-12-27 23:46:13 +00:00
|
|
|
import { useDispatch, useSelector } from "react-redux";
|
2023-01-12 12:00:44 +00:00
|
|
|
import { Outlet, useNavigate } from "react-router-dom";
|
2023-01-28 21:43:56 +00:00
|
|
|
import Envelope from "Icons/Envelope";
|
|
|
|
import Bell from "Icons/Bell";
|
|
|
|
import Search from "Icons/Search";
|
2022-12-29 15:36:40 +00:00
|
|
|
|
2023-01-20 11:11:50 +00:00
|
|
|
import { RootState } from "State/Store";
|
2023-01-23 01:45:53 +00:00
|
|
|
import { init, UserPreferences } from "State/Login";
|
2023-01-20 11:11:50 +00:00
|
|
|
import { HexKey, RawEvent, TaggedRawEvent } from "Nostr";
|
|
|
|
import { RelaySettings } from "Nostr/Connection";
|
|
|
|
import { System } from "Nostr/System"
|
|
|
|
import ProfileImage from "Element/ProfileImage";
|
|
|
|
import useLoginFeed from "Feed/LoginFeed";
|
|
|
|
import { totalUnread } from "Pages/MessagesPage";
|
2023-01-24 12:33:18 +00:00
|
|
|
import { SearchRelays } from 'Const';
|
2023-01-23 01:45:53 +00:00
|
|
|
import useEventPublisher from "Feed/EventPublisher";
|
2023-01-28 21:43:56 +00:00
|
|
|
import useModeration from "Hooks/useModeration";
|
|
|
|
|
2022-12-18 14:51:47 +00:00
|
|
|
|
2023-01-16 18:14:08 +00:00
|
|
|
export default function Layout() {
|
2022-12-27 23:46:13 +00:00
|
|
|
const dispatch = useDispatch();
|
2022-12-18 14:51:47 +00:00
|
|
|
const navigate = useNavigate();
|
2023-01-16 18:14:08 +00:00
|
|
|
const isInit = useSelector<RootState, boolean | undefined>(s => s.login.loggedOut);
|
|
|
|
const key = useSelector<RootState, HexKey | undefined>(s => s.login.publicKey);
|
|
|
|
const relays = useSelector<RootState, Record<string, RelaySettings>>(s => s.login.relays);
|
|
|
|
const notifications = useSelector<RootState, TaggedRawEvent[]>(s => s.login.notifications);
|
|
|
|
const readNotifications = useSelector<RootState, number>(s => s.login.readNotifications);
|
2023-01-17 10:47:00 +00:00
|
|
|
const dms = useSelector<RootState, RawEvent[]>(s => s.login.dms);
|
2023-01-28 21:43:56 +00:00
|
|
|
const { isMuted } = useModeration();
|
|
|
|
const filteredDms = dms.filter(a => !isMuted(a.pubkey))
|
2023-01-20 17:07:14 +00:00
|
|
|
const prefs = useSelector<RootState, UserPreferences>(s => s.login.preferences);
|
2023-01-23 01:45:53 +00:00
|
|
|
const pub = useEventPublisher();
|
2022-12-28 23:28:28 +00:00
|
|
|
useLoginFeed();
|
2022-12-18 14:51:47 +00:00
|
|
|
|
2023-01-28 16:58:52 +00:00
|
|
|
useEffect(() => {
|
2023-01-25 13:37:35 +00:00
|
|
|
System.nip42Auth = pub.nip42Auth
|
|
|
|
},[pub])
|
|
|
|
|
2022-12-18 14:51:47 +00:00
|
|
|
useEffect(() => {
|
2022-12-30 23:35:02 +00:00
|
|
|
if (relays) {
|
2022-12-28 23:28:28 +00:00
|
|
|
for (let [k, v] of Object.entries(relays)) {
|
2022-12-30 23:35:02 +00:00
|
|
|
System.ConnectToRelay(k, v);
|
2022-12-18 14:51:47 +00:00
|
|
|
}
|
2023-01-16 11:53:15 +00:00
|
|
|
for (let [k, v] of System.Sockets) {
|
2023-01-24 12:33:18 +00:00
|
|
|
if (!relays[k] && !SearchRelays.has(k)) {
|
2023-01-09 12:40:10 +00:00
|
|
|
System.DisconnectRelay(k);
|
|
|
|
}
|
|
|
|
}
|
2022-12-18 14:51:47 +00:00
|
|
|
}
|
2022-12-30 23:35:02 +00:00
|
|
|
}, [relays]);
|
2022-12-18 14:51:47 +00:00
|
|
|
|
2023-01-20 18:59:08 +00:00
|
|
|
function setTheme(theme: "light" | "dark") {
|
2023-01-20 17:07:14 +00:00
|
|
|
const elm = document.documentElement;
|
2023-01-20 18:59:08 +00:00
|
|
|
if (theme === "light" && !elm.classList.contains("light")) {
|
2023-01-20 17:07:14 +00:00
|
|
|
elm.classList.add("light");
|
2023-01-20 18:59:08 +00:00
|
|
|
} else if (theme === "dark" && elm.classList.contains("light")) {
|
2023-01-20 17:07:14 +00:00
|
|
|
elm.classList.remove("light");
|
|
|
|
}
|
2023-01-20 18:59:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
let osTheme = window.matchMedia("(prefers-color-scheme: light)");
|
|
|
|
setTheme(prefs.theme === "system" && osTheme.matches ? "light" : prefs.theme === "light" ? "light" : "dark");
|
|
|
|
|
|
|
|
osTheme.onchange = (e) => {
|
|
|
|
if (prefs.theme === "system") {
|
|
|
|
setTheme(e.matches ? "light" : "dark");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return () => { osTheme.onchange = null; }
|
|
|
|
}, [prefs.theme]);
|
2023-01-20 17:07:14 +00:00
|
|
|
|
2022-12-27 23:46:13 +00:00
|
|
|
useEffect(() => {
|
|
|
|
dispatch(init());
|
|
|
|
}, []);
|
|
|
|
|
2023-01-16 18:14:08 +00:00
|
|
|
async function goToNotifications(e: any) {
|
2023-01-03 13:17:09 +00:00
|
|
|
e.stopPropagation();
|
|
|
|
// request permissions to send notifications
|
2023-01-19 11:03:51 +00:00
|
|
|
if ("Notification" in window) {
|
2023-01-03 13:17:09 +00:00
|
|
|
try {
|
2023-01-19 11:03:51 +00:00
|
|
|
if (Notification.permission !== "granted") {
|
|
|
|
let res = await Notification.requestPermission();
|
|
|
|
console.debug(res);
|
|
|
|
}
|
2023-01-03 13:17:09 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
navigate("/notifications");
|
|
|
|
}
|
|
|
|
|
2022-12-29 15:36:40 +00:00
|
|
|
function accountHeader() {
|
2023-01-10 07:09:09 +00:00
|
|
|
const unreadNotifications = notifications?.filter(a => (a.created_at * 1000) > readNotifications).length;
|
2023-01-28 21:43:56 +00:00
|
|
|
const unreadDms = key ? totalUnread(filteredDms, key) : 0;
|
2022-12-29 15:36:40 +00:00
|
|
|
return (
|
2023-01-25 18:08:53 +00:00
|
|
|
<div className="header-actions">
|
2023-01-28 21:43:56 +00:00
|
|
|
<div className="btn btn-rnd" onClick={(e) => navigate("/search")}>
|
|
|
|
<Search />
|
|
|
|
</div>
|
|
|
|
<div className="btn btn-rnd" onClick={(e) => navigate("/messages")}>
|
2023-01-25 18:08:53 +00:00
|
|
|
<Envelope />
|
|
|
|
{unreadDms > 0 && (<span className="has-unread"></span>)}
|
2023-01-12 09:48:39 +00:00
|
|
|
</div>
|
2023-01-28 21:43:56 +00:00
|
|
|
<div className="btn btn-rnd" onClick={(e) => goToNotifications(e)}>
|
2023-01-25 18:08:53 +00:00
|
|
|
<Bell />
|
2023-01-26 07:30:23 +00:00
|
|
|
{unreadNotifications > 0 && (<span className="has-unread"></span>)}
|
2022-12-29 15:36:40 +00:00
|
|
|
</div>
|
2023-01-16 18:14:08 +00:00
|
|
|
<ProfileImage pubkey={key || ""} showUsername={false} />
|
2023-01-25 18:08:53 +00:00
|
|
|
</div>
|
2022-12-29 15:36:40 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-02 11:59:03 +00:00
|
|
|
if (typeof isInit !== "boolean") {
|
|
|
|
return null;
|
|
|
|
}
|
2022-12-18 14:51:47 +00:00
|
|
|
return (
|
|
|
|
<div className="page">
|
2023-01-25 18:08:53 +00:00
|
|
|
<header>
|
|
|
|
<div className="logo" onClick={() => navigate("/")}>Snort</div>
|
2022-12-18 14:51:47 +00:00
|
|
|
<div>
|
2022-12-29 15:36:40 +00:00
|
|
|
{key ? accountHeader() :
|
2023-01-25 18:08:53 +00:00
|
|
|
<button type="button" onClick={() => navigate("/login")}>Login</button>
|
2022-12-27 23:46:13 +00:00
|
|
|
}
|
2022-12-18 14:51:47 +00:00
|
|
|
</div>
|
2023-01-25 18:08:53 +00:00
|
|
|
</header>
|
2022-12-18 14:51:47 +00:00
|
|
|
|
2023-01-16 18:14:08 +00:00
|
|
|
<Outlet />
|
2022-12-18 14:51:47 +00:00
|
|
|
</div>
|
|
|
|
)
|
2023-01-19 23:19:09 +00:00
|
|
|
}
|