mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-09-29 00:10:50 +00:00
Fix profile tabs appearance
This commit is contained in:
parent
b777c791c8
commit
718ed55996
@ -38,14 +38,6 @@ const Note: Component<{ note: PrimalNote, id?: string }> = (props) => {
|
||||
|
||||
const [openCustomZap, setOpenCustomZap] = createSignal(false);
|
||||
|
||||
createEffect(() => {
|
||||
const n = props.note;
|
||||
|
||||
console.log('NOTE: ', n);
|
||||
console.log('REPLY: ', n.replyTo);
|
||||
console.log('------------------------')
|
||||
})
|
||||
|
||||
return (
|
||||
<A
|
||||
id={props.id}
|
||||
|
@ -34,7 +34,7 @@
|
||||
top: 48px;
|
||||
left: 0;
|
||||
border-radius: 2px 2px 0px 0px;
|
||||
background: linear-gradient(0deg, #CA079F 0%, #CA079F 100%), linear-gradient(134deg, #EF404A 0%, #5B12A4 100%), #121212;
|
||||
background: var(--accent-1);
|
||||
transition: all 250ms;
|
||||
}
|
||||
|
||||
@ -87,3 +87,8 @@
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
display: block;
|
||||
width: 24px;
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { useIntl } from "@cookbook/solid-intl";
|
||||
import { Tabs } from "@kobalte/core";
|
||||
import { Component, For, Match, onMount, Switch } from "solid-js";
|
||||
import { Component, createEffect, createSignal, For, Match, onMount, Show, Switch } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { profileContactListPage } from "../../constants";
|
||||
import { useAccountContext } from "../../contexts/AccountContext";
|
||||
import { useProfileContext } from "../../contexts/ProfileContext";
|
||||
import { hookForDev } from "../../lib/devTools";
|
||||
import { humanizeNumber } from "../../lib/stats";
|
||||
import { userName } from "../../stores/profile";
|
||||
import { profile as t, actions as tActions } from "../../translations";
|
||||
import { PrimalUser } from "../../types/primal";
|
||||
import Loader from "../Loader/Loader";
|
||||
import Note from "../Note/Note";
|
||||
import Paginator from "../Paginator/Paginator";
|
||||
@ -17,6 +20,7 @@ import styles from "./ProfileTabs.module.scss";
|
||||
|
||||
const ProfileTabs: Component<{
|
||||
id?: string,
|
||||
profile: PrimalUser | undefined,
|
||||
setProfile?: (pk: string) => void,
|
||||
}> = (props) => {
|
||||
|
||||
@ -26,9 +30,8 @@ const ProfileTabs: Component<{
|
||||
|
||||
const addToAllowlist = async () => {
|
||||
const pk = profile?.profileKey;
|
||||
const setP = props.setProfile;
|
||||
if (pk && setP) {
|
||||
account?.actions.addToAllowlist(pk, () => { setP(pk) });
|
||||
if (pk) {
|
||||
account?.actions.addToAllowlist(pk);
|
||||
}
|
||||
};
|
||||
|
||||
@ -46,13 +49,12 @@ const ProfileTabs: Component<{
|
||||
|
||||
const unMuteProfile = () => {
|
||||
const pk = profile?.profileKey;
|
||||
const setP = props.setProfile;
|
||||
|
||||
if (!account || !pk || !setP) {
|
||||
if (!account || !pk) {
|
||||
return;
|
||||
}
|
||||
|
||||
account.actions.removeFromMuteList(pk, () => setP(pk));
|
||||
account.actions.removeFromMuteList(pk);
|
||||
};
|
||||
|
||||
const onContactAction = (remove: boolean, pubkey: string) => {
|
||||
@ -64,40 +66,79 @@ const ProfileTabs: Component<{
|
||||
}
|
||||
};
|
||||
|
||||
const contacts = () => {
|
||||
const cts = [...(profile?.contacts || [])];
|
||||
const [contactsOffset, setContactsOffset] = createSignal(0);
|
||||
const [contacts, setContacts] = createStore<PrimalUser[]>([]);
|
||||
|
||||
createEffect(() => {
|
||||
if (!profile || profile.isFetchingContacts) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cts = [...(profile.contacts || [])];
|
||||
|
||||
cts.sort((a, b) => {
|
||||
const aFollowers = profile?.profileStats[a.pubkey] || 0;
|
||||
const bFollowers = profile?.profileStats[b.pubkey] || 0;
|
||||
const aFollowers: number = profile.profileStats[a.pubkey] || 0;
|
||||
const bFollowers: number = profile.profileStats[b.pubkey] || 0;
|
||||
|
||||
const c = bFollowers >= aFollowers ? 1 : -1;
|
||||
return bFollowers >= aFollowers ? 1 : -1;
|
||||
});
|
||||
|
||||
return c;
|
||||
setContacts((cs) => [ ...cs, ...(cts.slice(contactsOffset(), contactsOffset() + profileContactListPage))]);
|
||||
|
||||
});
|
||||
|
||||
return cts;
|
||||
const loadMoreFollows = () => {
|
||||
setContactsOffset(contactsOffset() + profileContactListPage);
|
||||
}
|
||||
|
||||
const followers = () => {
|
||||
const fls = [...(profile?.followers || [])];
|
||||
|
||||
fls.sort((a, b) => {
|
||||
const aFollowers = profile?.profileStats[a.pubkey] || 0;
|
||||
const bFollowers = profile?.profileStats[b.pubkey] || 0;
|
||||
const [followersOffset, setFollowersOffset] = createSignal(0);
|
||||
const [followers, setFollowers] = createStore<PrimalUser[]>([]);
|
||||
|
||||
const c = bFollowers >= aFollowers ? 1 : -1;
|
||||
createEffect(() => {
|
||||
if (!profile || profile.isFetchingFollowers) {
|
||||
return;
|
||||
}
|
||||
|
||||
return c;
|
||||
const cts = [...(profile.followers || [])];
|
||||
|
||||
cts.sort((a, b) => {
|
||||
const aFollowers: number = profile.profileStats[a.pubkey] || 0;
|
||||
const bFollowers: number = profile.profileStats[b.pubkey] || 0;
|
||||
|
||||
return bFollowers >= aFollowers ? 1 : -1;
|
||||
});
|
||||
|
||||
setFollowers((fs) => [ ...fs, ...(cts.slice(followersOffset(), followersOffset() + profileContactListPage))]);
|
||||
|
||||
});
|
||||
|
||||
return fls;
|
||||
const loadMoreFollowers = () => {
|
||||
setFollowersOffset(followersOffset() + profileContactListPage);
|
||||
}
|
||||
|
||||
const onChangeValue = (value: string) => {
|
||||
if (!props.profile) return;
|
||||
|
||||
switch(value) {
|
||||
case 'notes':
|
||||
profile?.notes.length === 0 &&profile?.actions.fetchNotes(props.profile.pubkey);
|
||||
break;
|
||||
case 'replies':
|
||||
profile?.replies.length === 0 && profile?.actions.fetchReplies(props.profile.pubkey);
|
||||
break;
|
||||
case 'follows':
|
||||
profile?.contacts.length === 0 && profile?.actions.fetchContactList(props.profile.pubkey);
|
||||
break;
|
||||
case 'followers':
|
||||
profile?.followers.length === 0 && profile?.actions.fetchFollowerList(props.profile.pubkey);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tabs.Root aria-label="Main navigation">
|
||||
<Show when={profile && props.profile && profile.fetchedUserStats}>
|
||||
<Tabs.Root onChange={onChangeValue}>
|
||||
<Tabs.List class={styles.profileTabs}>
|
||||
<Tabs.Trigger class={styles.profileTab} value="notes">
|
||||
<div class={styles.stat}>
|
||||
@ -110,6 +151,8 @@ const ProfileTabs: Component<{
|
||||
</div>
|
||||
</Tabs.Trigger>
|
||||
|
||||
<div class={styles.separator}></div>
|
||||
|
||||
<Tabs.Trigger class={styles.profileTab} value="replies">
|
||||
<div class={styles.stat}>
|
||||
<div class={styles.statNumber}>
|
||||
@ -121,6 +164,8 @@ const ProfileTabs: Component<{
|
||||
</div>
|
||||
</Tabs.Trigger>
|
||||
|
||||
<div class={styles.separator}></div>
|
||||
|
||||
<Tabs.Trigger class={styles.profileTab} value="follows">
|
||||
<div class={styles.stat}>
|
||||
<div class={styles.statNumber}>
|
||||
@ -132,6 +177,8 @@ const ProfileTabs: Component<{
|
||||
</div>
|
||||
</Tabs.Trigger>
|
||||
|
||||
<div class={styles.separator}></div>
|
||||
|
||||
<Tabs.Trigger class={styles.profileTab} value="followers">
|
||||
<div class={styles.stat}>
|
||||
<div class={styles.statNumber}>
|
||||
@ -253,7 +300,23 @@ const ProfileTabs: Component<{
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content class={styles.tabContent} value="follows">
|
||||
<For each={contacts()}>
|
||||
<div class={styles.profileNotes}>
|
||||
<Show
|
||||
when={!profile?.isFetchingContacts}
|
||||
fallback={
|
||||
<div style="margin-top: 40px;">
|
||||
<Loader />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<For each={contacts} fallback={
|
||||
<div class={styles.mutedProfile}>
|
||||
{intl.formatMessage(
|
||||
t.noFollows,
|
||||
{ name: profile?.userProfile ? userName(profile?.userProfile) : profile?.profileKey },
|
||||
)}
|
||||
</div>
|
||||
}>
|
||||
{contact =>
|
||||
<div>
|
||||
<ProfileContact
|
||||
@ -263,10 +326,29 @@ const ProfileTabs: Component<{
|
||||
/>
|
||||
</div>}
|
||||
</For>
|
||||
<Paginator loadNextPage={loadMoreFollows}/>
|
||||
</Show>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content class={styles.tabContent} value="followers">
|
||||
<For each={followers()}>
|
||||
<div class={styles.profileNotes}>
|
||||
<Show
|
||||
when={!profile?.isFetchingFollowers}
|
||||
fallback={
|
||||
<div style="margin-top: 40px;">
|
||||
<Loader />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<For each={followers} fallback={
|
||||
<div class={styles.mutedProfile}>
|
||||
{intl.formatMessage(
|
||||
t.noFollowers,
|
||||
{ name: profile?.userProfile ? userName(profile?.userProfile) : profile?.profileKey },
|
||||
)}
|
||||
</div>
|
||||
}>
|
||||
{follower =>
|
||||
<div>
|
||||
<ProfileContact
|
||||
@ -277,8 +359,12 @@ const ProfileTabs: Component<{
|
||||
</div>
|
||||
}
|
||||
</For>
|
||||
<Paginator loadNextPage={loadMoreFollowers}/>
|
||||
</Show>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -299,3 +299,5 @@ export const defaultContentModeration: ContentModeration[] = [
|
||||
export const algoNpub ='npub1tkpg9lyfgy83c4mgrgkrhzl90t732ekzvt73m6658xva88g5rj6qy6ntw4';
|
||||
|
||||
export const specialAlgos = ['primal_spam', 'primal_nsfw'];
|
||||
|
||||
export const profileContactListPage = 50;
|
||||
|
@ -55,6 +55,7 @@ export type ProfileContextStore = {
|
||||
reply_count: number,
|
||||
time_joined: number,
|
||||
},
|
||||
fetchedUserStats: boolean,
|
||||
knownProfiles: VanityProfiles,
|
||||
notes: PrimalNote[],
|
||||
replies: PrimalNote[],
|
||||
@ -172,11 +173,11 @@ export const ProfileProvider = (props: { children: ContextChildren }) => {
|
||||
|
||||
// ACTIONS --------------------------------------
|
||||
|
||||
const addContact = (pubkey: string, source: PrimalUser[]) => {
|
||||
const addContact = (pubkey: string, source: PrimalUser[]) => {
|
||||
const newContact = source.find(c => c.pubkey === pubkey);
|
||||
|
||||
newContact && updateStore('contacts', store.contacts.length, reconcile(newContact));
|
||||
};
|
||||
};
|
||||
|
||||
const removeContact = (pubkey: string) => {
|
||||
const newContacts = store.contacts.filter(c => c.pubkey !== pubkey);
|
||||
@ -248,19 +249,19 @@ const addContact = (pubkey: string, source: PrimalUser[]) => {
|
||||
}
|
||||
});
|
||||
|
||||
updateStore('isFetchingContacts', () => true);
|
||||
|
||||
getProfileContactList(pubkey, subIdContacts);
|
||||
};
|
||||
|
||||
const fetchFollowerList = (pubkey: string | undefined) => {
|
||||
if (!pubkey) return;
|
||||
|
||||
updateStore('isFetchingFollowers', () => true);
|
||||
|
||||
const subIdProfiles = `profile_followers_2_${APP_ID}`;
|
||||
|
||||
const unsubProfiles = subscribeTo(subIdProfiles, (type, _, content) => {
|
||||
if (type === 'EOSE') {
|
||||
updateStore('isFetchingFollowers', () => false);
|
||||
console.log('FETCHING FOLLOWERS DONE: ', store.isFetchingFollowers)
|
||||
unsubProfiles();
|
||||
return;
|
||||
}
|
||||
@ -289,6 +290,10 @@ const addContact = (pubkey: string, source: PrimalUser[]) => {
|
||||
}
|
||||
});
|
||||
|
||||
updateStore('isFetchingFollowers', () => true);
|
||||
|
||||
console.log('FETCHING FOLLOWERS: ', store.isFetchingFollowers)
|
||||
|
||||
getProfileFollowerList(pubkey, subIdProfiles);
|
||||
};
|
||||
|
||||
@ -327,7 +332,7 @@ const addContact = (pubkey: string, source: PrimalUser[]) => {
|
||||
return;
|
||||
}
|
||||
|
||||
updateStore('isFetching', () => true);
|
||||
updateStore('isFetchingReplies', () => true);
|
||||
updateStore('page', () => ({ messages: [], users: {}, postStats: {} }));
|
||||
getUserFeed(account?.publicKey, pubkey, `profile_replies_${APP_ID}`, 'replies', until, limit);
|
||||
}
|
||||
@ -752,6 +757,7 @@ const addContact = (pubkey: string, source: PrimalUser[]) => {
|
||||
updateStore('filterReason', () => null);
|
||||
updateStore('userProfile', () => undefined);
|
||||
updateStore('userStats', () => ({ ...emptyStats }));
|
||||
updateStore('fetchedUserStats', () => false);
|
||||
getUserProfileInfo(profileKey, account?.publicKey, `profile_info_${APP_ID}`);
|
||||
getProfileScoredNotes(profileKey, account?.publicKey, `profile_scored_${APP_ID}`, 10);
|
||||
|
||||
@ -831,6 +837,7 @@ const addContact = (pubkey: string, source: PrimalUser[]) => {
|
||||
const stats = JSON.parse(content.content);
|
||||
|
||||
updateStore('userStats', () => ({ ...stats }));
|
||||
updateStore('fetchedUserStats', () => true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -90,13 +90,8 @@ const Profile: Component = () => {
|
||||
profile?.actions.setProfileKey(hex);
|
||||
|
||||
profile?.actions.clearNotes();
|
||||
profile?.actions.clearReplies();
|
||||
profile?.actions.clearContacts();
|
||||
|
||||
profile?.actions.fetchNotes(hex);
|
||||
profile?.actions.fetchReplies(hex);
|
||||
profile?.actions.fetchContactList(hex);
|
||||
profile?.actions.fetchFollowerList(hex);
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
@ -606,7 +601,7 @@ const Profile: Component = () => {
|
||||
|
||||
</div>
|
||||
|
||||
<ProfileTabs setProfile={setProfile} />
|
||||
<ProfileTabs setProfile={setProfile} profile={profile?.userProfile}/>
|
||||
|
||||
<ConfirmModal
|
||||
open={confirmReportUser()}
|
||||
|
@ -857,6 +857,16 @@ export const profile = {
|
||||
defaultMessage: '{name} hasn\'t posted any notes',
|
||||
description: 'Label indicating that the profile has no notes',
|
||||
},
|
||||
noFollowers: {
|
||||
id: 'profile.noFollowers',
|
||||
defaultMessage: '{name} has no followers',
|
||||
description: 'Label indicating that the profile has no followers',
|
||||
},
|
||||
noFollows: {
|
||||
id: 'profile.noFollows',
|
||||
defaultMessage: 'No one is following {name}',
|
||||
description: 'Label indicating that the profile has no followers',
|
||||
},
|
||||
};
|
||||
|
||||
export const search = {
|
||||
|
Loading…
Reference in New Issue
Block a user