dev-hooks for components

This commit is contained in:
Bojan Mojsilovic 2023-09-01 16:53:22 +02:00
parent a8c46af0e2
commit f5eb6047d5
70 changed files with 485 additions and 366 deletions

View File

@ -1,7 +1,7 @@
import { Component, createResource, lazy } from 'solid-js';
import { Routes, Route, Navigate, RouteDataFuncArgs } from "@solidjs/router";
import { PrimalWindow } from './types/primal';
import { ComponentLog, PrimalWindow } from './types/primal';
import { fetchKnownProfiles } from './lib/profile';
import { useHomeContext } from './contexts/HomeContext';
@ -41,6 +41,8 @@ const Menu = lazy(() => import('./pages/Settings/Menu'));
const primalWindow = window as PrimalWindow;
const isDev = localStorage.getItem('devMode') === 'true';
const Router: Component = () => {
const account = useAccountContext();
@ -54,7 +56,7 @@ const Router: Component = () => {
const notifications = useNotificationsContext();
const search = useSearchContext();
const loadPrimalStores = () => {
if (isDev) {
primalWindow.primal = {
account,
explore,
@ -67,9 +69,10 @@ const Router: Component = () => {
settings,
thread,
};
};
primalWindow.loadPrimalStores = loadPrimalStores;
primalWindow.onPrimalComponentMount = (data: ComponentLog) => {};
primalWindow.onPrimalComponentCleanup = (data: ComponentLog) => {};
}
const getKnownProfiles = ({ params }: RouteDataFuncArgs) => {
const [profiles] = createResource(params.vanityName, fetchKnownProfiles)

View File

@ -1,17 +1,18 @@
import { Component, createMemo, createSignal, Show } from 'solid-js';
import defaultAvatar from '../../assets/icons/default_nostrich.svg';
import { useMediaContext } from '../../contexts/MediaContext';
import { getMediaUrl } from '../../lib/media';
import { hookForDev } from '../../lib/devTools';
import { MediaSize, PrimalUser } from '../../types/primal';
import VerificationCheck from '../VerificationCheck/VerificationCheck';
import styles from './Avatar.module.scss';
const Avatar: Component<{
src: string | undefined,
src?: string | undefined,
size?: "xxs" | "xss" | "xs" | "vs" | "sm" | "md" | "lg" | "xl" | "xxl",
user?: PrimalUser,
highlightBorder?: boolean,
id?: string,
}> = (props) => {
const media = useMediaContext();
@ -78,15 +79,21 @@ const Avatar: Component<{
break;
};
const url = media?.actions.getMediaUrl(props.src, size, true);
const src = props.user?.picture || props.src;
if (!src) {
return defaultAvatar;
}
const url = media?.actions.getMediaUrl(src, size, true);
setIsCached(!!url);
return url ?? props.src;
return url ?? src;
});
const notCachedFlag = () => {
const dev = JSON.parse(localStorage.getItem('devMode') || 'false');
const dev = localStorage.getItem('devMode') === 'true';
// @ts-ignore
if (isCached() || !dev) {
@ -97,7 +104,11 @@ const Avatar: Component<{
}
return (
<div class={`${avatarClass[selectedSize]} ${highlightClass()}`}>
<div
id={props.id}
class={`${avatarClass[selectedSize]} ${highlightClass()}`}
data-user={props.user?.pubkey}
>
<Show
when={imageSrc()}
fallback={
@ -119,4 +130,4 @@ const Avatar: Component<{
)
}
export default Avatar;
export default hookForDev(Avatar);

View File

@ -1,9 +1,10 @@
import { Component, JSXElement, Match, Show, Switch } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './Checkbox.module.scss';
const Checkbox: Component<{
id: string,
id?: string,
onChange: (e: Event) => void,
checked?: boolean,
label?: string,
@ -13,9 +14,11 @@ const Checkbox: Component<{
}> = (props) => {
return (
<div class={styles.checkbox}>
<div
id={props.id}
class={styles.checkbox}
>
<input
id={props.id}
type='checkbox'
checked={props.checked}
onChange={props.onChange}
@ -36,4 +39,4 @@ const Checkbox: Component<{
)
}
export default Checkbox;
export default hookForDev(Checkbox);

View File

@ -13,8 +13,10 @@ import { useToastContext } from '../Toaster/Toaster';
import { confirmDefaults as t } from '../../translations';
import styles from './ConfirmModal.module.scss';
import { hookForDev } from '../../lib/devTools';
const ConfirmModal: Component<{
id?: string,
open?: boolean,
title?: string,
description?: string,
@ -28,7 +30,7 @@ const ConfirmModal: Component<{
return (
<Modal open={props.open}>
<div class={styles.feedsRestoreModal}>
<div id={props.id} class={styles.feedsRestoreModal}>
<div class={styles.feedConfirmationTitle}>
{props.title || intl.formatMessage(t.title)}
</div>
@ -60,4 +62,4 @@ const ConfirmModal: Component<{
);
}
export default ConfirmModal;
export default hookForDev(ConfirmModal);

View File

@ -2,6 +2,7 @@ import { useIntl } from '@cookbook/solid-intl';
import { Component, createEffect, createSignal, For } from 'solid-js';
import { useAccountContext } from '../../contexts/AccountContext';
import { useSettingsContext } from '../../contexts/SettingsContext';
import { hookForDev } from '../../lib/devTools';
import { zapNote } from '../../lib/zap';
import { userName } from '../../stores/profile';
import { toastZapFail, zapCustomOption } from '../../translations';
@ -13,6 +14,7 @@ import { useToastContext } from '../Toaster/Toaster';
import styles from './CustomZap.module.scss';
const CustomZap: Component<{
id?: string,
open?: boolean,
note: PrimalNote,
onConfirm: (amount?: number) => void,
@ -96,7 +98,7 @@ const CustomZap: Component<{
return (
<Modal open={props.open}>
<div class={styles.customZap}>
<div id={props.id} class={styles.customZap}>
<div class={styles.header}>
<div class={styles.title}>
<div class={styles.zapIcon}></div>
@ -153,4 +155,4 @@ const CustomZap: Component<{
);
}
export default CustomZap;
export default hookForDev(CustomZap);

View File

@ -133,7 +133,7 @@ const EmbeddedNote: Component<{ note: PrimalNote, mentionedUsers?: Record<string
<>
<div class={styles.mentionedNoteHeader}>
<Avatar
src={props.note.user.picture}
user={props.note.user}
size="xxs"
/>
<span class={styles.postInfo}>

View File

@ -1,6 +1,7 @@
import { useIntl } from '@cookbook/solid-intl';
import { A } from '@solidjs/router';
import type { Component } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import { scopeDescriptors, timeframeDescriptors } from '../../translations';
import { ScopeDescriptor } from '../../types/primal';
@ -33,7 +34,7 @@ const timeframeIcons: Record<string, string> = {
latest: styles.clockIcon,
}
const ExploreMenuItem: Component<{ scope: string, stat: number }> = (props) => {
const ExploreMenuItem: Component<{ scope: string, stat: number, id?: string }> = (props) => {
const intl = useIntl();
@ -52,7 +53,7 @@ const ExploreMenuItem: Component<{ scope: string, stat: number }> = (props) => {
}
return (
<div class={styles.exploreMenuItem}>
<div id={props.id} class={styles.exploreMenuItem}>
<div class={styles.itemInfo}>
<div class={item().icon} ></div>
@ -82,4 +83,4 @@ const ExploreMenuItem: Component<{ scope: string, stat: number }> = (props) => {
)
}
export default ExploreMenuItem;
export default hookForDev(ExploreMenuItem);

View File

@ -16,8 +16,9 @@ import { getTrendingUsers } from '../../lib/profile';
import { hexToNpub } from '../../lib/keys';
import { exploreSidebarCaption } from '../../translations';
import { useAccountContext } from '../../contexts/AccountContext';
import { hookForDev } from '../../lib/devTools';
const ExploreSidebar: Component = () => {
const ExploreSidebar: Component<{ id?: string }> = (props) => {
const intl = useIntl();
const account = useAccountContext();
@ -119,7 +120,7 @@ const ExploreSidebar: Component = () => {
// RENDER ---------------------------------------
return (
<>
<div id={props.id}>
<div class={styles.trendingUsersCaption}>
{intl.formatMessage(exploreSidebarCaption)}
</div>
@ -132,15 +133,15 @@ const ExploreSidebar: Component = () => {
class={styles.user}
title={authorName(user)}
>
<Avatar src={user.picture} size="vs" />
<Avatar user={user} size="vs" />
<div class={styles.name}>{authorName(user)}</div>
</A>
)
}
</For>
</div>
</>
</div>
)
}
export default ExploreSidebar;
export default hookForDev(ExploreSidebar);

View File

@ -1,5 +1,6 @@
import { Component, Show } from 'solid-js';
import { useSettingsContext } from '../../contexts/SettingsContext';
import { hookForDev } from '../../lib/devTools';
import styles from './ExternalLink.module.scss';
@ -8,12 +9,13 @@ const ExternalLink: Component<{
darkIcon: string,
label: string,
href: string,
id?: string,
}> = (props) => {
const settings = useSettingsContext();
return (
<div class={styles.externalLink}>
<div id={props.id} class={styles.externalLink}>
<Show
when={['sunset', 'midnight'].includes(settings?.theme || 'sunset')}
fallback={
@ -32,4 +34,4 @@ const ExternalLink: Component<{
);
}
export default ExternalLink;
export default hookForDev(ExternalLink);

View File

@ -1,10 +1,11 @@
import { Component } from 'solid-js';
import { useHomeContext } from '../../contexts/HomeContext';
import { useSettingsContext } from '../../contexts/SettingsContext';
import { hookForDev } from '../../lib/devTools';
import { FeedOption } from '../../types/primal';
import SelectBox from '../SelectBox/SelectBox';
const FeedSelect: Component<{ isPhone?: boolean}> = (props) => {
const FeedSelect: Component<{ isPhone?: boolean, id?: string}> = (props) => {
const home = useHomeContext();
const settings = useSettingsContext();
@ -88,4 +89,4 @@ const FeedSelect: Component<{ isPhone?: boolean}> = (props) => {
);
}
export default FeedSelect;
export default hookForDev(FeedSelect);

View File

@ -1,12 +1,13 @@
import { Component, createEffect, createSignal, For, Show } from 'solid-js';
import { useAccountContext } from '../../contexts/AccountContext';
import { useSettingsContext } from '../../contexts/SettingsContext';
import { hookForDev } from '../../lib/devTools';
import { PrimalFeed } from '../../types/primal';
import styles from './FeedSorter.module.scss';
const FeedSorter: Component = () => {
const FeedSorter: Component<{ id?: string }> = (props) => {
let sorter: any;
@ -109,7 +110,7 @@ const FeedSorter: Component = () => {
});
return (
<div id="feed_sorter" class={styles.feedSorter} ref={sorter}>
<div id={props.id} class={styles.feedSorter} ref={sorter}>
<Show when={availableFeeds().length > 0}>
<For each={availableFeeds()}>
{(feed, index) => (
@ -176,4 +177,4 @@ const FeedSorter: Component = () => {
)
}
export default FeedSorter;
export default hookForDev(FeedSorter);

View File

@ -1,8 +1,10 @@
import { Component } from "solid-js";
import { useAccountContext } from "../../contexts/AccountContext";
import { hookForDev } from "../../lib/devTools";
import styles from "./FloatingNewPostButton.module.scss";
export default function FloatingNewPostButton() {
const FloatingNewPostButton: Component<{ id?: string }> = (props) => {
const account = useAccountContext();
const showNewNoteForm = () => {
@ -12,6 +14,7 @@ export default function FloatingNewPostButton() {
return (
<button
id={props.id}
class={styles.newPostButton}
onClick={showNewNoteForm}
>
@ -19,3 +22,5 @@ export default function FloatingNewPostButton() {
</button>
)
}
export default hookForDev(FloatingNewPostButton);

View File

@ -1,6 +1,7 @@
import { useIntl } from '@cookbook/solid-intl';
import { Component, Show } from 'solid-js';
import { useAccountContext } from '../../contexts/AccountContext';
import { hookForDev } from '../../lib/devTools';
import { account as t } from '../../translations';
import { PrimalUser } from '../../types/primal';
import { useToastContext } from '../Toaster/Toaster';
@ -8,7 +9,7 @@ import { useToastContext } from '../Toaster/Toaster';
import styles from './FollowButton.module.scss';
const FollowButton: Component<{ person: PrimalUser | undefined, large?: boolean }> = (props) => {
const FollowButton: Component<{ person: PrimalUser | undefined, large?: boolean, id?: string }> = (props) => {
const toast = useToastContext()
const account = useAccountContext();
@ -40,7 +41,7 @@ const FollowButton: Component<{ person: PrimalUser | undefined, large?: boolean
return (
<Show when={props.person}>
<div class={klass()}>
<div id={props.id} class={klass()}>
<button onClick={onFollow} >
<Show
when={isFollowed()}
@ -54,4 +55,4 @@ const FollowButton: Component<{ person: PrimalUser | undefined, large?: boolean
)
}
export default FollowButton;
export default hookForDev(FollowButton);

View File

@ -1,11 +1,12 @@
import { Component, JSXElement } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './HelpTip.module.scss';
const HelpTip: Component<{ children?: JSXElement, zIndex?: number }> = (props) => {
const HelpTip: Component<{ children?: JSXElement, zIndex?: number, id?: string }> = (props) => {
return (
<div class={styles.helpContent} style={props.zIndex ? `z-index: ${props.zIndex}` : ''}>
<div id={props.id} class={styles.helpContent} style={props.zIndex ? `z-index: ${props.zIndex}` : ''}>
<div class={styles.helpIcon}></div>
<div class={styles.content}>
{props.children}
@ -14,4 +15,4 @@ const HelpTip: Component<{ children?: JSXElement, zIndex?: number }> = (props) =
);
}
export default HelpTip;
export default hookForDev(HelpTip);

View File

@ -9,8 +9,9 @@ import { useHomeContext } from '../../contexts/HomeContext';
import { useIntl } from '@cookbook/solid-intl';
import { useSettingsContext } from '../../contexts/SettingsContext';
import { placeholders as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const HomeHeader: Component = () => {
const HomeHeader: Component< { id?: string} > = (props) => {
const account = useAccountContext();
const home = useHomeContext();
@ -73,7 +74,7 @@ const HomeHeader: Component = () => {
const activeUser = () => account?.activeUser;
return (
<div class={styles.fullHeader}>
<div id={props.id} class={styles.fullHeader}>
<Show
when={account?.hasPublicKey()}
fallback={<div class={styles.welcomeMessage}>
@ -82,7 +83,7 @@ const HomeHeader: Component = () => {
>
<button class={styles.callToAction} onClick={onShowNewNoteinput}>
<Avatar
src={activeUser()?.picture}
user={activeUser()}
size="lg"
/>
@ -128,4 +129,4 @@ const HomeHeader: Component = () => {
);
}
export default HomeHeader;
export default hookForDev(HomeHeader);

View File

@ -4,17 +4,18 @@ import styles from './HomeHeaderPhone.module.scss';
import FeedSelect from '../FeedSelect/FeedSelect';
import Branding from '../Branding/Branding';
import { useHomeContext } from '../../contexts/HomeContext';
import { hookForDev } from '../../lib/devTools';
const HomeHeaderPhone: Component = () => {
const HomeHeaderPhone: Component< { id?: string } > = (props) => {
const home = useHomeContext();
let lastScrollTop = document.body.scrollTop || document.documentElement.scrollTop;
let smallHeader: HTMLDivElement | undefined;
let border: HTMLDivElement | undefined;
const onScroll = () => {
const scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
const smallHeader = document.getElementById('phone_header');
const border = document.getElementById('small_bottom_border');
home?.actions?.updateScrollTop(scrollTop);
@ -58,7 +59,7 @@ const HomeHeaderPhone: Component = () => {
});
return (
<div id="phone_header" class={styles.fullHeader}>
<div id={props.id} class={styles.fullHeader} ref={smallHeader}>
<div class={styles.phoneHeader}>
<div class={styles.logo}>
<Branding small={true} />
@ -68,7 +69,7 @@ const HomeHeaderPhone: Component = () => {
</Show>
</div>
<div
id="small_bottom_border"
ref={border}
class={styles.smallHeaderBottomBorder}
>
<div class={styles.leftCorner}></div>
@ -78,4 +79,4 @@ const HomeHeaderPhone: Component = () => {
);
}
export default HomeHeaderPhone;
export default hookForDev(HomeHeaderPhone);

View File

@ -27,6 +27,7 @@ import { hourNarrow } from '../../formats';
import { home as t } from '../../translations';
import { useAccountContext } from '../../contexts/AccountContext';
import { hookForDev } from '../../lib/devTools';
const [init, setInit] = createSignal(false);
@ -47,7 +48,7 @@ const [data, setData] = createStore<Record<string, FeedPage & { notes: PrimalNot
},
});
const HomeSidebar: Component = () => {
const HomeSidebar: Component< { id?: string } > = (props) => {
const intl = useIntl();
const account = useAccountContext();
@ -157,7 +158,7 @@ const HomeSidebar: Component = () => {
};
return (
<div>
<div id={props.id}>
<div class={styles.headingTrending}>
<div>
<div class={styles.flameIcon}></div>
@ -199,4 +200,4 @@ const HomeSidebar: Component = () => {
);
}
export default HomeSidebar;
export default hookForDev(HomeSidebar);

View File

@ -1,13 +1,15 @@
import { Component, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './LinkPreview.module.scss';
const LinkPreview: Component<{ preview: any }> = (props) => {
const LinkPreview: Component<{ preview: any, id?: string }> = (props) => {
const encodedUrl = encodeURI(new URL(props.preview.url).origin);
return (
<a
id={props.id}
href={props.preview.url}
class={styles.linkPreview}
>
@ -40,4 +42,4 @@ const LinkPreview: Component<{ preview: any }> = (props) => {
);
}
export default LinkPreview;
export default hookForDev(LinkPreview);

View File

@ -1,12 +1,13 @@
import { Component } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './Loader.module.scss';
const Loader: Component = () => {
const Loader: Component< { id?: string } > = (props) => {
return (
<div class={styles.loader}><span></span></div>
<div id={props.id} class={styles.loader}><span></span></div>
);
}
export default Loader;
export default hookForDev(Loader);

View File

@ -1,5 +1,6 @@
import { useIntl } from '@cookbook/solid-intl';
import { Component, JSXElement, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import { placeholders as t } from '../../translations';
import Branding from '../Branding/Branding';
import Search from '../Search/Search';
@ -7,12 +8,12 @@ import Wormhole from '../Wormhole/Wormhole';
import styles from './MissingPage.module.scss';
const MissingPage: Component<{ title: string, children?: JSXElement }> = (props) => {
const MissingPage: Component<{ title: string, children?: JSXElement, id?: string }> = (props) => {
const intl = useIntl();
return (
<>
<div id={props.id}>
<Wormhole to="branding_holder" >
<Branding small={false} />
</Wormhole>
@ -44,8 +45,8 @@ const MissingPage: Component<{ title: string, children?: JSXElement }> = (props)
{props.children}
</div>
</Show>
</>
</div>
)
}
export default MissingPage;
export default hookForDev(MissingPage);

View File

@ -1,14 +1,15 @@
import { Component, JSXElement, Show } from 'solid-js';
import { Portal } from 'solid-js/web';
import { hookForDev } from '../../lib/devTools';
import styles from './Modal.module.scss';
const Modal: Component<{ children: JSXElement, open?: boolean}> = (props) => {
const Modal: Component<{ children: JSXElement, open?: boolean, id?: string}> = (props) => {
return (
<Show when={props.open}>
<Portal mount={document.getElementById("modal") as Node}>
<div class={styles.modal}>
<div id={props.id} class={styles.modal}>
{props.children}
</div>
</Portal>
@ -16,4 +17,4 @@ const Modal: Component<{ children: JSXElement, open?: boolean}> = (props) => {
);
}
export default Modal;
export default hookForDev(Modal);

View File

@ -1,9 +1,11 @@
import { A, useLocation, useNavigate } from '@solidjs/router';
import { Component, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './NavLink.module.scss';
const NavLink: Component<{
id?: string,
to: string,
label: string,
icon: string,
@ -40,7 +42,7 @@ const NavLink: Component<{
}
return (
<button class={`${styles.navLink} ${props.hiddenOnSmallScreens ? styles.hiddenOnSmallScreens : ''}`} onClick={onClick}>
<button id={props.id} class={`${styles.navLink} ${props.hiddenOnSmallScreens ? styles.hiddenOnSmallScreens : ''}`} onClick={onClick}>
<Show when={props.bubble && props.bubble() > 0}>
<div class={`${styles.bubble} ${bubbleClass()}`}>
<div>{props.bubble && props.bubble() < 100 ? props.bubble() : '99+'}</div>
@ -54,4 +56,4 @@ const NavLink: Component<{
)
}
export default NavLink;
export default hookForDev(NavLink);

View File

@ -9,8 +9,9 @@ import NavLink from '../NavLink/NavLink';
import FloatingNewPostButton from '../FloatingNewPostButton/FloatingNewPostButton';
import styles from './NavMenu.module.scss';
import { hookForDev } from '../../lib/devTools';
const NavMenu: Component = () => {
const NavMenu: Component< { id?: string } > = (props) => {
const account = useAccountContext();
const notifications = useNotificationsContext();
const messages = useMessagesContext();
@ -62,7 +63,7 @@ const NavMenu: Component = () => {
];
return (
<div class={styles.navMenu}>
<div id={props.id} class={styles.navMenu}>
<nav class={styles.sideNav}>
<For each={links}>
{({ to, label, icon, bubble, hiddenOnSmallScreens }) => {
@ -86,4 +87,4 @@ const NavMenu: Component = () => {
)
}
export default NavMenu;
export default hookForDev(NavMenu);

View File

@ -35,11 +35,13 @@ import {
actions as tActions,
} from "../../../translations";
import { useMediaContext } from "../../../contexts/MediaContext";
import { hookForDev } from "../../../lib/devTools";
type AutoSizedTextArea = HTMLTextAreaElement & { _baseScrollHeight: number };
const EditBox: Component<{
id?: string,
replyToNote?: PrimalNote,
onClose?: () => void,
onSuccess?: (note: SendNoteResult) => void,
@ -1085,6 +1087,7 @@ const EditBox: Component<{
return (
<div
id={props.id}
class={styles.noteEditBox}
ref={editWrap}
onDrop={onDrop}
@ -1147,7 +1150,7 @@ const EditBox: Component<{
<SearchOption
title={userName(user)}
description={nip05Verification(user)}
icon={<Avatar src={user.picture} size="xs" />}
icon={<Avatar user={user} size="xs" />}
statNumber={search?.scores[user.pubkey]}
statLabel={intl.formatMessage(tSearch.followers)}
onClick={() => selectUser(user)}
@ -1210,4 +1213,4 @@ const EditBox: Component<{
)
}
export default EditBox;
export default hookForDev(EditBox);

View File

@ -18,7 +18,6 @@ const NewNote: Component<{ onSuccess: (note: SendNoteResult) => void}> = (props)
<div class={styles.newNote}>
<div class={styles.leftSide}>
<Avatar
src={activeUser()?.picture}
size="md"
user={activeUser()}
/>

View File

@ -3,22 +3,24 @@ import { Component } from "solid-js";
import { PrimalNetStats } from "../../types/primal";
import styles from "./NostrStats.module.scss";
import { explore as t } from '../../translations';
import { hookForDev } from "../../lib/devTools";
const NostrStats: Component<{ stats: PrimalNetStats }> = (props) => {
const NostrStats: Component<{ stats: PrimalNetStats, id?: string }> = (props) => {
const intl = useIntl();
const statDisplay = (
const statDisplay = (opts:{
stat: number | string | undefined,
key: string,
) => {
id?: string,
}) => {
// @ts-ignore Record find entry by key
const label = t.statDisplay[key] || '';
const label = t.statDisplay[opts.key] || '';
return (
<div class={styles.netstat}>
<div id={opts.id} class={styles.netstat}>
<div class={styles.number}>
{stat?.toLocaleString()}
{opts.stat?.toLocaleString()}
</div>
<div class={styles.label}>
{intl.formatMessage(label)}
@ -29,17 +31,17 @@ const NostrStats: Component<{ stats: PrimalNetStats }> = (props) => {
return (
<div class={styles.netstats}>
{statDisplay(props.stats.users, 'users')}
{statDisplay(props.stats.pubkeys, 'pubkeys')}
{statDisplay(props.stats.zaps, 'zaps')}
{statDisplay((props.stats.satszapped /100000000).toFixed(8), 'btcZapped')}
{statDisplay(props.stats.pubnotes, 'pubnotes')}
{statDisplay(props.stats.reposts, 'reposts')}
{statDisplay(props.stats.reactions, 'reactions')}
{statDisplay(props.stats.any, 'any')}
<div id={props.id} class={styles.netstats}>
{hookForDev(statDisplay)({ stat: props.stats.users, key: 'users' })}
{hookForDev(statDisplay)({ stat: props.stats.pubkeys, key: 'pubkeys' })}
{hookForDev(statDisplay)({ stat: props.stats.zaps, key: 'zaps' })}
{hookForDev(statDisplay)({ stat: (props.stats.satszapped /100000000).toFixed(8), key: 'btcZapped' })}
{hookForDev(statDisplay)({ stat: props.stats.pubnotes, key: 'pubnotes' })}
{hookForDev(statDisplay)({ stat: props.stats.reposts, key: 'reposts' })}
{hookForDev(statDisplay)({ stat: props.stats.reactions, key: 'reactions' })}
{hookForDev(statDisplay)({ stat: props.stats.any, key: 'any' })}
</div>
)
}
export default NostrStats;
export default hookForDev(NostrStats);

View File

@ -1,19 +1,20 @@
import { A } from "@solidjs/router";
import { Component, JSXElement } from "solid-js";
import { userName, nip05Verification } from "../../../stores/profile";
import { hookForDev } from "../../../lib/devTools";
import { userName } from "../../../stores/profile";
import { PrimalUser } from "../../../types/primal";
import Avatar from "../../Avatar/Avatar";
import styles from "./MentionedUserLink.module.scss";
const MentionedUserLink: Component<{
user: PrimalUser,
openInNewTab?: boolean,
id?: string,
}> = (props) => {
const LinkComponent: Component<{ children: JSXElement }> = (p) => {
return props.openInNewTab ?
<a class={styles.userMention} href={`/p/${props.user.npub}`} target="_blank">{p.children}</a> :
<A class={styles.userMention} href={`/p/${props.user.npub}`}>{p.children}</A>;
<a id={props.id} class={styles.userMention} href={`/p/${props.user.npub}`} target="_blank">{p.children}</a> :
<A id={props.id} class={styles.userMention} href={`/p/${props.user.npub}`}>{p.children}</A>;
};
return (
@ -23,4 +24,4 @@ const MentionedUserLink: Component<{
);
}
export default MentionedUserLink;
export default hookForDev(MentionedUserLink);

View File

@ -10,8 +10,9 @@ import { useThreadContext } from '../../contexts/ThreadContext';
import { useIntl } from '@cookbook/solid-intl';
import { truncateNpub } from '../../stores/profile';
import { note as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const Note: Component<{ note: PrimalNote }> = (props) => {
const Note: Component<{ note: PrimalNote, id?: string }> = (props) => {
const threadContext = useThreadContext();
const intl = useIntl();
@ -38,6 +39,7 @@ const Note: Component<{ note: PrimalNote }> = (props) => {
return (
<A
id={props.id}
class={styles.postLink}
href={`/e/${props.note?.post.noteId}`}
onClick={() => navToThread(props.note)}
@ -73,4 +75,4 @@ const Note: Component<{ note: PrimalNote }> = (props) => {
)
}
export default Note;
export default hookForDev(Note);

View File

@ -17,8 +17,9 @@ import zapMD from '../../../assets/lottie/zap_md.json';
import { medZapLimit } from '../../../constants';
import { toast as t } from '../../../translations';
import PrimalMenu from '../../PrimalMenu/PrimalMenu';
import { hookForDev } from '../../../lib/devTools';
const NoteFooter: Component<{ note: PrimalNote, doCustomZap?: boolean }> = (props) => {
const NoteFooter: Component<{ note: PrimalNote, doCustomZap?: boolean, id?: string }> = (props) => {
const account = useAccountContext();
const toast = useToastContext();
@ -360,7 +361,7 @@ const NoteFooter: Component<{ note: PrimalNote, doCustomZap?: boolean }> = (prop
const [hideZapIcon, setHideZapIcon] = createSignal(false);
return (
<div class={styles.footer} ref={footerDiv}>
<div id={props.id} class={styles.footer} ref={footerDiv}>
<Show when={showSmallZapAnim()}>
<lottie-player
id={`note-small-zap-${props.note.post.id}`}
@ -472,4 +473,4 @@ const NoteFooter: Component<{ note: PrimalNote, doCustomZap?: boolean }> = (prop
)
}
export default NoteFooter;
export default hookForDev(NoteFooter);

View File

@ -18,8 +18,9 @@ import { reportUser } from '../../../lib/profile';
import { APP_ID } from '../../../App';
import ConfirmModal from '../../ConfirmModal/ConfirmModal';
import { hexToNpub } from '../../../lib/keys';
import { hookForDev } from '../../../lib/devTools';
const NoteHeader: Component<{ note: PrimalNote, openCustomZap?: () => void}> = (props) => {
const NoteHeader: Component<{ note: PrimalNote, openCustomZap?: () => void, id?: string }> = (props) => {
const intl = useIntl();
const toaster = useToastContext();
@ -191,7 +192,7 @@ const NoteHeader: Component<{ note: PrimalNote, openCustomZap?: () => void}> = (
noteContextForEveryone;
return (
<div class={styles.header}>
<div id={props.id} class={styles.header}>
<div class={styles.headerInfo}>
<div
class={styles.avatar}
@ -201,7 +202,7 @@ const NoteHeader: Component<{ note: PrimalNote, openCustomZap?: () => void}> = (
href={`/p/${props.note.user.npub}`}
>
<Avatar
src={props.note?.user?.picture}
user={props.note?.user}
size="sm"
highlightBorder={isVerifiedByPrimal()}
/>
@ -275,4 +276,4 @@ const NoteHeader: Component<{ note: PrimalNote, openCustomZap?: () => void}> = (
)
}
export default NoteHeader;
export default hookForDev(NoteHeader);

View File

@ -1,5 +1,5 @@
import { Component } from 'solid-js';
import { truncateNpub } from '../../../stores/profile';
import { hookForDev } from '../../../lib/devTools';
import { PrimalNote } from '../../../types/primal';
import ParsedNote from '../../ParsedNote/ParsedNote';
import NoteFooter from '../NoteFooter/NoteFooter';
@ -8,26 +8,27 @@ import NoteHeader from '../NoteHeader/NoteHeader';
import styles from './NotePrimary.module.scss';
const NotePrimary: Component<{ note: PrimalNote }> = (props) => {
const NotePrimary: Component<{ note: PrimalNote, id?: string }> = (props) => {
return (
<div
class={styles.post}
data-event={props.note.post.id}
data-event-bech32={props.note.post.noteId}
>
<div class={styles.border}></div>
<NoteHeader note={props.note} />
<div class={styles.content}>
<div
id={props.id}
class={styles.post}
data-event={props.note.post.id}
data-event-bech32={props.note.post.noteId}
>
<div class={styles.border}></div>
<NoteHeader note={props.note} />
<div class={styles.content}>
<div class={styles.message}>
<ParsedNote note={props.note} />
</div>
<NoteFooter note={props.note}/>
<div class={styles.message}>
<ParsedNote note={props.note} />
</div>
<NoteFooter note={props.note}/>
</div>
</div>
)
}
export default NotePrimary;
export default hookForDev(NotePrimary);

View File

@ -6,8 +6,9 @@ import NoteFooter from '../NoteFooter/NoteFooter';
import styles from './NotificationNote.module.scss';
import { useThreadContext } from '../../../contexts/ThreadContext';
import { hookForDev } from '../../../lib/devTools';
const Note: Component<{ note: PrimalNote }> = (props) => {
const NotificationNote: Component<{ note: PrimalNote, id?: string }> = (props) => {
const threadContext = useThreadContext();
@ -17,6 +18,7 @@ const Note: Component<{ note: PrimalNote }> = (props) => {
return (
<A
id={props.id}
class={styles.postLink}
href={`/e/${props.note?.post.noteId}`}
onClick={() => navToThread(props.note)}
@ -38,4 +40,4 @@ const Note: Component<{ note: PrimalNote }> = (props) => {
)
}
export default Note;
export default hookForDev(NotificationNote);

View File

@ -1,12 +1,14 @@
import { Component, Show } from 'solid-js';
import defaultAvatar from '../../assets/icons/default_nostrich.svg';
import { hookForDev } from '../../lib/devTools';
import styles from './NotificationAvatar.module.scss';
const NotificationAvatar: Component<{
number: number | undefined,
size?: "xxs" | "xs" | "vs" | "sm" | "md" | "lg" | "xl" | "xxl",
verified?: string
verified?: string,
id?: string,
}> = (props) => {
const selectedSize = props.size || 'sm';
@ -41,7 +43,7 @@ const NotificationAvatar: Component<{
}
return (
<div class={avatarClass[selectedSize]}>
<div id={props.id} class={avatarClass[selectedSize]}>
<Show
when={props.number}
fallback={
@ -59,4 +61,4 @@ const NotificationAvatar: Component<{
)
}
export default NotificationAvatar;
export default hookForDev(NotificationAvatar);

View File

@ -32,6 +32,7 @@ import mentionedPostReplied from '../../assets/icons/notifications/mentioned_pos
import NotificationNote from '../Note/NotificationNote/NotificationNote';
import NotificationAvatar from '../NotificationAvatar/NotificationAvatar';
import { notificationsNew as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const typeIcons: Record<string, string> = {
[NotificationType.NEW_USER_FOLLOWED_YOU]: userFollow,
@ -58,6 +59,7 @@ const typeIcons: Record<string, string> = {
}
type NotificationItemProps = {
id?: string,
type: NotificationType,
users?: PrimalNotifUser[],
note?: PrimalNote,
@ -141,7 +143,7 @@ const NotificationItem: Component<NotificationItemProps> = (props) => {
});
return (
<div class={styles.notifItem}>
<div id={props.id} class={styles.notifItem}>
<div class={styles.notifType}>
<img src={typeIcon()} alt="notification icon" />
<div class={styles.iconInfo} title={props.iconTooltip}>
@ -157,7 +159,7 @@ const NotificationItem: Component<NotificationItemProps> = (props) => {
href={`/p/${user.npub}`} class={styles.avatar}
title={userName(user)}
>
<Avatar src={user.picture} size="xs" />
<Avatar user={user} size="xs" />
</A>
)}
</For>
@ -192,4 +194,4 @@ const NotificationItem: Component<NotificationItemProps> = (props) => {
);
}
export default NotificationItem;
export default hookForDev(NotificationItem);

View File

@ -32,6 +32,7 @@ import mentionedPostReplied from '../../assets/icons/notifications/mentioned_pos
import NotificationNote from '../Note/NotificationNote/NotificationNote';
import { truncateNumber } from '../../lib/notifications';
import { notificationsOld as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const typeIcons: Record<string, string> = {
[NotificationType.NEW_USER_FOLLOWED_YOU]: userFollow,
@ -58,6 +59,7 @@ const typeIcons: Record<string, string> = {
}
type NotificationItemProps = {
id?: string,
notes: PrimalNote[],
users: Record<string, PrimalUser>,
userStats: Record<string, { followers_count: number }>,
@ -114,7 +116,7 @@ const NotificationItem2: Component<NotificationItemProps> = (props) => {
return (
<div class={styles.notifItem}>
<div id={props.id} class={styles.notifItem}>
<div class={styles.notifType}>
<img src={typeIcon()} alt="notification icon" />
<Show when={isZapType()}>
@ -129,7 +131,7 @@ const NotificationItem2: Component<NotificationItemProps> = (props) => {
href={`/p/${user()?.npub}`} class={styles.avatar}
title={userName(user())}
>
<Avatar src={user()?.picture} size="xs" />
<Avatar user={user()} size="xs" />
</A>
</div>
<div class={styles.description}>
@ -158,4 +160,4 @@ const NotificationItem2: Component<NotificationItemProps> = (props) => {
);
}
export default NotificationItem2;
export default hookForDev(NotificationItem2);

View File

@ -6,6 +6,7 @@ import { PrimalNotification, PrimalNotifUser, SortedNotifications } from '../../
import { notificationsSidebar as t } from '../../translations';
import styles from './NotificationsSidebar.module.scss';
import { hookForDev } from '../../lib/devTools';
const uniqueifyUsers = (users: PrimalNotifUser[]) => {
return users.reduce<PrimalNotifUser[]>((acc, u) => {
@ -15,6 +16,7 @@ const uniqueifyUsers = (users: PrimalNotifUser[]) => {
}
const NotificationsSidebar: Component<{
id?: string,
notifications: SortedNotifications,
getUsers: (notifs: PrimalNotification[], type: NotificationType) => PrimalNotifUser[],
}> = (props) => {
@ -29,8 +31,6 @@ const NotificationsSidebar: Component<{
const lost = props.getUsers(unffolowNotifs, NotificationType.USER_UNFOLLOWED_YOU);
return [uniqueifyUsers(followers).length, uniqueifyUsers(lost).length];
};
const mentions = () => {
@ -158,7 +158,7 @@ const NotificationsSidebar: Component<{
}
return (
<>
<div id={props.id}>
<div class={styles.sidebarHeading}>
{intl.formatMessage(t.heading)}
</div>
@ -368,8 +368,8 @@ const NotificationsSidebar: Component<{
</div>
</div>
</Show>
</>
</div>
)
}
export default NotificationsSidebar;
export default hookForDev(NotificationsSidebar);

View File

@ -1,14 +1,15 @@
import { Component, JSXElement } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './PageCaption.module.scss';
const PageCaption: Component<{ title?: string, children?: JSXElement }> = (props) => {
const PageCaption: Component<{ title?: string, children?: JSXElement, id?: string }> = (props) => {
return (
<div id="central_header" class={styles.fullHeader}>
<div id={props.id} class={styles.fullHeader}>
<div class={styles.logo}></div>
{props.children || props.title || ''}
</div>
)
}
export default PageCaption;
export default hookForDev(PageCaption);

View File

@ -1,8 +1,9 @@
import type { Component } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './PageNav.module.scss';
const PageNav: Component = () => {
const PageNav: Component<{ id?: string }> = (props) => {
const onBack = () => {
window.history.back();
@ -13,13 +14,13 @@ const PageNav: Component = () => {
}
return (
<>
<div id={props.id}>
<button onClick={onBack} class={styles.backIcon}>
</button>
<button onClick={onNext} class={styles.forwardIcon}>
</button>
</>
</div>
)
}
export default PageNav;
export default hookForDev(PageNav);

View File

@ -1,11 +1,14 @@
import { onCleanup, onMount } from "solid-js";
import { Component, onCleanup, onMount } from "solid-js";
import { hookForDev } from "../../lib/devTools";
import styles from "./Paginator.module.scss";
export default function Paginator(props: {
const Paginator: Component<{
id?: string,
loadNextPage: (() => void) | undefined,
isSmall?: boolean,
}) {
}> = (props) => {
let observer: IntersectionObserver | undefined;
let trigger: HTMLDivElement | undefined;
onMount(() => {
observer = new IntersectionObserver(entries => {
@ -28,7 +31,9 @@ export default function Paginator(props: {
});
return (
<div id="pagination_trigger" class={props.isSmall ? styles.smallPaginator : styles.paginator}>
<div id={props.id} ref={trigger} class={props.isSmall ? styles.smallPaginator : styles.paginator}>
</div>
)
}
export default hookForDev(Paginator);

View File

@ -15,6 +15,7 @@ import { nip19 } from 'nostr-tools';
import LinkPreview from '../LinkPreview/LinkPreview';
import MentionedUserLink from '../Note/MentionedUserLink/MentionedUserLink';
import { useMediaContext } from '../../contexts/MediaContext';
import { hookForDev } from '../../lib/devTools';
export const parseNoteLinks = (text: string, note: PrimalNote, highlightOnly = false) => {
@ -98,7 +99,7 @@ export const parseNpubLinks = (text: string, note: PrimalNote, highlightOnly = f
};
const ParsedNote: Component<{ note: PrimalNote, ignoreMentionedNotes?: boolean}> = (props) => {
const ParsedNote: Component<{ note: PrimalNote, ignoreMentionedNotes?: boolean, id?: string }> = (props) => {
const media = useMediaContext();
@ -221,9 +222,9 @@ const ParsedNote: Component<{ note: PrimalNote, ignoreMentionedNotes?: boolean}>
return (
<div innerHTML={displayedContent()}>
<div id={props.id} innerHTML={displayedContent()}>
</div>
);
};
export default ParsedNote;
export default hookForDev(ParsedNote);

View File

@ -1,5 +1,6 @@
import { A } from '@solidjs/router';
import { Component, For, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import { authorName, nip05Verification, truncateNpub } from '../../stores/profile';
import { PrimalUser } from '../../types/primal';
import Avatar from '../Avatar/Avatar';
@ -8,11 +9,11 @@ import FollowButton from '../FollowButton/FollowButton';
import styles from './PeopleList.module.scss';
const PeopleList: Component<{ people: PrimalUser[], label: string}> = (props) => {
const PeopleList: Component<{ people: PrimalUser[], label: string, id?: string }> = (props) => {
const people = () => props.people;
return (
<div id="trending_wrapper" class={styles.stickyWrapper}>
<div id={props.id} class={styles.stickyWrapper}>
<div class={styles.heading}>{props.label}</div>
<div id="trending_section" class={styles.trendingSection}>
<For each={people()}>
@ -21,7 +22,6 @@ const PeopleList: Component<{ people: PrimalUser[], label: string}> = (props) =>
<A href={`/p/${person?.npub}`} class={styles.peopleList}>
<div class={styles.avatar}>
<Avatar
src={person?.picture}
size="md"
user={person}
/>
@ -53,4 +53,4 @@ const PeopleList: Component<{ people: PrimalUser[], label: string}> = (props) =>
);
}
export default PeopleList;
export default hookForDev(PeopleList);

View File

@ -1,11 +1,14 @@
import { Component } from "solid-js";
import { hookForDev } from "../../lib/devTools";
import styles from "./PostButton.module.scss";
export default function PostButton() {
const PostButton: Component< {id?: string } > = (props) => {
const showPostForm = () => {};
return (
<button
id={props.id}
class={styles.postButton}
onClick={showPostForm}
>
@ -13,3 +16,5 @@ export default function PostButton() {
</button>
)
}
export default hookForDev(PostButton);

View File

@ -1,15 +1,16 @@
import { For } from "solid-js";
import { Component, For } from "solid-js";
import { hookForDev } from "../../lib/devTools";
import { MenuItem } from "../../types/primal";
import styles from "./PrimalMenu.module.scss";
import PrimalMenuItem from "./PrimalMenuItem";
export default function PrimalMenu(props: {
const PrimalMenu: Component<{
id: string,
items: MenuItem[],
position?: 'note_footer' | 'profile',
reverse?: boolean
}) {
reverse?: boolean,
}> = (props) => {
const positionClass = () => {
if (props.position == 'note_footer') {
@ -36,3 +37,5 @@ export default function PrimalMenu(props: {
</div>
)
}
export default hookForDev(PrimalMenu);

View File

@ -1,12 +1,14 @@
import { createSignal, onMount, Show } from "solid-js";
import { Component, createSignal, onMount, Show } from "solid-js";
import { hookForDev } from "../../lib/devTools";
import { MenuItem } from "../../types/primal";
import styles from "./PrimalMenuItem.module.scss";
export default function PrimalMenuItem(props: {
const PrimalMenuItem: Component<{
item: MenuItem,
reverse?: boolean,
}) {
id?: string,
}> = (props) => {
const [icon, setIcon] = createSignal<string>()
@ -28,6 +30,7 @@ export default function PrimalMenuItem(props: {
return (
<button
id={props.id}
onClick={(e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
@ -44,3 +47,5 @@ export default function PrimalMenuItem(props: {
</button>
)
}
export default hookForDev(PrimalMenuItem);

View File

@ -9,39 +9,46 @@ import SmallNote from '../SmallNote/SmallNote';
import { useIntl } from '@cookbook/solid-intl';
import { userName } from '../../stores/profile';
import { profile as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const ProfileSidebar: Component<{ notes: PrimalNote[] | undefined, profile: PrimalUser | undefined }> = (props) => {
const ProfileSidebar: Component<{
notes: PrimalNote[] | undefined,
profile: PrimalUser | undefined,
id?: string,
}> = (props) => {
const intl = useIntl();
return (
<Show when={props.profile}>
<div class={styles.headingTrending}>
<div>
{intl.formatMessage(t.sidebarCaption)}
</div>
</div>
<Show
when={props.notes && props.notes.length > 0}
fallback={
<div class={styles.noNotes}>
{intl.formatMessage(
t.sidebarNoNotes,
{
name: userName(props.profile),
},
)}
<div id={props.id}>
<Show when={props.profile}>
<div class={styles.headingTrending}>
<div>
{intl.formatMessage(t.sidebarCaption)}
</div>
}
>
<For each={props.notes}>
{(note) => <SmallNote note={note} />}
</For>
</div>
<Show
when={props.notes && props.notes.length > 0}
fallback={
<div class={styles.noNotes}>
{intl.formatMessage(
t.sidebarNoNotes,
{
name: userName(props.profile),
},
)}
</div>
}
>
<For each={props.notes}>
{(note) => <SmallNote note={note} />}
</For>
</Show>
</Show>
</Show>
</div>
);
}
export default ProfileSidebar;
export default hookForDev(ProfileSidebar);

View File

@ -6,21 +6,21 @@ import { trimVerification } from '../../lib/profile';
import { hexToNpub } from '../../lib/keys';
import styles from './ProfileWidget.module.scss';
import { hookForDev } from '../../lib/devTools';
const ProfileWidget: Component = () => {
const ProfileWidget: Component<{ id?: string }> = (props) => {
const account = useAccountContext()
const activeUser = () => account?.activeUser;
return (
<div>
<div id={props.id}>
<Show when={account?.hasPublicKey()}>
<A href="/profile" class={styles.userProfile}>
<div class={styles.avatar}>
<Avatar
size="vs"
src={activeUser()?.picture}
user={activeUser()}
/>
</div>
@ -37,4 +37,4 @@ const ProfileWidget: Component = () => {
);
}
export default ProfileWidget;
export default hookForDev(ProfileWidget);

View File

@ -1,6 +1,7 @@
import { useIntl } from "@cookbook/solid-intl";
import { Component, createEffect, createSignal, Show } from "solid-js";
import { useAccountContext } from "../../contexts/AccountContext";
import { hookForDev } from "../../lib/devTools";
import { userName } from "../../stores/profile";
import { actions as t } from "../../translations";
import { PrimalNote, SendNoteResult } from "../../types/primal";
@ -8,39 +9,12 @@ import Avatar from "../Avatar/Avatar";
import EditBox from "../NewNote/EditBox/EditBox";
import styles from "./ReplyToNote.module.scss";
type AutoSizedTextArea = HTMLTextAreaElement & { _baseScrollHeight: number };
const getScrollHeight = (elm: AutoSizedTextArea) => {
var savedValue = elm.value
elm.value = ''
elm._baseScrollHeight = elm.scrollHeight
elm.value = savedValue
}
const onExpandableTextareaInput: (event: InputEvent) => void = (event) => {
const maxHeight = document.documentElement.clientHeight || window.innerHeight || 0;
const elm = event.target as AutoSizedTextArea ;
if(elm.nodeName !== 'TEXTAREA' || elm.id !== 'reply_to_note_text_area') {
return;
}
const minRows = parseInt(elm.getAttribute('data-min-rows') || '0');
!elm._baseScrollHeight && getScrollHeight(elm);
if (elm.scrollHeight >= (maxHeight - 70)) {
return;
}
elm.rows = minRows
const rows = Math.ceil((elm.scrollHeight - elm._baseScrollHeight) / 20)
elm.rows = minRows + rows
}
const ReplyToNote: Component<{ note: PrimalNote, onNotePosted?: (note: SendNoteResult) => void }> = (props) => {
const ReplyToNote: Component<{
note: PrimalNote,
onNotePosted?: (note: SendNoteResult) => void,
id?: string,
}> = (props) => {
const intl = useIntl();
@ -81,57 +55,57 @@ const ReplyToNote: Component<{ note: PrimalNote, onNotePosted?: (note: SendNoteR
});
return (
<Show
when={open()}
fallback={
<button class={styles.replyBox} onClick={openReplyBox}>
<div class={styles.leftSideClosed}>
<Avatar
src={activeUser()?.picture}
size="md"
user={activeUser()}
/>
</div>
<div class={styles.rightSideClosed}>
<div class={styles.border}>
<div
class={styles.input}
>
<span>
{intl.formatMessage(
t.noteReply,
{
name: userName(props.note.user),
},
)}
</span>
<div id={props.id}>
<Show
when={open()}
fallback={
<button class={styles.replyBox} onClick={openReplyBox}>
<div class={styles.leftSideClosed}>
<Avatar
size="md"
user={activeUser()}
/>
</div>
<div class={styles.rightSideClosed}>
<div class={styles.border}>
<div
class={styles.input}
>
<span>
{intl.formatMessage(
t.noteReply,
{
name: userName(props.note.user),
},
)}
</span>
</div>
</div>
</div>
</div>
</button>
}
>
<div class={styles.newNoteBorder}>
<div class={styles.newNote}>
<div class={styles.leftSide}>
<Avatar
src={activeUser()?.picture}
size="md"
user={activeUser()}
/>
</div>
<div class={styles.rightSide}>
<EditBox
idPrefix="reply_"
replyToNote={props.note}
onClose={closeReplyToNote}
onSuccess={props.onNotePosted}
/>
</button>
}
>
<div class={styles.newNoteBorder}>
<div class={styles.newNote}>
<div class={styles.leftSide}>
<Avatar
size="md"
user={activeUser()}
/>
</div>
<div class={styles.rightSide}>
<EditBox
idPrefix="reply_"
replyToNote={props.note}
onClose={closeReplyToNote}
onSuccess={props.onNotePosted}
/>
</div>
</div>
</div>
</div>
</Show>
</Show>
</div>
)
}
export default ReplyToNote;
export default hookForDev(ReplyToNote);

View File

@ -12,6 +12,7 @@ import { placeholders, search as t } from '../../translations';
import styles from './Search.module.scss';
import SearchOption from './SearchOption';
import { hookForDev } from '../../lib/devTools';
const Search: Component<{
@ -20,6 +21,7 @@ const Search: Component<{
noLinks?: boolean,
hideDefault?: boolean,
placeholder?: string,
id?: string,
}> = (props) => {
const toaster = useToastContext();
@ -113,7 +115,7 @@ const Search: Component<{
});
return (
<div class={styles.searchHolder}>
<div id={props.id} class={styles.searchHolder}>
<form
class={styles.search}
onsubmit={onSearch}
@ -174,7 +176,7 @@ const Search: Component<{
href={props.noLinks ? undefined : `/p/${user.npub}`}
title={userName(user)}
description={nip05Verification(user)}
icon={<Avatar src={user.picture} size="xs" />}
icon={<Avatar user={user} size="xs" />}
statNumber={search?.scores[user.pubkey]}
statLabel={intl.formatMessage(t.followers)}
onClick={() => selectUser(user)}
@ -184,8 +186,7 @@ const Search: Component<{
</div>
</Show>
</div>
)
}
export default Search;
export default hookForDev(Search);

View File

@ -1,5 +1,6 @@
import { A } from '@solidjs/router';
import { Component, JSXElement, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import { truncateNumber } from '../../lib/notifications';
import { truncateName, } from '../../stores/profile';
@ -17,6 +18,7 @@ const SearchOption: Component<{
underline?: boolean,
onClick?: (e?: MouseEvent) => void,
highlighted?: boolean,
id?: string,
}> = (props) => {
const Content: Component<{ children: JSXElement }> = (prp) => {
@ -29,6 +31,7 @@ const SearchOption: Component<{
when={props.href}
fallback={
<div
id={props.id}
class={klass()}
onClick={props.onClick}
>
@ -37,6 +40,7 @@ const SearchOption: Component<{
}
>
<A
id={props.id}
href={props.href || ''}
class={klass()}
tabIndex={0}
@ -77,4 +81,4 @@ const SearchOption: Component<{
);
}
export default SearchOption;
export default hookForDev(SearchOption);

View File

@ -4,13 +4,21 @@ import { Select, createOptions } from "@thisbeyond/solid-select";
// Import default styles. (All examples use this via a global import)
import "@thisbeyond/solid-select/style.css";
import { Component } from "solid-js";
import { hookForDev } from "../../lib/devTools";
import { placeholders } from "../../translations";
import { FeedOption } from "../../types/primal";
// Apply custom styling. See stylesheet below.
import "./SelectBox.scss";
const SelectBox: Component<{ options: () => FeedOption[], onChange: (value: any) => void, initialValue: any, isSelected: (value: any) => boolean, isPhone?: boolean }> = (props) => {
const SelectBox: Component<{
options: () => FeedOption[],
onChange: (value: any) => void,
initialValue: any,
isSelected: (value: any) => boolean,
isPhone?: boolean,
id?: string,
}> = (props) => {
const intl = useIntl();
@ -28,6 +36,7 @@ const SelectBox: Component<{ options: () => FeedOption[], onChange: (value: any)
return (
<Select
id={props.id}
class={props.isPhone ? "phone_feed_select" : "feed_select"}
initialValue={props.initialValue}
onChange={props.onChange}
@ -42,4 +51,4 @@ const SelectBox: Component<{ options: () => FeedOption[], onChange: (value: any)
);
}
export default SelectBox;
export default hookForDev(SelectBox);

View File

@ -26,8 +26,9 @@ import styles from './SettingsNotifications.module.scss';
import { useSettingsContext } from '../../contexts/SettingsContext';
import { useIntl } from '@cookbook/solid-intl';
import Checkbox from '../Checkbox/Checkbox';
import { hookForDev } from '../../lib/devTools';
const SettingsNotifications: Component = () => {
const SettingsNotifications: Component<{ id?: string }> = (props) => {
const settings = useSettingsContext();
const intl = useIntl();
@ -109,7 +110,7 @@ const SettingsNotifications: Component = () => {
}
return (
<div class={styles.notificationSettings}>
<div id={props.id} class={styles.notificationSettings}>
<div class={styles.caption}>
{intl.formatMessage(t.notifications.core)}
@ -173,4 +174,4 @@ const SettingsNotifications: Component = () => {
);
}
export default SettingsNotifications;
export default hookForDev(SettingsNotifications);

View File

@ -8,8 +8,9 @@ import { Relay, relayInit } from "nostr-tools";
import styles from './SettingsSidebar.module.scss';
import { cacheServer, isConnected, socket } from '../../sockets';
import { hookForDev } from '../../lib/devTools';
const SettingsSidebar: Component = () => {
const SettingsSidebar: Component<{ id?: string }> = (props) => {
const intl = useIntl();
const account = useAccountContext();
@ -27,7 +28,7 @@ const SettingsSidebar: Component = () => {
};
return (
<>
<div id={props.id}>
<div class={styles.headingConnectedRelays}>
<div>
{intl.formatMessage(t.relays)}
@ -72,8 +73,8 @@ const SettingsSidebar: Component = () => {
{socket()?.url || cacheServer}
</span>
</div>
</>
</div>
)
}
export default SettingsSidebar;
export default hookForDev(SettingsSidebar);

View File

@ -6,8 +6,9 @@ import { debounce } from '../../utils';
import { useIntl } from '@cookbook/solid-intl';
import ConfirmModal from '../ConfirmModal/ConfirmModal';
import { settings as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const SettingsZap: Component = () => {
const SettingsZap: Component<{ id?: string }> = (props) => {
const intl = useIntl();
const settings = useSettingsContext();
@ -29,7 +30,6 @@ const SettingsZap: Component = () => {
return;
}
settings?.actions.setDefaultZapAmount(val);
}, 500)
};
@ -48,7 +48,7 @@ const SettingsZap: Component = () => {
};
return (
<div class={styles.zapSettings}>
<div id={props.id} class={styles.zapSettings}>
<div class={styles.defaultZaps}>
<div class={styles.caption}>
Set default zap amount:
@ -96,4 +96,4 @@ const SettingsZap: Component = () => {
);
}
export default SettingsZap;
export default hookForDev(SettingsZap);

View File

@ -6,8 +6,9 @@ import { useAccountContext } from '../../contexts/AccountContext';
import { PrimalUser } from '../../types/primal';
import { placeholders } from '../../translations';
import { useIntl } from '@cookbook/solid-intl';
import { hookForDev } from '../../lib/devTools';
const SmallCallToAction: Component<{ activeUser: PrimalUser | undefined }> = (params) => {
const SmallCallToAction: Component<{ activeUser: PrimalUser | undefined, id?: string }> = (props) => {
const account = useAccountContext();
const intl = useIntl();
@ -17,9 +18,9 @@ const SmallCallToAction: Component<{ activeUser: PrimalUser | undefined }> = (pa
};
return (
<button class={styles.callToAction} onClick={showNewNoteForm}>
<button id={props.id} class={styles.callToAction} onClick={showNewNoteForm}>
<Avatar
src={params.activeUser?.picture}
user={props.activeUser}
size="xs"
/>
@ -32,4 +33,4 @@ const SmallCallToAction: Component<{ activeUser: PrimalUser | undefined }> = (pa
);
}
export default SmallCallToAction;
export default hookForDev(SmallCallToAction);

View File

@ -9,9 +9,10 @@ import { date } from '../../lib/dates';
import { authorName } from '../../stores/profile';
import { note as t } from '../../translations';
import { useIntl } from '@cookbook/solid-intl';
import { hookForDev } from '../../lib/devTools';
const SmallNote: Component<{ note: PrimalNote, children?: JSXElement }> = (props) => {
const SmallNote: Component<{ note: PrimalNote, children?: JSXElement, id?: string }> = (props) => {
const threadContext = useThreadContext();
const intl = useIntl();
@ -71,37 +72,35 @@ const SmallNote: Component<{ note: PrimalNote, children?: JSXElement }> = (props
};
return (
<div>
<div class={styles.smallNote}>
<A href={`/p/${props.note.user.npub}`} class={styles.avatar}>
<Avatar src={props.note.user?.picture} size="xxs" />
</A>
<A
href={`/e/${props.note.post.noteId}`}
class={styles.content}
onClick={() => navToThread(props.note)}
>
<div class={styles.header}>
<div class={styles.name} title={nameOfAuthor()}>
{nameOfAuthor()}
</div>
<div class={styles.time}>
<Show
when={props.children}
fallback={date(props.note.post?.created_at).label}
>
{props.children}
</Show>
</div>
<div id={props.id} class={styles.smallNote}>
<A href={`/p/${props.note.user.npub}`} class={styles.avatar}>
<Avatar user={props.note.user} size="xxs" />
</A>
<A
href={`/e/${props.note.post.noteId}`}
class={styles.content}
onClick={() => navToThread(props.note)}
>
<div class={styles.header}>
<div class={styles.name} title={nameOfAuthor()}>
{nameOfAuthor()}
</div>
<div class={styles.message}>
<div innerHTML={parsedContent(props.note.post.content)}>
</div>
<div class={styles.time}>
<Show
when={props.children}
fallback={date(props.note.post?.created_at).label}
>
{props.children}
</Show>
</div>
</A>
</div>
</div>
<div class={styles.message}>
<div innerHTML={parsedContent(props.note.post.content)}>
</div>
</div>
</A>
</div>
);
}
export default SmallNote;
export default hookForDev(SmallNote);

View File

@ -1,16 +1,17 @@
import { Component, JSXElement } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import Wormhole from '../Wormhole/Wormhole';
import styles from './StickySidebar.module.scss';
const StickySidebar: Component<{ children: JSXElement }> = (props) => {
const StickySidebar: Component<{ children: JSXElement, id?: string }> = (props) => {
return (
<Wormhole
to="right_sidebar"
>
<div id="trending_wrapper" class={styles.stickyWrapper}>
<div id="trending_section" class={styles.trendingSection}>
<div id={props.id} class={styles.stickyWrapper}>
<div class={styles.trendingSection}>
{props.children}
</div>
</div>
@ -18,4 +19,4 @@ const StickySidebar: Component<{ children: JSXElement }> = (props) => {
);
}
export default StickySidebar;
export default hookForDev(StickySidebar);

View File

@ -4,8 +4,9 @@ import styles from './ThemeChooser.module.scss';
import ThemeOption from './ThemeOption/ThemeOption';
import { useSettingsContext } from '../../contexts/SettingsContext';
import { PrimalTheme } from '../../types/primal';
import { hookForDev } from '../../lib/devTools';
const ThemeChooser: Component = () => {
const ThemeChooser: Component<{ id?: string }> = (props) => {
const settings = useSettingsContext();
@ -14,7 +15,7 @@ const ThemeChooser: Component = () => {
};
return (
<div class={styles.themeChooser}>
<div id={props.id} class={styles.themeChooser}>
<For each={settings?.themes}>
{(theme) => (
<ThemeOption
@ -28,4 +29,4 @@ const ThemeChooser: Component = () => {
);
}
export default ThemeChooser;
export default hookForDev(ThemeChooser);

View File

@ -3,11 +3,13 @@ import styles from './ThemeOption.module.scss';
import check from '../../../assets/icons/check.svg';
import { PrimalTheme } from '../../../types/primal';
import { hookForDev } from '../../../lib/devTools';
const ThemeOption: Component<{
theme: PrimalTheme,
isSelected: boolean,
onSelect: (value: PrimalTheme) => void,
id?: string,
}> = (props) => {
const selectedClass = () => {
@ -19,7 +21,7 @@ const ThemeOption: Component<{
}
return (
<div class={styles.themeOption}>
<div id={props.id} class={styles.themeOption}>
<button
class={`${styles[props.theme.name]} ${selectedClass()}`}
onClick={() => props.onSelect(props.theme)}
@ -37,4 +39,4 @@ const ThemeOption: Component<{
);
}
export default ThemeOption;
export default hookForDev(ThemeOption);

View File

@ -1,11 +1,12 @@
import styles from "./VerificationCheck.module.scss";
import { Component, createSignal, Match, onMount, Show, Switch } from "solid-js";
import { Component, createSignal, onMount, Show } from "solid-js";
import { PrimalUser } from "../../types/primal";
import { isAccountVerified } from "../../lib/profile";
import { hookForDev } from "../../lib/devTools";
const VerificationCheck: Component<{ user: PrimalUser | undefined }> = (props) => {
const VerificationCheck: Component<{ user: PrimalUser | undefined, id?: string }> = (props) => {
const [isVerified, setIsVerified] = createSignal(true);
@ -32,28 +33,22 @@ const VerificationCheck: Component<{ user: PrimalUser | undefined }> = (props) =
})
return (
<Show
when={isVerified()}
fallback={
<div class={styles.verificationIcon}>
</div>
}
>
<div id={props.id} data-user={props.user?.pubkey} class={styles.verificationIcon}>
<Show
when={isVerifiedByPrimal()}
fallback={
<div class={styles.verificationIcon}>
<span class={styles.verifiedIcon} />
</div>
}
when={isVerified()}
>
<div class={styles.verificationIcon}>
<Show
when={isVerifiedByPrimal()}
fallback={
<span class={styles.verifiedIcon} />
}
>
<span class={styles.whiteCheck} />
<span class={styles.verifiedIconPrimal} />
</div>
</Show>
</Show>
</Show>
</div>
)
}
export default VerificationCheck;
export default hookForDev(VerificationCheck);

30
src/lib/devTools.ts Normal file
View File

@ -0,0 +1,30 @@
import { onMount, onCleanup } from "solid-js";
import { ComponentLog, PrimalWindow } from "../types/primal";
const hook = (type: 'onPrimalComponentMount' | 'onPrimalComponentCleanup', data: ComponentLog) => {
const fn = (window as PrimalWindow)[type];
fn && fn(data);
};
export const hookForDev = (fn: Function) => {
if (localStorage.getItem('devMode') !== 'true') {
return fn;
}
return (props: any) => {
const domId = props.id || `${fn.name}_${crypto.randomUUID()}`;
const scope: ComponentLog = { name: fn.name, props, domId };
onMount(() => {
hook('onPrimalComponentMount', scope);
});
onCleanup(() => {
hook('onPrimalComponentCleanup', scope);
})
props.id = domId;
return fn(props);
};
}

View File

@ -74,7 +74,7 @@ export const urlify = (
const isImage = url.includes('.jpg')|| url.includes('.jpeg')|| url.includes('.webp') || url.includes('.png') || url.includes('.gif') || url.includes('format=png');
if (isImage) {
const dev = JSON.parse(localStorage.getItem('devMode') || 'false');
const dev = localStorage.getItem('devMode') === 'true';
let imgUrl = getMediaUrl && getMediaUrl(url);
if (!imgUrl) {

View File

@ -49,7 +49,7 @@ const EditProfile: Component = () => {
const [bannerPreview, setBannerPreview] = createSignal<string>();
const flagBannerForWarning = () => {
const dev = JSON.parse(localStorage.getItem('devMode') || 'false');
const dev = localStorage.getItem('devMode') === 'true';
// @ts-ignore
if (isBannerCached() || !dev) {

View File

@ -151,7 +151,7 @@ const Home: Component = () => {
class={styles.avatar}
title={userName(user)}
>
<Avatar src={user.picture} size="xss" />
<Avatar user={user} size="xss" />
</div>
)}
</For>

View File

@ -933,7 +933,7 @@ const Messages: Component = () => {
onClick={() => selectSender(sender.npub)}
data-user={sender.pubkey}
>
<Avatar src={sender.picture} size="vs" />
<Avatar user={sender} size="vs" />
<div class={styles.senderInfo}>
<div class={styles.firstLine}>
<div class={styles.senderName}>
@ -993,7 +993,7 @@ const Messages: Component = () => {
<SearchOption
title={userName(user)}
description={user.nip05}
icon={<Avatar src={user.picture} size="xs" />}
icon={<Avatar user={user} size="xs" />}
statNumber={search?.scores[user.pubkey]}
statLabel={intl.formatMessage(tSearch.followers)}
onClick={() => selectUser(user)}
@ -1043,7 +1043,7 @@ const Messages: Component = () => {
href={`/p/${hexToNpub(thread.author)}`}
class={styles.avatar}
>
<Avatar src={account?.activeUser?.picture} size="xxs" />
<Avatar user={account?.activeUser} size="xxs" />
</A>
<div class={styles.threadMessages}>
<For each={thread.messages}>
@ -1070,7 +1070,7 @@ const Messages: Component = () => {
href={`/p/${hexToNpub(thread.author)}`}
class={styles.avatar}
>
<Avatar src={user(thread.author)?.picture} size="xxs" />
<Avatar user={user(thread.author)} size="xxs" />
</A>
<div class={styles.threadMessages}>
<For each={thread.messages}>

View File

@ -165,7 +165,7 @@ const Mutelist: Component = () => {
}
>
<Link class={styles.userInfo} href={`/p/${user(pubkey).npub}`}>
<Avatar src={user(pubkey).picture} size='sm' />
<Avatar user={user(pubkey)} size='sm' />
<div class={styles.userName}>
<div class={styles.title}>{userName(user(pubkey))}</div>
<div class={styles.verification}>{nip05Verification(user(pubkey))}</div>

View File

@ -175,7 +175,7 @@ const Profile: Component = () => {
}
const flagBannerForWarning = () => {
const dev = JSON.parse(localStorage.getItem('devMode') || 'false');
const dev = localStorage.getItem('devMode') === 'true';
// @ts-ignore
if (isBannerCached() || !dev) {
@ -483,11 +483,11 @@ const Profile: Component = () => {
<div class={styles.userImage}>
<div class={styles.avatar}>
<div class={styles.desktopAvatar}>
<Avatar src={profile?.userProfile?.picture} size="xxl" />
<Avatar user={profile?.userProfile} size="xxl" />
</div>
<div class={styles.phoneAvatar}>
<Avatar src={profile?.userProfile?.picture} size="lg" />
<Avatar user={profile?.userProfile} size="lg" />
</div>
</div>
</div>

View File

@ -227,7 +227,7 @@ const Moderation: Component = () => {
<div class={styles.filterListName} title={my()?.pubkey}>
<A href='/p' class={styles.avatar}>
<Avatar
src={account?.activeUser?.picture}
user={account?.activeUser}
size='xs'
/>
</A>
@ -259,7 +259,7 @@ const Moderation: Component = () => {
<div class={styles.filterListName} title={mutelist.pubkey}>
<A href={`/p/${users[mutelist.pubkey || '']?.npub}`} class={styles.avatar}>
<Avatar
src={users[mutelist.pubkey || '']?.picture}
user={users[mutelist.pubkey || '']}
size='xs'
/>
</A>
@ -385,7 +385,7 @@ const Moderation: Component = () => {
<div class={styles.filterListName} title={reason}>
<A href={`/p/${users[reason || '']?.npub}`} class={styles.avatar}>
<Avatar
src={users[reason || '']?.picture}
user={users[reason || '']}
size='xs'
/>
</A>
@ -446,7 +446,7 @@ const Moderation: Component = () => {
<div class={styles.allowlistInfo} title={hexToNpub(pubkey)}>
<div class={styles.avatar}>
<Avatar
src={users[pubkey || '']?.picture}
user={users[pubkey || '']}
size='xs'
/>
</div>

View File

@ -97,7 +97,7 @@ const Muted: Component = () => {
}
>
<Link class={styles.userInfo} href={`/p/${user(pubkey).npub}`}>
<Avatar src={user(pubkey).picture} size='sm' />
<Avatar user={user(pubkey)} size='sm' />
<div class={styles.userName}>
<div class={styles.title}>{userName(user(pubkey))}</div>
<div class={styles.verification}>{nip05Verification(user(pubkey))}</div>

View File

@ -156,6 +156,7 @@ const Thread: Component = () => {
<Show when={primaryNote()}>
<div id="primary_note" class={styles.threadList}>
<NotePrimary
id="bojan"
note={primaryNote() as PrimalNote}
/>
<Show when={account?.hasPublicKey()}>

View File

@ -340,6 +340,8 @@ export type NostrWindow = Window & typeof globalThis & {
export type PrimalWindow = Window & typeof globalThis & {
loadPrimalStores: () => void,
primal?: any,
onPrimalComponentMount?: (data: any) => void,
onPrimalComponentCleanup?: (data: any) => void,
};
export type NostrEventType = "EVENT" | "EOSE" | "NOTICE";
@ -572,3 +574,9 @@ export type ContentModeration = {
name: string,
scopes: string[],
}
export type ComponentLog = {
name: string,
domId: string,
props: any,
}