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

View File

@ -1,17 +1,18 @@
import { Component, createMemo, createSignal, Show } from 'solid-js'; import { Component, createMemo, createSignal, Show } from 'solid-js';
import defaultAvatar from '../../assets/icons/default_nostrich.svg'; import defaultAvatar from '../../assets/icons/default_nostrich.svg';
import { useMediaContext } from '../../contexts/MediaContext'; import { useMediaContext } from '../../contexts/MediaContext';
import { getMediaUrl } from '../../lib/media'; import { hookForDev } from '../../lib/devTools';
import { MediaSize, PrimalUser } from '../../types/primal'; import { MediaSize, PrimalUser } from '../../types/primal';
import VerificationCheck from '../VerificationCheck/VerificationCheck'; import VerificationCheck from '../VerificationCheck/VerificationCheck';
import styles from './Avatar.module.scss'; import styles from './Avatar.module.scss';
const Avatar: Component<{ const Avatar: Component<{
src: string | undefined, src?: string | undefined,
size?: "xxs" | "xss" | "xs" | "vs" | "sm" | "md" | "lg" | "xl" | "xxl", size?: "xxs" | "xss" | "xs" | "vs" | "sm" | "md" | "lg" | "xl" | "xxl",
user?: PrimalUser, user?: PrimalUser,
highlightBorder?: boolean, highlightBorder?: boolean,
id?: string,
}> = (props) => { }> = (props) => {
const media = useMediaContext(); const media = useMediaContext();
@ -78,15 +79,21 @@ const Avatar: Component<{
break; 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); setIsCached(!!url);
return url ?? props.src; return url ?? src;
}); });
const notCachedFlag = () => { const notCachedFlag = () => {
const dev = JSON.parse(localStorage.getItem('devMode') || 'false'); const dev = localStorage.getItem('devMode') === 'true';
// @ts-ignore // @ts-ignore
if (isCached() || !dev) { if (isCached() || !dev) {
@ -97,7 +104,11 @@ const Avatar: Component<{
} }
return ( return (
<div class={`${avatarClass[selectedSize]} ${highlightClass()}`}> <div
id={props.id}
class={`${avatarClass[selectedSize]} ${highlightClass()}`}
data-user={props.user?.pubkey}
>
<Show <Show
when={imageSrc()} when={imageSrc()}
fallback={ 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 { Component, JSXElement, Match, Show, Switch } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './Checkbox.module.scss'; import styles from './Checkbox.module.scss';
const Checkbox: Component<{ const Checkbox: Component<{
id: string, id?: string,
onChange: (e: Event) => void, onChange: (e: Event) => void,
checked?: boolean, checked?: boolean,
label?: string, label?: string,
@ -13,9 +14,11 @@ const Checkbox: Component<{
}> = (props) => { }> = (props) => {
return ( return (
<div class={styles.checkbox}> <div
id={props.id}
class={styles.checkbox}
>
<input <input
id={props.id}
type='checkbox' type='checkbox'
checked={props.checked} checked={props.checked}
onChange={props.onChange} 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 { confirmDefaults as t } from '../../translations';
import styles from './ConfirmModal.module.scss'; import styles from './ConfirmModal.module.scss';
import { hookForDev } from '../../lib/devTools';
const ConfirmModal: Component<{ const ConfirmModal: Component<{
id?: string,
open?: boolean, open?: boolean,
title?: string, title?: string,
description?: string, description?: string,
@ -28,7 +30,7 @@ const ConfirmModal: Component<{
return ( return (
<Modal open={props.open}> <Modal open={props.open}>
<div class={styles.feedsRestoreModal}> <div id={props.id} class={styles.feedsRestoreModal}>
<div class={styles.feedConfirmationTitle}> <div class={styles.feedConfirmationTitle}>
{props.title || intl.formatMessage(t.title)} {props.title || intl.formatMessage(t.title)}
</div> </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 { Component, createEffect, createSignal, For } from 'solid-js';
import { useAccountContext } from '../../contexts/AccountContext'; import { useAccountContext } from '../../contexts/AccountContext';
import { useSettingsContext } from '../../contexts/SettingsContext'; import { useSettingsContext } from '../../contexts/SettingsContext';
import { hookForDev } from '../../lib/devTools';
import { zapNote } from '../../lib/zap'; import { zapNote } from '../../lib/zap';
import { userName } from '../../stores/profile'; import { userName } from '../../stores/profile';
import { toastZapFail, zapCustomOption } from '../../translations'; import { toastZapFail, zapCustomOption } from '../../translations';
@ -13,6 +14,7 @@ import { useToastContext } from '../Toaster/Toaster';
import styles from './CustomZap.module.scss'; import styles from './CustomZap.module.scss';
const CustomZap: Component<{ const CustomZap: Component<{
id?: string,
open?: boolean, open?: boolean,
note: PrimalNote, note: PrimalNote,
onConfirm: (amount?: number) => void, onConfirm: (amount?: number) => void,
@ -96,7 +98,7 @@ const CustomZap: Component<{
return ( return (
<Modal open={props.open}> <Modal open={props.open}>
<div class={styles.customZap}> <div id={props.id} class={styles.customZap}>
<div class={styles.header}> <div class={styles.header}>
<div class={styles.title}> <div class={styles.title}>
<div class={styles.zapIcon}></div> <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}> <div class={styles.mentionedNoteHeader}>
<Avatar <Avatar
src={props.note.user.picture} user={props.note.user}
size="xxs" size="xxs"
/> />
<span class={styles.postInfo}> <span class={styles.postInfo}>

View File

@ -1,6 +1,7 @@
import { useIntl } from '@cookbook/solid-intl'; import { useIntl } from '@cookbook/solid-intl';
import { A } from '@solidjs/router'; import { A } from '@solidjs/router';
import type { Component } from 'solid-js'; import type { Component } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import { scopeDescriptors, timeframeDescriptors } from '../../translations'; import { scopeDescriptors, timeframeDescriptors } from '../../translations';
import { ScopeDescriptor } from '../../types/primal'; import { ScopeDescriptor } from '../../types/primal';
@ -33,7 +34,7 @@ const timeframeIcons: Record<string, string> = {
latest: styles.clockIcon, latest: styles.clockIcon,
} }
const ExploreMenuItem: Component<{ scope: string, stat: number }> = (props) => { const ExploreMenuItem: Component<{ scope: string, stat: number, id?: string }> = (props) => {
const intl = useIntl(); const intl = useIntl();
@ -52,7 +53,7 @@ const ExploreMenuItem: Component<{ scope: string, stat: number }> = (props) => {
} }
return ( return (
<div class={styles.exploreMenuItem}> <div id={props.id} class={styles.exploreMenuItem}>
<div class={styles.itemInfo}> <div class={styles.itemInfo}>
<div class={item().icon} ></div> <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 { hexToNpub } from '../../lib/keys';
import { exploreSidebarCaption } from '../../translations'; import { exploreSidebarCaption } from '../../translations';
import { useAccountContext } from '../../contexts/AccountContext'; import { useAccountContext } from '../../contexts/AccountContext';
import { hookForDev } from '../../lib/devTools';
const ExploreSidebar: Component = () => { const ExploreSidebar: Component<{ id?: string }> = (props) => {
const intl = useIntl(); const intl = useIntl();
const account = useAccountContext(); const account = useAccountContext();
@ -119,7 +120,7 @@ const ExploreSidebar: Component = () => {
// RENDER --------------------------------------- // RENDER ---------------------------------------
return ( return (
<> <div id={props.id}>
<div class={styles.trendingUsersCaption}> <div class={styles.trendingUsersCaption}>
{intl.formatMessage(exploreSidebarCaption)} {intl.formatMessage(exploreSidebarCaption)}
</div> </div>
@ -132,15 +133,15 @@ const ExploreSidebar: Component = () => {
class={styles.user} class={styles.user}
title={authorName(user)} title={authorName(user)}
> >
<Avatar src={user.picture} size="vs" /> <Avatar user={user} size="vs" />
<div class={styles.name}>{authorName(user)}</div> <div class={styles.name}>{authorName(user)}</div>
</A> </A>
) )
} }
</For> </For>
</div> </div>
</> </div>
) )
} }
export default ExploreSidebar; export default hookForDev(ExploreSidebar);

View File

@ -1,5 +1,6 @@
import { Component, Show } from 'solid-js'; import { Component, Show } from 'solid-js';
import { useSettingsContext } from '../../contexts/SettingsContext'; import { useSettingsContext } from '../../contexts/SettingsContext';
import { hookForDev } from '../../lib/devTools';
import styles from './ExternalLink.module.scss'; import styles from './ExternalLink.module.scss';
@ -8,12 +9,13 @@ const ExternalLink: Component<{
darkIcon: string, darkIcon: string,
label: string, label: string,
href: string, href: string,
id?: string,
}> = (props) => { }> = (props) => {
const settings = useSettingsContext(); const settings = useSettingsContext();
return ( return (
<div class={styles.externalLink}> <div id={props.id} class={styles.externalLink}>
<Show <Show
when={['sunset', 'midnight'].includes(settings?.theme || 'sunset')} when={['sunset', 'midnight'].includes(settings?.theme || 'sunset')}
fallback={ 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 { Component } from 'solid-js';
import { useHomeContext } from '../../contexts/HomeContext'; import { useHomeContext } from '../../contexts/HomeContext';
import { useSettingsContext } from '../../contexts/SettingsContext'; import { useSettingsContext } from '../../contexts/SettingsContext';
import { hookForDev } from '../../lib/devTools';
import { FeedOption } from '../../types/primal'; import { FeedOption } from '../../types/primal';
import SelectBox from '../SelectBox/SelectBox'; import SelectBox from '../SelectBox/SelectBox';
const FeedSelect: Component<{ isPhone?: boolean}> = (props) => { const FeedSelect: Component<{ isPhone?: boolean, id?: string}> = (props) => {
const home = useHomeContext(); const home = useHomeContext();
const settings = useSettingsContext(); 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 { Component, createEffect, createSignal, For, Show } from 'solid-js';
import { useAccountContext } from '../../contexts/AccountContext'; import { useAccountContext } from '../../contexts/AccountContext';
import { useSettingsContext } from '../../contexts/SettingsContext'; import { useSettingsContext } from '../../contexts/SettingsContext';
import { hookForDev } from '../../lib/devTools';
import { PrimalFeed } from '../../types/primal'; import { PrimalFeed } from '../../types/primal';
import styles from './FeedSorter.module.scss'; import styles from './FeedSorter.module.scss';
const FeedSorter: Component = () => { const FeedSorter: Component<{ id?: string }> = (props) => {
let sorter: any; let sorter: any;
@ -109,7 +110,7 @@ const FeedSorter: Component = () => {
}); });
return ( return (
<div id="feed_sorter" class={styles.feedSorter} ref={sorter}> <div id={props.id} class={styles.feedSorter} ref={sorter}>
<Show when={availableFeeds().length > 0}> <Show when={availableFeeds().length > 0}>
<For each={availableFeeds()}> <For each={availableFeeds()}>
{(feed, index) => ( {(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 { useAccountContext } from "../../contexts/AccountContext";
import { hookForDev } from "../../lib/devTools";
import styles from "./FloatingNewPostButton.module.scss"; import styles from "./FloatingNewPostButton.module.scss";
export default function FloatingNewPostButton() { const FloatingNewPostButton: Component<{ id?: string }> = (props) => {
const account = useAccountContext(); const account = useAccountContext();
const showNewNoteForm = () => { const showNewNoteForm = () => {
@ -12,6 +14,7 @@ export default function FloatingNewPostButton() {
return ( return (
<button <button
id={props.id}
class={styles.newPostButton} class={styles.newPostButton}
onClick={showNewNoteForm} onClick={showNewNoteForm}
> >
@ -19,3 +22,5 @@ export default function FloatingNewPostButton() {
</button> </button>
) )
} }
export default hookForDev(FloatingNewPostButton);

View File

@ -1,6 +1,7 @@
import { useIntl } from '@cookbook/solid-intl'; import { useIntl } from '@cookbook/solid-intl';
import { Component, Show } from 'solid-js'; import { Component, Show } from 'solid-js';
import { useAccountContext } from '../../contexts/AccountContext'; import { useAccountContext } from '../../contexts/AccountContext';
import { hookForDev } from '../../lib/devTools';
import { account as t } from '../../translations'; import { account as t } from '../../translations';
import { PrimalUser } from '../../types/primal'; import { PrimalUser } from '../../types/primal';
import { useToastContext } from '../Toaster/Toaster'; import { useToastContext } from '../Toaster/Toaster';
@ -8,7 +9,7 @@ import { useToastContext } from '../Toaster/Toaster';
import styles from './FollowButton.module.scss'; 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 toast = useToastContext()
const account = useAccountContext(); const account = useAccountContext();
@ -40,7 +41,7 @@ const FollowButton: Component<{ person: PrimalUser | undefined, large?: boolean
return ( return (
<Show when={props.person}> <Show when={props.person}>
<div class={klass()}> <div id={props.id} class={klass()}>
<button onClick={onFollow} > <button onClick={onFollow} >
<Show <Show
when={isFollowed()} 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 { Component, JSXElement } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './HelpTip.module.scss'; 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 ( 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.helpIcon}></div>
<div class={styles.content}> <div class={styles.content}>
{props.children} {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 { useIntl } from '@cookbook/solid-intl';
import { useSettingsContext } from '../../contexts/SettingsContext'; import { useSettingsContext } from '../../contexts/SettingsContext';
import { placeholders as t } from '../../translations'; import { placeholders as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const HomeHeader: Component = () => { const HomeHeader: Component< { id?: string} > = (props) => {
const account = useAccountContext(); const account = useAccountContext();
const home = useHomeContext(); const home = useHomeContext();
@ -73,7 +74,7 @@ const HomeHeader: Component = () => {
const activeUser = () => account?.activeUser; const activeUser = () => account?.activeUser;
return ( return (
<div class={styles.fullHeader}> <div id={props.id} class={styles.fullHeader}>
<Show <Show
when={account?.hasPublicKey()} when={account?.hasPublicKey()}
fallback={<div class={styles.welcomeMessage}> fallback={<div class={styles.welcomeMessage}>
@ -82,7 +83,7 @@ const HomeHeader: Component = () => {
> >
<button class={styles.callToAction} onClick={onShowNewNoteinput}> <button class={styles.callToAction} onClick={onShowNewNoteinput}>
<Avatar <Avatar
src={activeUser()?.picture} user={activeUser()}
size="lg" 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 FeedSelect from '../FeedSelect/FeedSelect';
import Branding from '../Branding/Branding'; import Branding from '../Branding/Branding';
import { useHomeContext } from '../../contexts/HomeContext'; import { useHomeContext } from '../../contexts/HomeContext';
import { hookForDev } from '../../lib/devTools';
const HomeHeaderPhone: Component = () => { const HomeHeaderPhone: Component< { id?: string } > = (props) => {
const home = useHomeContext(); const home = useHomeContext();
let lastScrollTop = document.body.scrollTop || document.documentElement.scrollTop; let lastScrollTop = document.body.scrollTop || document.documentElement.scrollTop;
let smallHeader: HTMLDivElement | undefined;
let border: HTMLDivElement | undefined;
const onScroll = () => { const onScroll = () => {
const scrollTop = document.body.scrollTop || document.documentElement.scrollTop; 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); home?.actions?.updateScrollTop(scrollTop);
@ -58,7 +59,7 @@ const HomeHeaderPhone: Component = () => {
}); });
return ( return (
<div id="phone_header" class={styles.fullHeader}> <div id={props.id} class={styles.fullHeader} ref={smallHeader}>
<div class={styles.phoneHeader}> <div class={styles.phoneHeader}>
<div class={styles.logo}> <div class={styles.logo}>
<Branding small={true} /> <Branding small={true} />
@ -68,7 +69,7 @@ const HomeHeaderPhone: Component = () => {
</Show> </Show>
</div> </div>
<div <div
id="small_bottom_border" ref={border}
class={styles.smallHeaderBottomBorder} class={styles.smallHeaderBottomBorder}
> >
<div class={styles.leftCorner}></div> <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 { home as t } from '../../translations';
import { useAccountContext } from '../../contexts/AccountContext'; import { useAccountContext } from '../../contexts/AccountContext';
import { hookForDev } from '../../lib/devTools';
const [init, setInit] = createSignal(false); 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 intl = useIntl();
const account = useAccountContext(); const account = useAccountContext();
@ -157,7 +158,7 @@ const HomeSidebar: Component = () => {
}; };
return ( return (
<div> <div id={props.id}>
<div class={styles.headingTrending}> <div class={styles.headingTrending}>
<div> <div>
<div class={styles.flameIcon}></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 { Component, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './LinkPreview.module.scss'; 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); const encodedUrl = encodeURI(new URL(props.preview.url).origin);
return ( return (
<a <a
id={props.id}
href={props.preview.url} href={props.preview.url}
class={styles.linkPreview} 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 { Component } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './Loader.module.scss'; import styles from './Loader.module.scss';
const Loader: Component = () => { const Loader: Component< { id?: string } > = (props) => {
return ( 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 { useIntl } from '@cookbook/solid-intl';
import { Component, JSXElement, Show } from 'solid-js'; import { Component, JSXElement, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import { placeholders as t } from '../../translations'; import { placeholders as t } from '../../translations';
import Branding from '../Branding/Branding'; import Branding from '../Branding/Branding';
import Search from '../Search/Search'; import Search from '../Search/Search';
@ -7,12 +8,12 @@ import Wormhole from '../Wormhole/Wormhole';
import styles from './MissingPage.module.scss'; 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(); const intl = useIntl();
return ( return (
<> <div id={props.id}>
<Wormhole to="branding_holder" > <Wormhole to="branding_holder" >
<Branding small={false} /> <Branding small={false} />
</Wormhole> </Wormhole>
@ -44,8 +45,8 @@ const MissingPage: Component<{ title: string, children?: JSXElement }> = (props)
{props.children} {props.children}
</div> </div>
</Show> </Show>
</> </div>
) )
} }
export default MissingPage; export default hookForDev(MissingPage);

View File

@ -1,14 +1,15 @@
import { Component, JSXElement, Show } from 'solid-js'; import { Component, JSXElement, Show } from 'solid-js';
import { Portal } from 'solid-js/web'; import { Portal } from 'solid-js/web';
import { hookForDev } from '../../lib/devTools';
import styles from './Modal.module.scss'; 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 ( return (
<Show when={props.open}> <Show when={props.open}>
<Portal mount={document.getElementById("modal") as Node}> <Portal mount={document.getElementById("modal") as Node}>
<div class={styles.modal}> <div id={props.id} class={styles.modal}>
{props.children} {props.children}
</div> </div>
</Portal> </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 { A, useLocation, useNavigate } from '@solidjs/router';
import { Component, Show } from 'solid-js'; import { Component, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './NavLink.module.scss'; import styles from './NavLink.module.scss';
const NavLink: Component<{ const NavLink: Component<{
id?: string,
to: string, to: string,
label: string, label: string,
icon: string, icon: string,
@ -40,7 +42,7 @@ const NavLink: Component<{
} }
return ( 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}> <Show when={props.bubble && props.bubble() > 0}>
<div class={`${styles.bubble} ${bubbleClass()}`}> <div class={`${styles.bubble} ${bubbleClass()}`}>
<div>{props.bubble && props.bubble() < 100 ? props.bubble() : '99+'}</div> <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 FloatingNewPostButton from '../FloatingNewPostButton/FloatingNewPostButton';
import styles from './NavMenu.module.scss'; import styles from './NavMenu.module.scss';
import { hookForDev } from '../../lib/devTools';
const NavMenu: Component = () => { const NavMenu: Component< { id?: string } > = (props) => {
const account = useAccountContext(); const account = useAccountContext();
const notifications = useNotificationsContext(); const notifications = useNotificationsContext();
const messages = useMessagesContext(); const messages = useMessagesContext();
@ -62,7 +63,7 @@ const NavMenu: Component = () => {
]; ];
return ( return (
<div class={styles.navMenu}> <div id={props.id} class={styles.navMenu}>
<nav class={styles.sideNav}> <nav class={styles.sideNav}>
<For each={links}> <For each={links}>
{({ to, label, icon, bubble, hiddenOnSmallScreens }) => { {({ 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, actions as tActions,
} from "../../../translations"; } from "../../../translations";
import { useMediaContext } from "../../../contexts/MediaContext"; import { useMediaContext } from "../../../contexts/MediaContext";
import { hookForDev } from "../../../lib/devTools";
type AutoSizedTextArea = HTMLTextAreaElement & { _baseScrollHeight: number }; type AutoSizedTextArea = HTMLTextAreaElement & { _baseScrollHeight: number };
const EditBox: Component<{ const EditBox: Component<{
id?: string,
replyToNote?: PrimalNote, replyToNote?: PrimalNote,
onClose?: () => void, onClose?: () => void,
onSuccess?: (note: SendNoteResult) => void, onSuccess?: (note: SendNoteResult) => void,
@ -1085,6 +1087,7 @@ const EditBox: Component<{
return ( return (
<div <div
id={props.id}
class={styles.noteEditBox} class={styles.noteEditBox}
ref={editWrap} ref={editWrap}
onDrop={onDrop} onDrop={onDrop}
@ -1147,7 +1150,7 @@ const EditBox: Component<{
<SearchOption <SearchOption
title={userName(user)} title={userName(user)}
description={nip05Verification(user)} description={nip05Verification(user)}
icon={<Avatar src={user.picture} size="xs" />} icon={<Avatar user={user} size="xs" />}
statNumber={search?.scores[user.pubkey]} statNumber={search?.scores[user.pubkey]}
statLabel={intl.formatMessage(tSearch.followers)} statLabel={intl.formatMessage(tSearch.followers)}
onClick={() => selectUser(user)} 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.newNote}>
<div class={styles.leftSide}> <div class={styles.leftSide}>
<Avatar <Avatar
src={activeUser()?.picture}
size="md" size="md"
user={activeUser()} user={activeUser()}
/> />

View File

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

View File

@ -1,19 +1,20 @@
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { Component, JSXElement } from "solid-js"; 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 { PrimalUser } from "../../../types/primal";
import Avatar from "../../Avatar/Avatar";
import styles from "./MentionedUserLink.module.scss"; import styles from "./MentionedUserLink.module.scss";
const MentionedUserLink: Component<{ const MentionedUserLink: Component<{
user: PrimalUser, user: PrimalUser,
openInNewTab?: boolean, openInNewTab?: boolean,
id?: string,
}> = (props) => { }> = (props) => {
const LinkComponent: Component<{ children: JSXElement }> = (p) => { const LinkComponent: Component<{ children: JSXElement }> = (p) => {
return props.openInNewTab ? return props.openInNewTab ?
<a 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}`} 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}`}>{p.children}</A>;
}; };
return ( 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 { useIntl } from '@cookbook/solid-intl';
import { truncateNpub } from '../../stores/profile'; import { truncateNpub } from '../../stores/profile';
import { note as t } from '../../translations'; 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 threadContext = useThreadContext();
const intl = useIntl(); const intl = useIntl();
@ -38,6 +39,7 @@ const Note: Component<{ note: PrimalNote }> = (props) => {
return ( return (
<A <A
id={props.id}
class={styles.postLink} class={styles.postLink}
href={`/e/${props.note?.post.noteId}`} href={`/e/${props.note?.post.noteId}`}
onClick={() => navToThread(props.note)} 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 { medZapLimit } from '../../../constants';
import { toast as t } from '../../../translations'; import { toast as t } from '../../../translations';
import PrimalMenu from '../../PrimalMenu/PrimalMenu'; 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 account = useAccountContext();
const toast = useToastContext(); const toast = useToastContext();
@ -360,7 +361,7 @@ const NoteFooter: Component<{ note: PrimalNote, doCustomZap?: boolean }> = (prop
const [hideZapIcon, setHideZapIcon] = createSignal(false); const [hideZapIcon, setHideZapIcon] = createSignal(false);
return ( return (
<div class={styles.footer} ref={footerDiv}> <div id={props.id} class={styles.footer} ref={footerDiv}>
<Show when={showSmallZapAnim()}> <Show when={showSmallZapAnim()}>
<lottie-player <lottie-player
id={`note-small-zap-${props.note.post.id}`} 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 { APP_ID } from '../../../App';
import ConfirmModal from '../../ConfirmModal/ConfirmModal'; import ConfirmModal from '../../ConfirmModal/ConfirmModal';
import { hexToNpub } from '../../../lib/keys'; 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 intl = useIntl();
const toaster = useToastContext(); const toaster = useToastContext();
@ -191,7 +192,7 @@ const NoteHeader: Component<{ note: PrimalNote, openCustomZap?: () => void}> = (
noteContextForEveryone; noteContextForEveryone;
return ( return (
<div class={styles.header}> <div id={props.id} class={styles.header}>
<div class={styles.headerInfo}> <div class={styles.headerInfo}>
<div <div
class={styles.avatar} class={styles.avatar}
@ -201,7 +202,7 @@ const NoteHeader: Component<{ note: PrimalNote, openCustomZap?: () => void}> = (
href={`/p/${props.note.user.npub}`} href={`/p/${props.note.user.npub}`}
> >
<Avatar <Avatar
src={props.note?.user?.picture} user={props.note?.user}
size="sm" size="sm"
highlightBorder={isVerifiedByPrimal()} 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 { Component } from 'solid-js';
import { truncateNpub } from '../../../stores/profile'; import { hookForDev } from '../../../lib/devTools';
import { PrimalNote } from '../../../types/primal'; import { PrimalNote } from '../../../types/primal';
import ParsedNote from '../../ParsedNote/ParsedNote'; import ParsedNote from '../../ParsedNote/ParsedNote';
import NoteFooter from '../NoteFooter/NoteFooter'; import NoteFooter from '../NoteFooter/NoteFooter';
@ -8,26 +8,27 @@ import NoteHeader from '../NoteHeader/NoteHeader';
import styles from './NotePrimary.module.scss'; import styles from './NotePrimary.module.scss';
const NotePrimary: Component<{ note: PrimalNote }> = (props) => { const NotePrimary: Component<{ note: PrimalNote, id?: string }> = (props) => {
return ( return (
<div <div
class={styles.post} id={props.id}
data-event={props.note.post.id} class={styles.post}
data-event-bech32={props.note.post.noteId} 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.border}></div>
<div class={styles.content}> <NoteHeader note={props.note} />
<div class={styles.content}>
<div class={styles.message}> <div class={styles.message}>
<ParsedNote note={props.note} /> <ParsedNote note={props.note} />
</div>
<NoteFooter note={props.note}/>
</div> </div>
<NoteFooter note={props.note}/>
</div> </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 styles from './NotificationNote.module.scss';
import { useThreadContext } from '../../../contexts/ThreadContext'; 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(); const threadContext = useThreadContext();
@ -17,6 +18,7 @@ const Note: Component<{ note: PrimalNote }> = (props) => {
return ( return (
<A <A
id={props.id}
class={styles.postLink} class={styles.postLink}
href={`/e/${props.note?.post.noteId}`} href={`/e/${props.note?.post.noteId}`}
onClick={() => navToThread(props.note)} 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 { Component, Show } from 'solid-js';
import defaultAvatar from '../../assets/icons/default_nostrich.svg'; import defaultAvatar from '../../assets/icons/default_nostrich.svg';
import { hookForDev } from '../../lib/devTools';
import styles from './NotificationAvatar.module.scss'; import styles from './NotificationAvatar.module.scss';
const NotificationAvatar: Component<{ const NotificationAvatar: Component<{
number: number | undefined, number: number | undefined,
size?: "xxs" | "xs" | "vs" | "sm" | "md" | "lg" | "xl" | "xxl", size?: "xxs" | "xs" | "vs" | "sm" | "md" | "lg" | "xl" | "xxl",
verified?: string verified?: string,
id?: string,
}> = (props) => { }> = (props) => {
const selectedSize = props.size || 'sm'; const selectedSize = props.size || 'sm';
@ -41,7 +43,7 @@ const NotificationAvatar: Component<{
} }
return ( return (
<div class={avatarClass[selectedSize]}> <div id={props.id} class={avatarClass[selectedSize]}>
<Show <Show
when={props.number} when={props.number}
fallback={ 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 NotificationNote from '../Note/NotificationNote/NotificationNote';
import NotificationAvatar from '../NotificationAvatar/NotificationAvatar'; import NotificationAvatar from '../NotificationAvatar/NotificationAvatar';
import { notificationsNew as t } from '../../translations'; import { notificationsNew as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const typeIcons: Record<string, string> = { const typeIcons: Record<string, string> = {
[NotificationType.NEW_USER_FOLLOWED_YOU]: userFollow, [NotificationType.NEW_USER_FOLLOWED_YOU]: userFollow,
@ -58,6 +59,7 @@ const typeIcons: Record<string, string> = {
} }
type NotificationItemProps = { type NotificationItemProps = {
id?: string,
type: NotificationType, type: NotificationType,
users?: PrimalNotifUser[], users?: PrimalNotifUser[],
note?: PrimalNote, note?: PrimalNote,
@ -141,7 +143,7 @@ const NotificationItem: Component<NotificationItemProps> = (props) => {
}); });
return ( return (
<div class={styles.notifItem}> <div id={props.id} class={styles.notifItem}>
<div class={styles.notifType}> <div class={styles.notifType}>
<img src={typeIcon()} alt="notification icon" /> <img src={typeIcon()} alt="notification icon" />
<div class={styles.iconInfo} title={props.iconTooltip}> <div class={styles.iconInfo} title={props.iconTooltip}>
@ -157,7 +159,7 @@ const NotificationItem: Component<NotificationItemProps> = (props) => {
href={`/p/${user.npub}`} class={styles.avatar} href={`/p/${user.npub}`} class={styles.avatar}
title={userName(user)} title={userName(user)}
> >
<Avatar src={user.picture} size="xs" /> <Avatar user={user} size="xs" />
</A> </A>
)} )}
</For> </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 NotificationNote from '../Note/NotificationNote/NotificationNote';
import { truncateNumber } from '../../lib/notifications'; import { truncateNumber } from '../../lib/notifications';
import { notificationsOld as t } from '../../translations'; import { notificationsOld as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const typeIcons: Record<string, string> = { const typeIcons: Record<string, string> = {
[NotificationType.NEW_USER_FOLLOWED_YOU]: userFollow, [NotificationType.NEW_USER_FOLLOWED_YOU]: userFollow,
@ -58,6 +59,7 @@ const typeIcons: Record<string, string> = {
} }
type NotificationItemProps = { type NotificationItemProps = {
id?: string,
notes: PrimalNote[], notes: PrimalNote[],
users: Record<string, PrimalUser>, users: Record<string, PrimalUser>,
userStats: Record<string, { followers_count: number }>, userStats: Record<string, { followers_count: number }>,
@ -114,7 +116,7 @@ const NotificationItem2: Component<NotificationItemProps> = (props) => {
return ( return (
<div class={styles.notifItem}> <div id={props.id} class={styles.notifItem}>
<div class={styles.notifType}> <div class={styles.notifType}>
<img src={typeIcon()} alt="notification icon" /> <img src={typeIcon()} alt="notification icon" />
<Show when={isZapType()}> <Show when={isZapType()}>
@ -129,7 +131,7 @@ const NotificationItem2: Component<NotificationItemProps> = (props) => {
href={`/p/${user()?.npub}`} class={styles.avatar} href={`/p/${user()?.npub}`} class={styles.avatar}
title={userName(user())} title={userName(user())}
> >
<Avatar src={user()?.picture} size="xs" /> <Avatar user={user()} size="xs" />
</A> </A>
</div> </div>
<div class={styles.description}> <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 { notificationsSidebar as t } from '../../translations';
import styles from './NotificationsSidebar.module.scss'; import styles from './NotificationsSidebar.module.scss';
import { hookForDev } from '../../lib/devTools';
const uniqueifyUsers = (users: PrimalNotifUser[]) => { const uniqueifyUsers = (users: PrimalNotifUser[]) => {
return users.reduce<PrimalNotifUser[]>((acc, u) => { return users.reduce<PrimalNotifUser[]>((acc, u) => {
@ -15,6 +16,7 @@ const uniqueifyUsers = (users: PrimalNotifUser[]) => {
} }
const NotificationsSidebar: Component<{ const NotificationsSidebar: Component<{
id?: string,
notifications: SortedNotifications, notifications: SortedNotifications,
getUsers: (notifs: PrimalNotification[], type: NotificationType) => PrimalNotifUser[], getUsers: (notifs: PrimalNotification[], type: NotificationType) => PrimalNotifUser[],
}> = (props) => { }> = (props) => {
@ -29,8 +31,6 @@ const NotificationsSidebar: Component<{
const lost = props.getUsers(unffolowNotifs, NotificationType.USER_UNFOLLOWED_YOU); const lost = props.getUsers(unffolowNotifs, NotificationType.USER_UNFOLLOWED_YOU);
return [uniqueifyUsers(followers).length, uniqueifyUsers(lost).length]; return [uniqueifyUsers(followers).length, uniqueifyUsers(lost).length];
}; };
const mentions = () => { const mentions = () => {
@ -158,7 +158,7 @@ const NotificationsSidebar: Component<{
} }
return ( return (
<> <div id={props.id}>
<div class={styles.sidebarHeading}> <div class={styles.sidebarHeading}>
{intl.formatMessage(t.heading)} {intl.formatMessage(t.heading)}
</div> </div>
@ -368,8 +368,8 @@ const NotificationsSidebar: Component<{
</div> </div>
</div> </div>
</Show> </Show>
</> </div>
) )
} }
export default NotificationsSidebar; export default hookForDev(NotificationsSidebar);

View File

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

View File

@ -1,8 +1,9 @@
import type { Component } from 'solid-js'; import type { Component } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import styles from './PageNav.module.scss'; import styles from './PageNav.module.scss';
const PageNav: Component = () => { const PageNav: Component<{ id?: string }> = (props) => {
const onBack = () => { const onBack = () => {
window.history.back(); window.history.back();
@ -13,13 +14,13 @@ const PageNav: Component = () => {
} }
return ( return (
<> <div id={props.id}>
<button onClick={onBack} class={styles.backIcon}> <button onClick={onBack} class={styles.backIcon}>
</button> </button>
<button onClick={onNext} class={styles.forwardIcon}> <button onClick={onNext} class={styles.forwardIcon}>
</button> </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"; import styles from "./Paginator.module.scss";
export default function Paginator(props: { const Paginator: Component<{
id?: string,
loadNextPage: (() => void) | undefined, loadNextPage: (() => void) | undefined,
isSmall?: boolean, isSmall?: boolean,
}) { }> = (props) => {
let observer: IntersectionObserver | undefined; let observer: IntersectionObserver | undefined;
let trigger: HTMLDivElement | undefined;
onMount(() => { onMount(() => {
observer = new IntersectionObserver(entries => { observer = new IntersectionObserver(entries => {
@ -28,7 +31,9 @@ export default function Paginator(props: {
}); });
return ( 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> </div>
) )
} }
export default hookForDev(Paginator);

View File

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

View File

@ -1,5 +1,6 @@
import { A } from '@solidjs/router'; import { A } from '@solidjs/router';
import { Component, For, Show } from 'solid-js'; import { Component, For, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import { authorName, nip05Verification, truncateNpub } from '../../stores/profile'; import { authorName, nip05Verification, truncateNpub } from '../../stores/profile';
import { PrimalUser } from '../../types/primal'; import { PrimalUser } from '../../types/primal';
import Avatar from '../Avatar/Avatar'; import Avatar from '../Avatar/Avatar';
@ -8,11 +9,11 @@ import FollowButton from '../FollowButton/FollowButton';
import styles from './PeopleList.module.scss'; 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; const people = () => props.people;
return ( return (
<div id="trending_wrapper" class={styles.stickyWrapper}> <div id={props.id} class={styles.stickyWrapper}>
<div class={styles.heading}>{props.label}</div> <div class={styles.heading}>{props.label}</div>
<div id="trending_section" class={styles.trendingSection}> <div id="trending_section" class={styles.trendingSection}>
<For each={people()}> <For each={people()}>
@ -21,7 +22,6 @@ const PeopleList: Component<{ people: PrimalUser[], label: string}> = (props) =>
<A href={`/p/${person?.npub}`} class={styles.peopleList}> <A href={`/p/${person?.npub}`} class={styles.peopleList}>
<div class={styles.avatar}> <div class={styles.avatar}>
<Avatar <Avatar
src={person?.picture}
size="md" size="md"
user={person} 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"; import styles from "./PostButton.module.scss";
export default function PostButton() { const PostButton: Component< {id?: string } > = (props) => {
const showPostForm = () => {}; const showPostForm = () => {};
return ( return (
<button <button
id={props.id}
class={styles.postButton} class={styles.postButton}
onClick={showPostForm} onClick={showPostForm}
> >
@ -13,3 +16,5 @@ export default function PostButton() {
</button> </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 { MenuItem } from "../../types/primal";
import styles from "./PrimalMenu.module.scss"; import styles from "./PrimalMenu.module.scss";
import PrimalMenuItem from "./PrimalMenuItem"; import PrimalMenuItem from "./PrimalMenuItem";
export default function PrimalMenu(props: { const PrimalMenu: Component<{
id: string, id: string,
items: MenuItem[], items: MenuItem[],
position?: 'note_footer' | 'profile', position?: 'note_footer' | 'profile',
reverse?: boolean reverse?: boolean,
}) { }> = (props) => {
const positionClass = () => { const positionClass = () => {
if (props.position == 'note_footer') { if (props.position == 'note_footer') {
@ -36,3 +37,5 @@ export default function PrimalMenu(props: {
</div> </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 { MenuItem } from "../../types/primal";
import styles from "./PrimalMenuItem.module.scss"; import styles from "./PrimalMenuItem.module.scss";
export default function PrimalMenuItem(props: { const PrimalMenuItem: Component<{
item: MenuItem, item: MenuItem,
reverse?: boolean, reverse?: boolean,
}) { id?: string,
}> = (props) => {
const [icon, setIcon] = createSignal<string>() const [icon, setIcon] = createSignal<string>()
@ -28,6 +30,7 @@ export default function PrimalMenuItem(props: {
return ( return (
<button <button
id={props.id}
onClick={(e: MouseEvent) => { onClick={(e: MouseEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -44,3 +47,5 @@ export default function PrimalMenuItem(props: {
</button> </button>
) )
} }
export default hookForDev(PrimalMenuItem);

View File

@ -9,39 +9,46 @@ import SmallNote from '../SmallNote/SmallNote';
import { useIntl } from '@cookbook/solid-intl'; import { useIntl } from '@cookbook/solid-intl';
import { userName } from '../../stores/profile'; import { userName } from '../../stores/profile';
import { profile as t } from '../../translations'; 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(); const intl = useIntl();
return ( return (
<Show when={props.profile}> <div id={props.id}>
<div class={styles.headingTrending}> <Show when={props.profile}>
<div> <div class={styles.headingTrending}>
{intl.formatMessage(t.sidebarCaption)} <div>
</div> {intl.formatMessage(t.sidebarCaption)}
</div>
<Show
when={props.notes && props.notes.length > 0}
fallback={
<div class={styles.noNotes}>
{intl.formatMessage(
t.sidebarNoNotes,
{
name: userName(props.profile),
},
)}
</div> </div>
} </div>
>
<For each={props.notes}> <Show
{(note) => <SmallNote note={note} />} when={props.notes && props.notes.length > 0}
</For> 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>
</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 { hexToNpub } from '../../lib/keys';
import styles from './ProfileWidget.module.scss'; import styles from './ProfileWidget.module.scss';
import { hookForDev } from '../../lib/devTools';
const ProfileWidget: Component = () => { const ProfileWidget: Component<{ id?: string }> = (props) => {
const account = useAccountContext() const account = useAccountContext()
const activeUser = () => account?.activeUser; const activeUser = () => account?.activeUser;
return ( return (
<div> <div id={props.id}>
<Show when={account?.hasPublicKey()}> <Show when={account?.hasPublicKey()}>
<A href="/profile" class={styles.userProfile}> <A href="/profile" class={styles.userProfile}>
<div class={styles.avatar}> <div class={styles.avatar}>
<Avatar <Avatar
size="vs" size="vs"
src={activeUser()?.picture}
user={activeUser()} user={activeUser()}
/> />
</div> </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 { useIntl } from "@cookbook/solid-intl";
import { Component, createEffect, createSignal, Show } from "solid-js"; import { Component, createEffect, createSignal, Show } from "solid-js";
import { useAccountContext } from "../../contexts/AccountContext"; import { useAccountContext } from "../../contexts/AccountContext";
import { hookForDev } from "../../lib/devTools";
import { userName } from "../../stores/profile"; import { userName } from "../../stores/profile";
import { actions as t } from "../../translations"; import { actions as t } from "../../translations";
import { PrimalNote, SendNoteResult } from "../../types/primal"; import { PrimalNote, SendNoteResult } from "../../types/primal";
@ -8,39 +9,12 @@ import Avatar from "../Avatar/Avatar";
import EditBox from "../NewNote/EditBox/EditBox"; import EditBox from "../NewNote/EditBox/EditBox";
import styles from "./ReplyToNote.module.scss"; import styles from "./ReplyToNote.module.scss";
type AutoSizedTextArea = HTMLTextAreaElement & { _baseScrollHeight: number };
const getScrollHeight = (elm: AutoSizedTextArea) => { const ReplyToNote: Component<{
var savedValue = elm.value note: PrimalNote,
elm.value = '' onNotePosted?: (note: SendNoteResult) => void,
elm._baseScrollHeight = elm.scrollHeight id?: string,
elm.value = savedValue }> = (props) => {
}
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 intl = useIntl(); const intl = useIntl();
@ -81,57 +55,57 @@ const ReplyToNote: Component<{ note: PrimalNote, onNotePosted?: (note: SendNoteR
}); });
return ( return (
<Show <div id={props.id}>
when={open()} <Show
fallback={ when={open()}
<button class={styles.replyBox} onClick={openReplyBox}> fallback={
<div class={styles.leftSideClosed}> <button class={styles.replyBox} onClick={openReplyBox}>
<Avatar <div class={styles.leftSideClosed}>
src={activeUser()?.picture} <Avatar
size="md" size="md"
user={activeUser()} user={activeUser()}
/> />
</div> </div>
<div class={styles.rightSideClosed}> <div class={styles.rightSideClosed}>
<div class={styles.border}> <div class={styles.border}>
<div <div
class={styles.input} class={styles.input}
> >
<span> <span>
{intl.formatMessage( {intl.formatMessage(
t.noteReply, t.noteReply,
{ {
name: userName(props.note.user), name: userName(props.note.user),
}, },
)} )}
</span> </span>
</div>
</div> </div>
</div> </div>
</div> </button>
</button> }
} >
> <div class={styles.newNoteBorder}>
<div class={styles.newNoteBorder}> <div class={styles.newNote}>
<div class={styles.newNote}> <div class={styles.leftSide}>
<div class={styles.leftSide}> <Avatar
<Avatar size="md"
src={activeUser()?.picture} user={activeUser()}
size="md" />
user={activeUser()} </div>
/> <div class={styles.rightSide}>
</div> <EditBox
<div class={styles.rightSide}> idPrefix="reply_"
<EditBox replyToNote={props.note}
idPrefix="reply_" onClose={closeReplyToNote}
replyToNote={props.note} onSuccess={props.onNotePosted}
onClose={closeReplyToNote} />
onSuccess={props.onNotePosted} </div>
/>
</div> </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 styles from './Search.module.scss';
import SearchOption from './SearchOption'; import SearchOption from './SearchOption';
import { hookForDev } from '../../lib/devTools';
const Search: Component<{ const Search: Component<{
@ -20,6 +21,7 @@ const Search: Component<{
noLinks?: boolean, noLinks?: boolean,
hideDefault?: boolean, hideDefault?: boolean,
placeholder?: string, placeholder?: string,
id?: string,
}> = (props) => { }> = (props) => {
const toaster = useToastContext(); const toaster = useToastContext();
@ -113,7 +115,7 @@ const Search: Component<{
}); });
return ( return (
<div class={styles.searchHolder}> <div id={props.id} class={styles.searchHolder}>
<form <form
class={styles.search} class={styles.search}
onsubmit={onSearch} onsubmit={onSearch}
@ -174,7 +176,7 @@ const Search: Component<{
href={props.noLinks ? undefined : `/p/${user.npub}`} href={props.noLinks ? undefined : `/p/${user.npub}`}
title={userName(user)} title={userName(user)}
description={nip05Verification(user)} description={nip05Verification(user)}
icon={<Avatar src={user.picture} size="xs" />} icon={<Avatar user={user} size="xs" />}
statNumber={search?.scores[user.pubkey]} statNumber={search?.scores[user.pubkey]}
statLabel={intl.formatMessage(t.followers)} statLabel={intl.formatMessage(t.followers)}
onClick={() => selectUser(user)} onClick={() => selectUser(user)}
@ -184,8 +186,7 @@ const Search: Component<{
</div> </div>
</Show> </Show>
</div> </div>
) )
} }
export default Search; export default hookForDev(Search);

View File

@ -1,5 +1,6 @@
import { A } from '@solidjs/router'; import { A } from '@solidjs/router';
import { Component, JSXElement, Show } from 'solid-js'; import { Component, JSXElement, Show } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import { truncateNumber } from '../../lib/notifications'; import { truncateNumber } from '../../lib/notifications';
import { truncateName, } from '../../stores/profile'; import { truncateName, } from '../../stores/profile';
@ -17,6 +18,7 @@ const SearchOption: Component<{
underline?: boolean, underline?: boolean,
onClick?: (e?: MouseEvent) => void, onClick?: (e?: MouseEvent) => void,
highlighted?: boolean, highlighted?: boolean,
id?: string,
}> = (props) => { }> = (props) => {
const Content: Component<{ children: JSXElement }> = (prp) => { const Content: Component<{ children: JSXElement }> = (prp) => {
@ -29,6 +31,7 @@ const SearchOption: Component<{
when={props.href} when={props.href}
fallback={ fallback={
<div <div
id={props.id}
class={klass()} class={klass()}
onClick={props.onClick} onClick={props.onClick}
> >
@ -37,6 +40,7 @@ const SearchOption: Component<{
} }
> >
<A <A
id={props.id}
href={props.href || ''} href={props.href || ''}
class={klass()} class={klass()}
tabIndex={0} 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 default styles. (All examples use this via a global import)
import "@thisbeyond/solid-select/style.css"; import "@thisbeyond/solid-select/style.css";
import { Component } from "solid-js"; import { Component } from "solid-js";
import { hookForDev } from "../../lib/devTools";
import { placeholders } from "../../translations"; import { placeholders } from "../../translations";
import { FeedOption } from "../../types/primal"; import { FeedOption } from "../../types/primal";
// Apply custom styling. See stylesheet below. // Apply custom styling. See stylesheet below.
import "./SelectBox.scss"; 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(); const intl = useIntl();
@ -28,6 +36,7 @@ const SelectBox: Component<{ options: () => FeedOption[], onChange: (value: any)
return ( return (
<Select <Select
id={props.id}
class={props.isPhone ? "phone_feed_select" : "feed_select"} class={props.isPhone ? "phone_feed_select" : "feed_select"}
initialValue={props.initialValue} initialValue={props.initialValue}
onChange={props.onChange} 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 { useSettingsContext } from '../../contexts/SettingsContext';
import { useIntl } from '@cookbook/solid-intl'; import { useIntl } from '@cookbook/solid-intl';
import Checkbox from '../Checkbox/Checkbox'; import Checkbox from '../Checkbox/Checkbox';
import { hookForDev } from '../../lib/devTools';
const SettingsNotifications: Component = () => { const SettingsNotifications: Component<{ id?: string }> = (props) => {
const settings = useSettingsContext(); const settings = useSettingsContext();
const intl = useIntl(); const intl = useIntl();
@ -109,7 +110,7 @@ const SettingsNotifications: Component = () => {
} }
return ( return (
<div class={styles.notificationSettings}> <div id={props.id} class={styles.notificationSettings}>
<div class={styles.caption}> <div class={styles.caption}>
{intl.formatMessage(t.notifications.core)} {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 styles from './SettingsSidebar.module.scss';
import { cacheServer, isConnected, socket } from '../../sockets'; import { cacheServer, isConnected, socket } from '../../sockets';
import { hookForDev } from '../../lib/devTools';
const SettingsSidebar: Component = () => { const SettingsSidebar: Component<{ id?: string }> = (props) => {
const intl = useIntl(); const intl = useIntl();
const account = useAccountContext(); const account = useAccountContext();
@ -27,7 +28,7 @@ const SettingsSidebar: Component = () => {
}; };
return ( return (
<> <div id={props.id}>
<div class={styles.headingConnectedRelays}> <div class={styles.headingConnectedRelays}>
<div> <div>
{intl.formatMessage(t.relays)} {intl.formatMessage(t.relays)}
@ -72,8 +73,8 @@ const SettingsSidebar: Component = () => {
{socket()?.url || cacheServer} {socket()?.url || cacheServer}
</span> </span>
</div> </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 { useIntl } from '@cookbook/solid-intl';
import ConfirmModal from '../ConfirmModal/ConfirmModal'; import ConfirmModal from '../ConfirmModal/ConfirmModal';
import { settings as t } from '../../translations'; import { settings as t } from '../../translations';
import { hookForDev } from '../../lib/devTools';
const SettingsZap: Component = () => { const SettingsZap: Component<{ id?: string }> = (props) => {
const intl = useIntl(); const intl = useIntl();
const settings = useSettingsContext(); const settings = useSettingsContext();
@ -29,7 +30,6 @@ const SettingsZap: Component = () => {
return; return;
} }
settings?.actions.setDefaultZapAmount(val); settings?.actions.setDefaultZapAmount(val);
}, 500) }, 500)
}; };
@ -48,7 +48,7 @@ const SettingsZap: Component = () => {
}; };
return ( return (
<div class={styles.zapSettings}> <div id={props.id} class={styles.zapSettings}>
<div class={styles.defaultZaps}> <div class={styles.defaultZaps}>
<div class={styles.caption}> <div class={styles.caption}>
Set default zap amount: 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 { PrimalUser } from '../../types/primal';
import { placeholders } from '../../translations'; import { placeholders } from '../../translations';
import { useIntl } from '@cookbook/solid-intl'; 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 account = useAccountContext();
const intl = useIntl(); const intl = useIntl();
@ -17,9 +18,9 @@ const SmallCallToAction: Component<{ activeUser: PrimalUser | undefined }> = (pa
}; };
return ( return (
<button class={styles.callToAction} onClick={showNewNoteForm}> <button id={props.id} class={styles.callToAction} onClick={showNewNoteForm}>
<Avatar <Avatar
src={params.activeUser?.picture} user={props.activeUser}
size="xs" 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 { authorName } from '../../stores/profile';
import { note as t } from '../../translations'; import { note as t } from '../../translations';
import { useIntl } from '@cookbook/solid-intl'; 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 threadContext = useThreadContext();
const intl = useIntl(); const intl = useIntl();
@ -71,37 +72,35 @@ const SmallNote: Component<{ note: PrimalNote, children?: JSXElement }> = (props
}; };
return ( return (
<div> <div id={props.id} class={styles.smallNote}>
<div class={styles.smallNote}> <A href={`/p/${props.note.user.npub}`} class={styles.avatar}>
<A href={`/p/${props.note.user.npub}`} class={styles.avatar}> <Avatar user={props.note.user} size="xxs" />
<Avatar src={props.note.user?.picture} size="xxs" /> </A>
</A> <A
<A href={`/e/${props.note.post.noteId}`}
href={`/e/${props.note.post.noteId}`} class={styles.content}
class={styles.content} onClick={() => navToThread(props.note)}
onClick={() => navToThread(props.note)} >
> <div class={styles.header}>
<div class={styles.header}> <div class={styles.name} title={nameOfAuthor()}>
<div class={styles.name} title={nameOfAuthor()}> {nameOfAuthor()}
{nameOfAuthor()}
</div>
<div class={styles.time}>
<Show
when={props.children}
fallback={date(props.note.post?.created_at).label}
>
{props.children}
</Show>
</div>
</div> </div>
<div class={styles.message}> <div class={styles.time}>
<div innerHTML={parsedContent(props.note.post.content)}> <Show
</div> when={props.children}
fallback={date(props.note.post?.created_at).label}
>
{props.children}
</Show>
</div> </div>
</A> </div>
</div> <div class={styles.message}>
<div innerHTML={parsedContent(props.note.post.content)}>
</div>
</div>
</A>
</div> </div>
); );
} }
export default SmallNote; export default hookForDev(SmallNote);

View File

@ -1,16 +1,17 @@
import { Component, JSXElement } from 'solid-js'; import { Component, JSXElement } from 'solid-js';
import { hookForDev } from '../../lib/devTools';
import Wormhole from '../Wormhole/Wormhole'; import Wormhole from '../Wormhole/Wormhole';
import styles from './StickySidebar.module.scss'; import styles from './StickySidebar.module.scss';
const StickySidebar: Component<{ children: JSXElement }> = (props) => { const StickySidebar: Component<{ children: JSXElement, id?: string }> = (props) => {
return ( return (
<Wormhole <Wormhole
to="right_sidebar" to="right_sidebar"
> >
<div id="trending_wrapper" class={styles.stickyWrapper}> <div id={props.id} class={styles.stickyWrapper}>
<div id="trending_section" class={styles.trendingSection}> <div class={styles.trendingSection}>
{props.children} {props.children}
</div> </div>
</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 ThemeOption from './ThemeOption/ThemeOption';
import { useSettingsContext } from '../../contexts/SettingsContext'; import { useSettingsContext } from '../../contexts/SettingsContext';
import { PrimalTheme } from '../../types/primal'; import { PrimalTheme } from '../../types/primal';
import { hookForDev } from '../../lib/devTools';
const ThemeChooser: Component = () => { const ThemeChooser: Component<{ id?: string }> = (props) => {
const settings = useSettingsContext(); const settings = useSettingsContext();
@ -14,7 +15,7 @@ const ThemeChooser: Component = () => {
}; };
return ( return (
<div class={styles.themeChooser}> <div id={props.id} class={styles.themeChooser}>
<For each={settings?.themes}> <For each={settings?.themes}>
{(theme) => ( {(theme) => (
<ThemeOption <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 check from '../../../assets/icons/check.svg';
import { PrimalTheme } from '../../../types/primal'; import { PrimalTheme } from '../../../types/primal';
import { hookForDev } from '../../../lib/devTools';
const ThemeOption: Component<{ const ThemeOption: Component<{
theme: PrimalTheme, theme: PrimalTheme,
isSelected: boolean, isSelected: boolean,
onSelect: (value: PrimalTheme) => void, onSelect: (value: PrimalTheme) => void,
id?: string,
}> = (props) => { }> = (props) => {
const selectedClass = () => { const selectedClass = () => {
@ -19,7 +21,7 @@ const ThemeOption: Component<{
} }
return ( return (
<div class={styles.themeOption}> <div id={props.id} class={styles.themeOption}>
<button <button
class={`${styles[props.theme.name]} ${selectedClass()}`} class={`${styles[props.theme.name]} ${selectedClass()}`}
onClick={() => props.onSelect(props.theme)} 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 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 { PrimalUser } from "../../types/primal";
import { isAccountVerified } from "../../lib/profile"; 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); const [isVerified, setIsVerified] = createSignal(true);
@ -32,28 +33,22 @@ const VerificationCheck: Component<{ user: PrimalUser | undefined }> = (props) =
}) })
return ( return (
<Show <div id={props.id} data-user={props.user?.pubkey} class={styles.verificationIcon}>
when={isVerified()}
fallback={
<div class={styles.verificationIcon}>
</div>
}
>
<Show <Show
when={isVerifiedByPrimal()} when={isVerified()}
fallback={
<div class={styles.verificationIcon}>
<span class={styles.verifiedIcon} />
</div>
}
> >
<div class={styles.verificationIcon}> <Show
when={isVerifiedByPrimal()}
fallback={
<span class={styles.verifiedIcon} />
}
>
<span class={styles.whiteCheck} /> <span class={styles.whiteCheck} />
<span class={styles.verifiedIconPrimal} /> <span class={styles.verifiedIconPrimal} />
</div> </Show>
</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'); const isImage = url.includes('.jpg')|| url.includes('.jpeg')|| url.includes('.webp') || url.includes('.png') || url.includes('.gif') || url.includes('format=png');
if (isImage) { if (isImage) {
const dev = JSON.parse(localStorage.getItem('devMode') || 'false'); const dev = localStorage.getItem('devMode') === 'true';
let imgUrl = getMediaUrl && getMediaUrl(url); let imgUrl = getMediaUrl && getMediaUrl(url);
if (!imgUrl) { if (!imgUrl) {

View File

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

View File

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

View File

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

View File

@ -165,7 +165,7 @@ const Mutelist: Component = () => {
} }
> >
<Link class={styles.userInfo} href={`/p/${user(pubkey).npub}`}> <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.userName}>
<div class={styles.title}>{userName(user(pubkey))}</div> <div class={styles.title}>{userName(user(pubkey))}</div>
<div class={styles.verification}>{nip05Verification(user(pubkey))}</div> <div class={styles.verification}>{nip05Verification(user(pubkey))}</div>

View File

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

View File

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

View File

@ -97,7 +97,7 @@ const Muted: Component = () => {
} }
> >
<Link class={styles.userInfo} href={`/p/${user(pubkey).npub}`}> <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.userName}>
<div class={styles.title}>{userName(user(pubkey))}</div> <div class={styles.title}>{userName(user(pubkey))}</div>
<div class={styles.verification}>{nip05Verification(user(pubkey))}</div> <div class={styles.verification}>{nip05Verification(user(pubkey))}</div>

View File

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

View File

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