forked from Kieran/snort
Compare commits
9 Commits
dda38ab824
...
601ecaa9de
Author | SHA1 | Date | |
---|---|---|---|
601ecaa9de | |||
9fec0ab95b | |||
297186c33c | |||
81a4c1aac1 | |||
984e216f0b | |||
f659bef15d | |||
d3f3e50c0b | |||
1d5107bd89 | |||
689839e043 |
@ -2,7 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Feature packed nostr client" />
|
||||
<meta
|
||||
|
20
packages/app/src/Element/Button/NavLink.tsx
Normal file
20
packages/app/src/Element/Button/NavLink.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { NavLink as RouterNavLink, NavLinkProps, useLocation } from "react-router-dom";
|
||||
|
||||
export default function NavLink(props: NavLinkProps) {
|
||||
const { to, onClick, ...rest } = props;
|
||||
const location = useLocation();
|
||||
|
||||
const isActive = location.pathname === to.toString();
|
||||
|
||||
const handleClick = event => {
|
||||
if (onClick) {
|
||||
onClick(event);
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
}
|
||||
};
|
||||
|
||||
return <RouterNavLink to={to} onClick={handleClick} {...rest} />;
|
||||
}
|
@ -33,7 +33,7 @@ export const ProxyImg = ({ size, className, promptToLoadDirectly, missingImageEl
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const src = bypass ? props.src : proxy(props.src ?? "", size);
|
||||
const src = loadFailed && bypass ? props.src : proxy(props.src ?? "", size);
|
||||
if (!src || (loadFailed && !bypass)) return missingImageElement;
|
||||
return (
|
||||
<img
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import NavLink from "@/Element/Button/NavLink";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import Icon from "@/Icons/Icon";
|
||||
import { ProfileLink } from "@/Element/User/ProfileLink";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { LogoHeader } from "./LogoHeader";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Icon from "@/Icons/Icon";
|
||||
import { ProfileLink } from "../../Element/User/ProfileLink";
|
||||
import Avatar from "../../Element/User/Avatar";
|
||||
@ -10,6 +10,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
||||
import classNames from "classnames";
|
||||
import { getCurrentSubscription } from "@/Subscription";
|
||||
import { HasNotificationsMarker } from "@/Pages/Layout/HasNotificationsMarker";
|
||||
import NavLink from "@/Element/Button/NavLink";
|
||||
|
||||
const MENU_ITEMS = [
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { base64 } from "@scure/base";
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
@ -9,6 +9,7 @@ import useLogin from "@/Hooks/useLogin";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import SnortApi from "@/External/SnortApi";
|
||||
import { HasNotificationsMarker } from "@/Pages/Layout/HasNotificationsMarker";
|
||||
import NavLink from "@/Element/Button/NavLink";
|
||||
|
||||
const NotificationsHeader = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -70,10 +71,10 @@ const NotificationsHeader = () => {
|
||||
|
||||
return (
|
||||
<div className="flex justify-between">
|
||||
<Link className="btn" to="/notifications" onClick={goToNotifications}>
|
||||
<NavLink className="btn" to="/notifications" onClick={goToNotifications}>
|
||||
<Icon name="bell-02" size={24} />
|
||||
<HasNotificationsMarker />
|
||||
</Link>
|
||||
</NavLink>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import "./Layout.css";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
|
||||
import Icon from "@/Icons/Icon";
|
||||
@ -21,7 +21,6 @@ import Footer from "@/Pages/Layout/Footer";
|
||||
|
||||
export default function Index() {
|
||||
const location = useLocation();
|
||||
const [pageClass, setPageClass] = useState("page");
|
||||
const { id, stalker } = useLogin(s => ({ id: s.id, stalker: s.stalker ?? false }));
|
||||
|
||||
useTheme();
|
||||
@ -29,22 +28,9 @@ export default function Index() {
|
||||
useLoginFeed();
|
||||
|
||||
const hideHeaderPaths = ["/login", "/new"];
|
||||
const shouldHideFooter = location.pathname.startsWith("/messages/");
|
||||
const shouldHideHeader = hideHeaderPaths.some(path => location.pathname.startsWith(path));
|
||||
|
||||
const pageClassPaths = useMemo(
|
||||
() => ({
|
||||
widePage: ["/login", "/messages"],
|
||||
noScroll: ["/messages"],
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const isWidePage = pageClassPaths.widePage.some(path => location.pathname.startsWith(path));
|
||||
const isNoScroll = pageClassPaths.noScroll.some(path => location.pathname.startsWith(path));
|
||||
setPageClass(isWidePage ? (isNoScroll ? "scroll-lock" : "") : "page");
|
||||
}, [location, pageClassPaths]);
|
||||
|
||||
const handleKeyboardShortcut = useCallback(event => {
|
||||
if (event.target && !isFormElement(event.target as HTMLElement)) {
|
||||
event.preventDefault();
|
||||
@ -58,11 +44,11 @@ export default function Index() {
|
||||
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<div className={`${pageClass} w-full max-w-screen-xl`}>
|
||||
<div className="w-full max-w-screen-xl">
|
||||
{!shouldHideHeader && <Header />}
|
||||
<div className="flex flex-row w-full">
|
||||
<NavSidebar />
|
||||
<div className="flex flex-1 flex-col overflow-x-hidden">
|
||||
<div className="flex flex-1 flex-col overflow-x-hidden pb-footer-height md:pb-0">
|
||||
<ErrorBoundary>
|
||||
<Outlet />
|
||||
</ErrorBoundary>
|
||||
@ -73,7 +59,7 @@ export default function Index() {
|
||||
</div>
|
||||
<LoginUnlock />
|
||||
{isStalker && <StalkerModal id={id} />}
|
||||
<Footer />
|
||||
{!shouldHideFooter && <Footer />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -88,9 +88,9 @@ export default function MessagesPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 h-screen overflow-hidden">
|
||||
<div className="flex flex-1 md:h-screen md:overflow-hidden">
|
||||
{(pageWidth >= TwoCol || !chat) && (
|
||||
<div className="overflow-y-auto h-screen p-1 w-full md:w-1/3 flex-shrink-0">
|
||||
<div className="overflow-y-auto md:h-screen p-1 w-full md:w-1/3 flex-shrink-0">
|
||||
<div className="flex items-center justify-between p-2">
|
||||
<button disabled={unreadCount <= 0} type="button" className="text-sm font-semibold">
|
||||
<FormattedMessage defaultMessage="Mark all read" id="ShdEie" />
|
||||
|
@ -157,7 +157,7 @@ export default function ProfilePage({ id: propId, state }: ProfilePageProps) {
|
||||
<div className="flex flex-col g4">
|
||||
<h2 className="flex items-center g4">
|
||||
<DisplayName user={user} pubkey={user?.pubkey ?? ""} />
|
||||
<FollowsYou followsMe={follows.includes(loginPubKey ?? "")} />
|
||||
<FollowsYou followsMe={user?.pubkey !== loginPubKey && follows.includes(loginPubKey ?? "")} />
|
||||
</h2>
|
||||
{user?.nip05 && <Nip05 nip05={user.nip05} pubkey={user.pubkey} />}
|
||||
</div>
|
||||
@ -357,7 +357,8 @@ export default function ProfilePage({ id: propId, state }: ProfilePageProps) {
|
||||
return <TabElement key={v.value} t={v} tab={tab} setTab={setTab} />;
|
||||
}
|
||||
|
||||
const w = window.document.querySelector(".page")?.clientWidth;
|
||||
const bannerWidth = Math.min(window.innerWidth, 940);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="profile">
|
||||
@ -366,7 +367,7 @@ export default function ProfilePage({ id: propId, state }: ProfilePageProps) {
|
||||
alt="banner"
|
||||
className="banner pointer"
|
||||
src={user.banner}
|
||||
size={w}
|
||||
size={bannerWidth}
|
||||
onClick={() => setModalImage(user.banner || "")}
|
||||
/>
|
||||
)}
|
||||
|
@ -926,3 +926,7 @@ svg.repeat {
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none; /* Safari and Chrome */
|
||||
}
|
||||
|
||||
.pb-safe-area {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
@ -123,7 +123,7 @@
|
||||
"C8HhVE": "Vorgeschlagene Follows",
|
||||
"CHTbO3": "Lightning Zahlungsanforderung konnte nicht geladen werden",
|
||||
"CVWeJ6": "Angesagte Personen",
|
||||
"CYkOCI": "und {count} andere, denen du folgst",
|
||||
"CYkOCI": "und {count} anderen, denen du folgst",
|
||||
"CmZ9ls": "{n} Stummgeschaltet",
|
||||
"CsCUYo": "{n} sats",
|
||||
"Cu/K85": "Übersetzt von {lang}",
|
||||
@ -180,7 +180,7 @@
|
||||
"IVbtTS": "Allen {n} sats zappen",
|
||||
"IWz1ta": "Automatische Übersetzung",
|
||||
"Ig9/a1": "Sende {n} sats an {name}",
|
||||
"IgsWFG": "Not followed by anyone you follow",
|
||||
"IgsWFG": "Von keinem gefolgt, dem du folgst",
|
||||
"IoQq+a": "Klicke hier, um trotzdem zu laden",
|
||||
"Ix8l+B": "Angesagte Notes",
|
||||
"J+dIsA": "Abonnements",
|
||||
|
@ -21,6 +21,9 @@ module.exports = {
|
||||
spacing: {
|
||||
px: "1px",
|
||||
},
|
||||
padding: {
|
||||
"footer-height": "calc(56px + env(safe-area-inset-bottom))",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
|
Loading…
Reference in New Issue
Block a user