Skip to content

Commit

Permalink
profile fn comp, restore reposts in feeds
Browse files Browse the repository at this point in the history
  • Loading branch information
mmalmi committed Aug 21, 2023
1 parent c27400e commit 1e8b6e2
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 158 deletions.
2 changes: 1 addition & 1 deletion src/js/components/user/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const MyAvatar: React.FC<Props> = (props) => {

return () => unsub?.();
}
}, [hex, props.str, props.width]);
}, [hex, props.str]);

const width = props.width;
const isActive = ['online', 'active'].includes(activity || '');
Expand Down
1 change: 1 addition & 0 deletions src/js/nostr/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const Session = {
},
init: function (options: any) {
Key.getOrCreate(options);
localState.get('isMyProfile').put(false);
localState.get('loggedIn').on(() => this.onLoggedIn());
Helpers.showConsoleWarning();
},
Expand Down
4 changes: 2 additions & 2 deletions src/js/views/feeds/Global.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useMemo } from 'react';
import CreateNoteForm from '@/components/create/CreateNoteForm';
import FeedComponent from '@/components/feed/Feed';
import OnboardingNotification from '@/components/onboarding/OnboardingNotification';
import { getEventReplyingTo } from '@/nostr/utils';
import { getEventReplyingTo, isRepost } from '@/nostr/utils';
import { translate as t } from '@/translations/Translation.mjs';
import { RouteProps } from '@/views/types.ts';
import View from '@/views/View.tsx';
Expand All @@ -14,7 +14,7 @@ const Global: React.FC<RouteProps> = () => {
{
name: t('posts'),
filter: { kinds: [1, 6], limit: 10 },
filterFn: (event) => !getEventReplyingTo(event),
filterFn: (event) => !getEventReplyingTo(event) || isRepost(event),
eventProps: { showRepliedMsg: true },
},
{
Expand Down
4 changes: 2 additions & 2 deletions src/js/views/feeds/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import FeedComponent from '@/components/feed/Feed';
import Show from '@/components/helpers/Show';
import OnboardingNotification from '@/components/onboarding/OnboardingNotification';
import Key from '@/nostr/Key';
import { getEventReplyingTo } from '@/nostr/utils';
import { getEventReplyingTo, isRepost } from '@/nostr/utils';
import { translate as t } from '@/translations/Translation.mjs';
import { ID, STR } from '@/utils/UniqueIds';
import { RouteProps } from '@/views/types.ts';
Expand Down Expand Up @@ -40,7 +40,7 @@ const Home: React.FC<RouteProps> = () => {
{
name: t('posts'),
filter: { kinds: [1, 6], authors: followedUsers, limit: 10 },
filterFn: (event) => !getEventReplyingTo(event),
filterFn: (event) => !getEventReplyingTo(event) || isRepost(event),
eventProps: { showRepliedMsg: true },
},
{
Expand Down
280 changes: 127 additions & 153 deletions src/js/views/profile/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,191 +1,165 @@
import { useMemo } from 'react';
import { useEffect, useState } from 'preact/hooks';
import { route } from 'preact-router';

import BaseComponent from '@/BaseComponent.ts';
import SimpleImageModal from '@/components/modal/Image.tsx';
import { getEventReplyingTo } from '@/nostr/utils.ts';
import { getEventReplyingTo, isRepost } from '@/nostr/utils.ts';
import ProfileHelmet from '@/views/profile/Helmet.tsx';

import Feed from '../../components/feed/Feed.tsx';
import Show from '../../components/helpers/Show.tsx';
import { isSafeOrigin } from '../../components/SafeImg.tsx';
import ProfileCard from '../../components/user/ProfileCard.tsx';
import localState from '../../LocalState.ts';
import { useLocalState } from '../../LocalState.ts';
import Key from '../../nostr/Key.ts';
import SocialNetwork from '../../nostr/SocialNetwork.ts';
import { translate as t } from '../../translations/Translation.mjs';
import View from '../View.tsx';

class Profile extends BaseComponent {
subscriptions: any[];
unsub: any;
state = {
hexPub: '',
npub: '',
name: '',
display_name: '',
profile: {} as any,
banner: '',
fullBanner: '',
picture: '',
website: '',
lightning: '',
blocked: false,
bannerModalOpen: false,
notFound: false,
};

constructor() {
super();
this.subscriptions = [];
}
function Profile(props) {
const [blocked, setBlocked] = useState(false);
const [hexPub, setHexPub] = useState('');
const [npub, setNpub] = useState('');
const [profile, setProfile] = useState({} as any);
const [banner, setBanner] = useState('');
const [bannerModalOpen, setBannerModalOpen] = useState(false);
const setIsMyProfile = useLocalState('isMyProfile', false)[1];

render() {
const {
hexPub,
display_name,
name,
profile,
banner,
picture,
blocked,
bannerModalOpen,
fullBanner,
} = this.state;

if (!hexPub) {
return <div></div>;
}

const title = display_name || name || 'Profile';
const ogTitle = `${title} | Iris`;
const description = `Latest posts by ${display_name || name || 'user'}. ${
profile?.about || ''
}`;
const setBannerModalOpen = (bannerModalOpen) => this.setState({ bannerModalOpen });

return (
<View>
<Show when={banner}>
<div
className="mb-4 h-48 bg-cover bg-center cursor-pointer"
style={{ backgroundImage: `url(${banner})` }}
onClick={() => setBannerModalOpen(true)}
></div>
<Show when={bannerModalOpen}>
<SimpleImageModal imageUrl={fullBanner} onClose={() => setBannerModalOpen(false)} />
</Show>
</Show>
<div>
<ProfileHelmet
title={title}
description={description}
picture={picture}
ogTitle={ogTitle}
/>
<ProfileCard npub={this.state.npub} hexPub={this.state.hexPub} />
<Show when={!blocked}>
<Feed
key={`posts${this.state.hexPub}`}
filterOptions={[
{
name: t('posts'),
filter: { authors: [this.state.hexPub], kinds: [1, 6], limit: 10 },
filterFn: (event) => !getEventReplyingTo(event),
eventProps: { showRepliedMsg: true },
},
{
name: t('posts_and_replies'),
filter: { authors: [this.state.hexPub], kinds: [1, 6], limit: 5 },
eventProps: { showRepliedMsg: true, fullWidth: false },
},
{
name: t('likes'),
filter: { authors: [this.state.hexPub], kinds: [7], limit: 5 },
},
]}
/>
</Show>
</div>
</View>
);
}

componentWillUnmount() {
super.componentWillUnmount();
this.unsub?.();
localState.get('isMyProfile').put(false);
}
useEffect(() => {
const pub = props.id;
const npubComputed = Key.toNostrBech32Address(pub, 'npub');

componentDidUpdate(_prevProps, prevState) {
if (!prevState.name && this.state.name) {
this.unsub?.();
setTimeout(() => {
// important for SEO: prerenderReady is false until page content is loaded
window.prerenderReady = true;
}, 1000); // give feed a sec to load
}
}

loadProfile(hexPub: string) {
const isMyProfile = hexPub === Key.getPubKey();
localState.get('isMyProfile').put(isMyProfile);
this.subscriptions.push(
SocialNetwork.getProfile(hexPub, (profile) => {
let banner, fullBanner;

try {
banner = profile.banner && new URL(profile.banner).toString();
if (!banner) {
return;
}
fullBanner = banner;
banner = isSafeOrigin(banner)
? banner
: `https://imgproxy.iris.to/insecure/rs:fit:948:948/plain/${banner}`;
this.setState({ banner, fullBanner });
} catch (e) {
console.log('Invalid banner URL', profile.banner);
}
}),
);
}

componentDidMount() {
const pub = this.props.id;
const npub = Key.toNostrBech32Address(pub, 'npub');
//localState.get('loggedIn').on(this.inject());
if (npub && npub !== pub) {
route(`/${npub}`, true);
if (npubComputed && npubComputed !== pub) {
route(`/${npubComputed}`, true);
return;
}
const hexPub = Key.toNostrHexAddress(pub);
if (!hexPub) {
// id is not a nostr address, but maybe it's a username

const hexPubComputed = Key.toNostrHexAddress(pub) || '';
if (!hexPubComputed) {
let nostrAddress = pub;
if (!nostrAddress.match(/.+@.+\..+/)) {
// domain name?
if (nostrAddress.match(/.+\..+/)) {
nostrAddress = '_@' + nostrAddress;
} else {
nostrAddress = nostrAddress + '@iris.to';
}
}

Key.getPubKeyByNip05Address(nostrAddress).then((pubKey) => {
if (pubKey) {
const npub = Key.toNostrBech32Address(pubKey, 'npub');
if (npub && npub !== pubKey) {
this.setState({ npub, hexPub: pubKey });
this.loadProfile(pubKey);
const npubComputed = Key.toNostrBech32Address(pubKey, 'npub');
if (npubComputed && npubComputed !== pubKey) {
setNpub(npubComputed);
setHexPub(pubKey);
loadProfile(pubKey);
}
} else {
this.setState({ notFound: true });
setNpub(''); // To indicate not found
}
});
}

setHexPub(hexPubComputed);
setNpub(Key.toNostrBech32Address(hexPubComputed, 'npub') || '');
loadProfile(hexPubComputed);
setTimeout(() => {
window.prerenderReady = true;
}, 1000);
return () => {
setIsMyProfile(false);
};
}, [props.id]);

const filterOptions = useMemo(() => {
return [
{
name: t('posts'),
filter: { authors: [hexPub], kinds: [1, 6], limit: 10 },
filterFn: (event) => !getEventReplyingTo(event) || isRepost(event),
eventProps: { showRepliedMsg: true },
},
{
name: t('posts_and_replies'),
filter: { authors: [hexPub], kinds: [1, 6], limit: 5 },
eventProps: { showRepliedMsg: true, fullWidth: false },
},
{
name: t('likes'),
filter: { authors: [hexPub], kinds: [7], limit: 5 },
},
];
}, [hexPub]);

const loadProfile = (hexPub) => {
if (!hexPub) {
return;
}
this.setState({ hexPub, npub: Key.toNostrBech32Address(hexPub, 'npub') });
this.loadProfile(hexPub);
const isMyProfile = hexPub === Key.getPubKey();
setIsMyProfile(isMyProfile);
SocialNetwork.getBlockedUsers((blockedUsers) => {
setBlocked(blockedUsers.has(hexPub));
});

SocialNetwork.getProfile(hexPub, (profileData) => {
if (!profileData) {
return;
}
setProfile(profileData);
let bannerURL;

try {
bannerURL = profileData.banner && new URL(profileData.banner).toString();
if (!bannerURL) {
return;
}

bannerURL = isSafeOrigin(bannerURL)
? bannerURL
: `https://imgproxy.iris.to/insecure/rs:fit:948:948/plain/${bannerURL}`;

setBanner(bannerURL);
} catch (e) {
console.log('Invalid banner URL', profileData.banner);
}
});
};

if (!hexPub) {
return <div></div>;
}

const title = profile.display_name || profile.name || 'Profile';
const ogTitle = `${title} | Iris`;
const description = `Latest posts by ${profile.display_name || profile.name || 'user'}. ${
profile.about || ''
}`;

return (
<View>
<Show when={banner && !blocked}>
<div
className="mb-4 h-48 bg-cover bg-center cursor-pointer"
style={{ backgroundImage: `url(${banner})` }}
onClick={() => setBannerModalOpen(true)}
></div>
<Show when={bannerModalOpen}>
<SimpleImageModal imageUrl={profile.banner} onClose={() => setBannerModalOpen(false)} />
</Show>
</Show>
<div>
<ProfileHelmet
title={title}
description={description}
picture={profile.picture}
ogTitle={ogTitle}
/>
<ProfileCard npub={npub} hexPub={hexPub} />
<Show when={!blocked}>
<Feed key={`posts${hexPub}`} filterOptions={filterOptions} />
</Show>
</div>
</View>
);
}

export default Profile;

0 comments on commit 1e8b6e2

Please sign in to comment.