Style tweaks
This commit is contained in:
parent
4e21c4834b
commit
4b85a16904
@ -1,20 +1,21 @@
|
||||
import "./Avatar.css";
|
||||
import Nostrich from "nostrich.webp";
|
||||
|
||||
import { CSSProperties, useEffect, useState } from "react";
|
||||
import type { UserMetadata } from "@snort/system";
|
||||
|
||||
import useImgProxy from "Hooks/useImgProxy";
|
||||
import { getDisplayName } from "Element/ProfileImage";
|
||||
import { defaultAvatar } from "SnortUtils";
|
||||
|
||||
interface AvatarProps {
|
||||
pubkey: string;
|
||||
user?: UserMetadata;
|
||||
onClick?: () => void;
|
||||
size?: number;
|
||||
image?: string;
|
||||
}
|
||||
const Avatar = ({ user, size, onClick, image }: AvatarProps) => {
|
||||
const [url, setUrl] = useState<string>(Nostrich);
|
||||
const Avatar = ({ pubkey, user, size, onClick, image }: AvatarProps) => {
|
||||
const [url, setUrl] = useState("");
|
||||
const { proxy } = useImgProxy();
|
||||
|
||||
useEffect(() => {
|
||||
@ -23,7 +24,7 @@ const Avatar = ({ user, size, onClick, image }: AvatarProps) => {
|
||||
const proxyUrl = proxy(url, size ?? 120);
|
||||
setUrl(proxyUrl);
|
||||
} else {
|
||||
setUrl(Nostrich);
|
||||
setUrl(defaultAvatar(pubkey));
|
||||
}
|
||||
}, [user, image]);
|
||||
|
||||
|
@ -22,6 +22,6 @@
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.modal-body button:hover {
|
||||
.modal-body button.secondary:hover {
|
||||
background-color: var(--gray);
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import "./Nip05.css";
|
||||
import { HexKey } from "@snort/system";
|
||||
|
||||
import Icon from "Icons/Icon";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
export function useIsVerified(pubkey: HexKey, bypassCheck?: boolean) {
|
||||
@ -28,7 +26,6 @@ const Nip05 = ({ nip05, pubkey, verifyNip = true }: Nip05Params) => {
|
||||
<span className="domain" data-domain={domain?.toLowerCase()}>
|
||||
{domain}
|
||||
</span>
|
||||
<Icon name="check-verified" className="badge" size={16} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
@ -81,6 +81,7 @@
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-color: var(--highlight);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
position: fixed;
|
||||
|
@ -19,9 +19,6 @@ a.pfp {
|
||||
}
|
||||
|
||||
.pfp .username {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
@ -54,11 +54,11 @@ export default function ProfileImage({
|
||||
return (
|
||||
<>
|
||||
<div className="avatar-wrapper">
|
||||
<Avatar user={user} size={size} />
|
||||
<Avatar pubkey={pubkey} user={user} size={size} />
|
||||
</div>
|
||||
{showUsername && (
|
||||
<div className="f-ellipsis">
|
||||
<div className="username">
|
||||
<div className="flex g4 username">
|
||||
<div>{name.trim()}</div>
|
||||
{nip05 && <Nip05 nip05={nip05} pubkey={pubkey} verifyNip={verifyNip} />}
|
||||
</div>
|
||||
|
@ -4,9 +4,11 @@ import { NostrEvent, TaggedNostrEvent } from "@snort/system";
|
||||
import PageSpinner from "Element/PageSpinner";
|
||||
import Note from "Element/Note";
|
||||
import NostrBandApi from "External/NostrBand";
|
||||
import { useReactions } from "Feed/FeedReactions";
|
||||
|
||||
export default function TrendingNotes() {
|
||||
const [posts, setPosts] = useState<Array<NostrEvent>>();
|
||||
const related = useReactions("trending", posts?.map(a => a.id) ?? []);
|
||||
|
||||
async function loadTrendingNotes() {
|
||||
const api = new NostrBandApi();
|
||||
@ -23,7 +25,7 @@ export default function TrendingNotes() {
|
||||
return (
|
||||
<>
|
||||
{posts.map(e => (
|
||||
<Note key={e.id} data={e as TaggedNostrEvent} related={[]} depth={0} />
|
||||
<Note key={e.id} data={e as TaggedNostrEvent} related={related?.data ?? []} depth={0} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
25
packages/app/src/Feed/FeedReactions.ts
Normal file
25
packages/app/src/Feed/FeedReactions.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { RequestBuilder, EventKind, NoteCollection } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export function useReactions(subId: string, ids: Array<string>, others?: (rb: RequestBuilder) => void) {
|
||||
const { preferences: pref } = useLogin();
|
||||
|
||||
const sub = useMemo(() => {
|
||||
const rb = new RequestBuilder(subId);
|
||||
if (ids.length > 0) {
|
||||
rb.withFilter()
|
||||
.kinds(
|
||||
pref.enableReactions
|
||||
? [EventKind.Reaction, EventKind.Repost, EventKind.ZapReceipt]
|
||||
: [EventKind.ZapReceipt, EventKind.Repost]
|
||||
)
|
||||
.tag("e", ids);
|
||||
}
|
||||
others?.(rb);
|
||||
return rb.numFilters > 0 ? rb : null;
|
||||
}, [ids]);
|
||||
|
||||
return useRequestBuilder(NoteCollection, sub);
|
||||
}
|
@ -6,6 +6,7 @@ import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils";
|
||||
import useTimelineWindow from "Hooks/useTimelineWindow";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { SearchRelays } from "Const";
|
||||
import { useReactions } from "./FeedReactions";
|
||||
|
||||
export interface TimelineFeedOptions {
|
||||
method: "TIME_RANGE" | "LIMIT_UNTIL";
|
||||
@ -157,26 +158,13 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
||||
return [];
|
||||
}
|
||||
|
||||
const subNext = useMemo(() => {
|
||||
const rb = new RequestBuilder(`timeline-related:${subject.type}:${subject.discriminator}`);
|
||||
const trackingEvents = main.data?.map(a => a.id) ?? [];
|
||||
if (trackingEvents.length > 0) {
|
||||
rb.withFilter()
|
||||
.kinds(
|
||||
pref.enableReactions
|
||||
? [EventKind.Reaction, EventKind.Repost, EventKind.ZapReceipt]
|
||||
: [EventKind.ZapReceipt, EventKind.Repost]
|
||||
)
|
||||
.tag("e", trackingEvents);
|
||||
}
|
||||
const related = useReactions(`timeline-related:${subject.type}:${subject.discriminator}`, trackingEvents, rb => {
|
||||
const trackingParentEvents = getParentEvents();
|
||||
if (trackingParentEvents.length > 0) {
|
||||
rb.withFilter().ids(trackingParentEvents);
|
||||
}
|
||||
return rb.numFilters > 0 ? rb : null;
|
||||
}, [main.data, pref, subject.type]);
|
||||
|
||||
const related = useRequestBuilder(NoteCollection, subNext);
|
||||
});
|
||||
|
||||
return {
|
||||
main: main.data,
|
||||
|
@ -77,7 +77,7 @@ export const DefaultPreferences = {
|
||||
language: "en",
|
||||
enableReactions: true,
|
||||
reactionEmoji: "+",
|
||||
autoLoadMedia: "follows-only",
|
||||
autoLoadMedia: "all",
|
||||
theme: "system",
|
||||
confirmReposts: false,
|
||||
showDebugMenus: false,
|
||||
|
@ -1,9 +1,7 @@
|
||||
import Nostrich from "nostrich.webp";
|
||||
|
||||
import { TaggedNostrEvent, EventKind, MetadataCache } from "@snort/system";
|
||||
import { getDisplayName } from "Element/ProfileImage";
|
||||
import { MentionRegex } from "Const";
|
||||
import { tagFilterOfTextRepost, unwrap } from "SnortUtils";
|
||||
import { defaultAvatar, tagFilterOfTextRepost, unwrap } from "SnortUtils";
|
||||
import { UserCache } from "Cache";
|
||||
import { LoginSession } from "Login";
|
||||
|
||||
@ -28,7 +26,7 @@ export async function makeNotification(ev: TaggedNostrEvent): Promise<Notificati
|
||||
.map(a => unwrap(a));
|
||||
const fromUser = UserCache.getFromCache(ev.pubkey);
|
||||
const name = getDisplayName(fromUser, ev.pubkey);
|
||||
const avatarUrl = fromUser?.picture || Nostrich;
|
||||
const avatarUrl = fromUser?.picture || defaultAvatar(ev.pubkey);
|
||||
return {
|
||||
title: `Reply from ${name}`,
|
||||
body: replaceTagsWithUser(ev, allUsers).substring(0, 50),
|
||||
|
@ -27,7 +27,7 @@ const HashTagsPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="main-content">
|
||||
<div className="main-content p">
|
||||
<div className="action-heading">
|
||||
<h2>#{tag}</h2>
|
||||
{isFollowing ? (
|
||||
|
@ -179,6 +179,7 @@ const AccountHeader = () => {
|
||||
{hasNotifications && <span className="has-unread"></span>}
|
||||
</Link>
|
||||
<Avatar
|
||||
pubkey={publicKey ?? ""}
|
||||
user={profile}
|
||||
onClick={() => {
|
||||
if (profile) {
|
||||
|
@ -382,7 +382,7 @@ export default function ProfilePage() {
|
||||
function avatar() {
|
||||
return (
|
||||
<div className="avatar-wrapper w-max">
|
||||
<Avatar user={user} />
|
||||
<Avatar pubkey={id ?? ""} user={user} />
|
||||
<div className="profile-actions">
|
||||
{renderIcons()}
|
||||
{!isMe && id && <FollowButton pubkey={id} />}
|
||||
|
@ -36,6 +36,7 @@ export default function RootPage() {
|
||||
{
|
||||
tab: "following",
|
||||
path: "/notes",
|
||||
show: Boolean(pubKey),
|
||||
element: (
|
||||
<>
|
||||
<Icon name="user-v2" />
|
||||
@ -46,6 +47,7 @@ export default function RootPage() {
|
||||
{
|
||||
tab: "trending-notes",
|
||||
path: "/trending/notes",
|
||||
show: true,
|
||||
element: (
|
||||
<>
|
||||
<Icon name="fire" />
|
||||
@ -56,6 +58,7 @@ export default function RootPage() {
|
||||
{
|
||||
tab: "conversations",
|
||||
path: "/conversations",
|
||||
show: Boolean(pubKey),
|
||||
element: (
|
||||
<>
|
||||
<Icon name="message-chat-circle" />
|
||||
@ -66,6 +69,7 @@ export default function RootPage() {
|
||||
{
|
||||
tab: "trending-people",
|
||||
path: "/trending/people",
|
||||
show: true,
|
||||
element: (
|
||||
<>
|
||||
<Icon name="user-up" />
|
||||
@ -76,6 +80,7 @@ export default function RootPage() {
|
||||
{
|
||||
tab: "suggested",
|
||||
path: "/suggested",
|
||||
show: Boolean(pubKey),
|
||||
element: (
|
||||
<>
|
||||
<Icon name="thumbs-up" />
|
||||
@ -86,6 +91,7 @@ export default function RootPage() {
|
||||
{
|
||||
tab: "global",
|
||||
path: "/global",
|
||||
show: true,
|
||||
element: (
|
||||
<>
|
||||
<Icon name="globe" />
|
||||
@ -96,12 +102,13 @@ export default function RootPage() {
|
||||
] as Array<{
|
||||
tab: RootPage;
|
||||
path: string;
|
||||
show: boolean;
|
||||
element: ReactNode;
|
||||
}>;
|
||||
|
||||
useEffect(() => {
|
||||
if (location.pathname === "/") {
|
||||
const t = pubKey ? preferences.defaultRootTab ?? "/notes" : "/global";
|
||||
const t = pubKey ? preferences.defaultRootTab ?? "/notes" : "/trending/notes";
|
||||
navigate(t);
|
||||
} else {
|
||||
const currentTab = menuItems.find(a => a.path === location.pathname)?.tab;
|
||||
@ -140,7 +147,9 @@ export default function RootPage() {
|
||||
<div className="close-menu" />
|
||||
</MenuItem>
|
||||
</div>
|
||||
{menuItems.map(a => (
|
||||
{menuItems
|
||||
.filter(a => a.show)
|
||||
.map(a => (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
navigate(a.path);
|
||||
|
@ -16,6 +16,7 @@
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(90deg, #60a5fa 0%, #a78bfa 100%);
|
||||
}
|
||||
|
||||
.settings .image-settings {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import "./Profile.css";
|
||||
import Nostrich from "nostrich.webp";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@ -168,7 +167,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
|
||||
{(props.banner ?? true) && (
|
||||
<div
|
||||
style={{
|
||||
backgroundImage: `url(${(banner?.length ?? 0) === 0 ? Nostrich : banner})`,
|
||||
backgroundImage: (banner?.length ?? 0) > 0 ? `url(${banner})` : undefined,
|
||||
}}
|
||||
className="banner">
|
||||
<AsyncButton type="button" onClick={() => setNewBanner()}>
|
||||
@ -178,7 +177,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
|
||||
)}
|
||||
{(props.avatar ?? true) && (
|
||||
<div className="avatar-stack">
|
||||
<Avatar user={user} image={picture} />
|
||||
<Avatar pubkey={id} user={user} image={picture} />
|
||||
<AsyncButton type="button" className="btn-rnd" onClick={() => setNewAvatar()}>
|
||||
<Icon name="upload-01" />
|
||||
</AsyncButton>
|
||||
|
@ -505,3 +505,7 @@ export function kvToObject<T>(o: string, sep?: string) {
|
||||
})
|
||||
) as T;
|
||||
}
|
||||
|
||||
export function defaultAvatar(input: string) {
|
||||
return `https://robohash.v0l.io/${input}.png`;
|
||||
}
|
||||
|
@ -123,6 +123,10 @@ code {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
border: 1px solid var(--gray-superdark);
|
||||
}
|
||||
}
|
||||
|
||||
.p {
|
||||
@ -161,9 +165,9 @@ button {
|
||||
cursor: pointer;
|
||||
padding: 6px 12px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
color: var(--bg-color);
|
||||
font-size: var(--font-size);
|
||||
background-color: var(--highlight);
|
||||
background-color: var(--font-color);
|
||||
border: none;
|
||||
border-radius: 16px;
|
||||
outline: none;
|
||||
@ -194,8 +198,7 @@ button:disabled:hover {
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: var(--font-color);
|
||||
color: var(--bg-color);
|
||||
background-color: var(--gray-light);
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
@ -595,10 +598,6 @@ small.xs {
|
||||
color: var(--highlight);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
border: 1px solid var(--gray-superdark);
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user