mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-09-30 00:41:09 +00:00
Add Follows step to create Account
This commit is contained in:
parent
13de3e001a
commit
dbfbebcb7e
@ -13,6 +13,7 @@ import styles from './Buttons.module.scss';
|
|||||||
const ButtonFollow: Component<{
|
const ButtonFollow: Component<{
|
||||||
person: PrimalUser | undefined,
|
person: PrimalUser | undefined,
|
||||||
id?: string,
|
id?: string,
|
||||||
|
onFollow?: (person: PrimalUser | undefined) => void,
|
||||||
then?: (remove: boolean, pubkey: string) => void,
|
then?: (remove: boolean, pubkey: string) => void,
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
|
|
||||||
@ -28,6 +29,12 @@ const ButtonFollow: Component<{
|
|||||||
|
|
||||||
const onFollow = (e: MouseEvent) => {
|
const onFollow = (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (props.onFollow) {
|
||||||
|
props.onFollow(props.person);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!account || !account.hasPublicKey() || !props.person) {
|
if (!account || !account.hasPublicKey() || !props.person) {
|
||||||
toast?.sendWarning(intl.formatMessage(t.needToLogin))
|
toast?.sendWarning(intl.formatMessage(t.needToLogin))
|
||||||
return;
|
return;
|
||||||
|
26
src/components/Buttons/ButtonTertiary.tsx
Normal file
26
src/components/Buttons/ButtonTertiary.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Button } from '@kobalte/core';
|
||||||
|
import { Component, JSXElement } from 'solid-js';
|
||||||
|
import { hookForDev } from '../../lib/devTools';
|
||||||
|
|
||||||
|
import styles from './Buttons.module.scss';
|
||||||
|
|
||||||
|
|
||||||
|
const ButtonTertiary: Component<{
|
||||||
|
id?: string,
|
||||||
|
onClick?: (e: MouseEvent) => void,
|
||||||
|
children?: JSXElement,
|
||||||
|
disabled?: boolean,
|
||||||
|
}> = (props) => {
|
||||||
|
return (
|
||||||
|
<Button.Root
|
||||||
|
id={props.id}
|
||||||
|
class={styles.tertiary}
|
||||||
|
onClick={props.onClick}
|
||||||
|
disabled={props.disabled}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</Button.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default hookForDev(ButtonTertiary);
|
@ -33,6 +33,8 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
padding-inline: 10px;
|
||||||
|
padding-block: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,3 +189,20 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tertiary {
|
||||||
|
border-radius: 6px;
|
||||||
|
padding-inline: 10px;
|
||||||
|
padding-block: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
background: var(--background-card);
|
||||||
|
border: 1px solid var(--subtile-devider);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 1px solid var(--text-tertiary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,9 +26,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.leftContent {
|
.leftContent {
|
||||||
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 18px);
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--background-site);
|
||||||
|
opacity: 0.8;
|
||||||
|
z-index: var(--z-index-overlay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.leftFooter {
|
.leftFooter {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, createEffect, createSignal, onCleanup, onMount } from 'solid-js';
|
import { Component, createEffect, createSignal, onCleanup, onMount, Show } from 'solid-js';
|
||||||
|
|
||||||
import styles from './Layout.module.scss';
|
import styles from './Layout.module.scss';
|
||||||
|
|
||||||
@ -47,6 +47,7 @@ const Layout: Component = () => {
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
window.addEventListener('resize', onResize);
|
window.addEventListener('resize', onResize);
|
||||||
|
console.log('OVERLAY: ', location)
|
||||||
});
|
});
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
@ -100,6 +101,9 @@ const Layout: Component = () => {
|
|||||||
|
|
||||||
<div class={styles.leftContent}>
|
<div class={styles.leftContent}>
|
||||||
<NavMenu />
|
<NavMenu />
|
||||||
|
<Show when={location.pathname === '/new'}>
|
||||||
|
<div class={styles.overlay}></div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={styles.leftFooter}>
|
<div class={styles.leftFooter}>
|
||||||
|
@ -128,6 +128,11 @@ export function AccountProvider(props: { children: JSXElement }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setSec = (sec: string | undefined) => {
|
const setSec = (sec: string | undefined) => {
|
||||||
|
if (!sec) {
|
||||||
|
logout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const decoded = nip19.decode(sec);
|
const decoded = nip19.decode(sec);
|
||||||
|
|
||||||
if (decoded.type === 'nsec' && decoded.data) {
|
if (decoded.type === 'nsec' && decoded.data) {
|
||||||
|
@ -290,6 +290,7 @@
|
|||||||
--z-index-lifted: 10;
|
--z-index-lifted: 10;
|
||||||
--z-index-header: 20;
|
--z-index-header: 20;
|
||||||
--z-index-floater: 30;
|
--z-index-floater: 30;
|
||||||
|
--z-index-overlay: 40;
|
||||||
|
|
||||||
--sidebar-section-icon-gradient: linear-gradient(175.11deg, #FA9A43 6.94%, #FA4343 29.79%, #5B12A4 97.76%), linear-gradient(170.29deg, #CCCCCC 12.73%, #808080 94.98%), #D9D9D9;
|
--sidebar-section-icon-gradient: linear-gradient(175.11deg, #FA9A43 6.94%, #FA4343 29.79%, #5B12A4 97.76%), linear-gradient(170.29deg, #CCCCCC 12.73%, #808080 94.98%), #D9D9D9;
|
||||||
|
|
||||||
|
@ -445,3 +445,65 @@ form {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recomendedFollowsCaption {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 5px 10px 5px;
|
||||||
|
background-color: var(--background-card);
|
||||||
|
border-radius: 8px;
|
||||||
|
height: 54px;
|
||||||
|
.caption {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 16px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-block: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.userToFollow {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding-block: 10px;
|
||||||
|
padding-inline: 8px;
|
||||||
|
border-bottom: 1px solid var(--background-input);
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.nameAndNip05 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.name {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 12px;
|
||||||
|
}
|
||||||
|
.nip05 {
|
||||||
|
color: var(--text-tertiary-2);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
button {
|
||||||
|
width: fit-content;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useIntl } from '@cookbook/solid-intl';
|
import { useIntl } from '@cookbook/solid-intl';
|
||||||
import { useNavigate } from '@solidjs/router';
|
import { useNavigate } from '@solidjs/router';
|
||||||
import { Component, createEffect, createSignal, onMount, Show } from 'solid-js';
|
import { Component, createEffect, createSignal, For, onMount, Show } from 'solid-js';
|
||||||
import { APP_ID } from '../App';
|
import { APP_ID } from '../App';
|
||||||
import Avatar from '../components/Avatar/Avatar';
|
import Avatar from '../components/Avatar/Avatar';
|
||||||
import Loader from '../components/Loader/Loader';
|
import Loader from '../components/Loader/Loader';
|
||||||
@ -12,7 +12,7 @@ import { useAccountContext } from '../contexts/AccountContext';
|
|||||||
import { useMediaContext } from '../contexts/MediaContext';
|
import { useMediaContext } from '../contexts/MediaContext';
|
||||||
import { useProfileContext } from '../contexts/ProfileContext';
|
import { useProfileContext } from '../contexts/ProfileContext';
|
||||||
import { uploadMedia } from '../lib/media';
|
import { uploadMedia } from '../lib/media';
|
||||||
import { getSuggestions, sendProfile } from '../lib/profile';
|
import { getProfileContactList, getSuggestions, getUserProfiles, sendProfile } from '../lib/profile';
|
||||||
import { subscribeTo as uploadSub } from "../uploadSocket";
|
import { subscribeTo as uploadSub } from "../uploadSocket";
|
||||||
import {
|
import {
|
||||||
actions as tActions,
|
actions as tActions,
|
||||||
@ -29,6 +29,12 @@ import { hexToNpub, hexToNsec } from '../lib/keys';
|
|||||||
import { storeSec } from '../lib/localStore';
|
import { storeSec } from '../lib/localStore';
|
||||||
import { getPreConfiguredRelays } from '../lib/relays';
|
import { getPreConfiguredRelays } from '../lib/relays';
|
||||||
import CreatePinModal from '../components/CreatePinModal/CreatePinModal';
|
import CreatePinModal from '../components/CreatePinModal/CreatePinModal';
|
||||||
|
import { useSearchContext } from '../contexts/SearchContext';
|
||||||
|
import ButtonFollow from '../components/Buttons/ButtonFollow';
|
||||||
|
import ButtonTertiary from '../components/Buttons/ButtonTertiary';
|
||||||
|
import { sendContacts } from '../lib/notes';
|
||||||
|
import ButtonSecondary from '../components/Buttons/ButtonSecondary';
|
||||||
|
import { nip05Verification } from '../stores/profile';
|
||||||
|
|
||||||
type AutoSizedTextArea = HTMLTextAreaElement & { _baseScrollHeight: number };
|
type AutoSizedTextArea = HTMLTextAreaElement & { _baseScrollHeight: number };
|
||||||
|
|
||||||
@ -36,6 +42,7 @@ const CreateAccount: Component = () => { const intl = useIntl();
|
|||||||
const profile = useProfileContext();
|
const profile = useProfileContext();
|
||||||
const media = useMediaContext();
|
const media = useMediaContext();
|
||||||
const account = useAccountContext();
|
const account = useAccountContext();
|
||||||
|
const search = useSearchContext();
|
||||||
const toast = useToastContext();
|
const toast = useToastContext();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@ -203,6 +210,8 @@ const CreateAccount: Component = () => { const intl = useIntl();
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pubkey = account.publicKey;
|
||||||
|
|
||||||
const data = new FormData(e.target as HTMLFormElement);
|
const data = new FormData(e.target as HTMLFormElement);
|
||||||
|
|
||||||
const name = data.get('name')?.toString() || '';
|
const name = data.get('name')?.toString() || '';
|
||||||
@ -234,11 +243,21 @@ const CreateAccount: Component = () => { const intl = useIntl();
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const { success } = await sendProfile({ ...metadata }, account.relays, relaySettings);
|
const { success } = await sendProfile({ ...metadata }, account.relays, relaySettings);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
toast?.sendSuccess(intl.formatMessage(tToast.updateProfileSuccess));
|
toast?.sendSuccess(intl.formatMessage(tToast.updateProfileSuccess));
|
||||||
|
pubkey && getUserProfiles([pubkey], `user_profile_${APP_ID}`);
|
||||||
|
|
||||||
|
const tags = followed.map(pk => ['p', pk]);
|
||||||
|
const relayInfo = JSON.stringify(relaySettings);
|
||||||
|
const date = Math.floor((new Date()).getTime() / 1000);
|
||||||
|
|
||||||
|
const { success: succ } = await sendContacts(tags, date, relayInfo, account.relays, relaySettings);
|
||||||
|
|
||||||
|
if (succ) {
|
||||||
|
getProfileContactList(account?.publicKey, `user_contacts_${APP_ID}`);
|
||||||
|
}
|
||||||
|
|
||||||
setShowCreatePin(true);
|
setShowCreatePin(true);
|
||||||
|
|
||||||
@ -289,7 +308,8 @@ const CreateAccount: Component = () => { const intl = useIntl();
|
|||||||
setTempNsec(nsec);
|
setTempNsec(nsec);
|
||||||
|
|
||||||
setCreatedAccount(() => ({ sec: nsec, pubkey }));
|
setCreatedAccount(() => ({ sec: nsec, pubkey }));
|
||||||
getSuggestions()
|
getSuggestions();
|
||||||
|
search?.actions.getRecomendedUsers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -305,6 +325,37 @@ const CreateAccount: Component = () => { const intl = useIntl();
|
|||||||
setShowCreatePin(false);
|
setShowCreatePin(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clearNewAccount = () => {
|
||||||
|
account?.actions.setSec(undefined);
|
||||||
|
setTempNsec(undefined);
|
||||||
|
setCreatedAccount(reconcile({}));
|
||||||
|
navigate('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
const [followed, setFollowed] = createStore<string[]>([])
|
||||||
|
|
||||||
|
const isFollowingAllProminent = () => {
|
||||||
|
return !search?.users.some((u) => !followed.includes(u.pubkey));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFollowProminent = () => {
|
||||||
|
const pubkeys = search?.users.map(u => u.pubkey) || [];
|
||||||
|
setFollowed(() => [ ...pubkeys ]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onUnfollowProminent = () => {
|
||||||
|
setFollowed(() => []);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFollow = (pubkey: string) => {
|
||||||
|
setFollowed(followed.length, () => pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onUnfollow = (pubkey: string) => {
|
||||||
|
const follows = followed.filter(f => f !== pubkey)
|
||||||
|
setFollowed(() => [...follows]);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div class={styles.container}>
|
<div class={styles.container}>
|
||||||
@ -518,8 +569,58 @@ const CreateAccount: Component = () => { const intl = useIntl();
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={currentStep() === 'follow' ? '' : 'invisible'}>
|
<div class={currentStep() === 'follow' ? '' : 'invisible'}>
|
||||||
|
<div class={styles.recomendedFollowsCaption}>
|
||||||
|
<div class={styles.caption}>
|
||||||
|
Prominent Nostriches
|
||||||
|
</div>
|
||||||
|
<div class={styles.action}>
|
||||||
|
|
||||||
|
<Show
|
||||||
|
when={isFollowingAllProminent()}
|
||||||
|
fallback={
|
||||||
|
<ButtonSecondary onClick={onFollowProminent}>
|
||||||
|
<span>Follow All</span>
|
||||||
|
</ButtonSecondary>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ButtonTertiary onClick={onUnfollowProminent}>
|
||||||
|
Unfollow all
|
||||||
|
</ButtonTertiary>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
HERE BE FOLLOWS
|
<For each={search?.users}>
|
||||||
|
{user => (
|
||||||
|
<div class={styles.userToFollow}>
|
||||||
|
<div class={styles.info}>
|
||||||
|
<Avatar user={user} />
|
||||||
|
<div class={styles.nameAndNip05}>
|
||||||
|
<div class={styles.name}>
|
||||||
|
{user.name}
|
||||||
|
</div>
|
||||||
|
<div class={styles.nip05}>
|
||||||
|
{nip05Verification(user)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class={styles.action}>
|
||||||
|
<Show
|
||||||
|
when={followed.includes(user.pubkey)}
|
||||||
|
fallback={
|
||||||
|
<ButtonSecondary onClick={() => onFollow(user.pubkey)}>
|
||||||
|
Follow
|
||||||
|
</ButtonSecondary>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ButtonTertiary onClick={() => onUnfollow(user.pubkey)}>
|
||||||
|
Unfollow
|
||||||
|
</ButtonTertiary>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -561,7 +662,7 @@ const CreateAccount: Component = () => { const intl = useIntl();
|
|||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
class={styles.secondaryButton}
|
class={styles.secondaryButton}
|
||||||
onClick={() => navigate('/profile')}
|
onClick={clearNewAccount}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<span>{intl.formatMessage(tActions.cancel)}</span>
|
<span>{intl.formatMessage(tActions.cancel)}</span>
|
||||||
|
Loading…
Reference in New Issue
Block a user