mirror of
https://github.com/irislib/iris-messenger.git
synced 2024-09-07 20:13:28 +00:00
more fn comps
This commit is contained in:
parent
209f7a3fad
commit
3842ba5e9d
@ -18,6 +18,7 @@ module.exports = {
|
||||
plugins: ['simple-import-sort', '@typescript-eslint'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'simple-import-sort/imports': [
|
||||
'error',
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Component from '../../BaseComponent';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Key from '../../nostr/Key';
|
||||
import SocialNetwork from '../../nostr/SocialNetwork';
|
||||
import { translate as t } from '../../translations/Translation.mjs';
|
||||
@ -11,77 +12,61 @@ type Props = {
|
||||
onClick?: (e) => void;
|
||||
};
|
||||
|
||||
class Block extends Component<Props> {
|
||||
key: string;
|
||||
cls?: string;
|
||||
actionDone: string;
|
||||
action: string;
|
||||
activeClass: string;
|
||||
hoverAction: string;
|
||||
const Block = ({ id, showName, className, onClick }: Props) => {
|
||||
const cls = 'block-btn';
|
||||
const key = 'blocked';
|
||||
const activeClass = 'blocked';
|
||||
const action = t('block');
|
||||
const actionDone = t('blocked');
|
||||
const hoverAction = t('unblock');
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.cls = 'block-btn';
|
||||
this.key = 'blocked';
|
||||
this.activeClass = 'blocked';
|
||||
this.action = t('block');
|
||||
this.actionDone = t('blocked');
|
||||
this.hoverAction = t('unblock');
|
||||
this.state = { ...this.state, hover: false };
|
||||
}
|
||||
const [hover, setHover] = useState(false);
|
||||
const [isBlocked, setIsBlocked] = useState(false);
|
||||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({ hover: true });
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ hover: false });
|
||||
};
|
||||
|
||||
onClick(e) {
|
||||
e.preventDefault();
|
||||
const newValue = !this.state[this.key];
|
||||
const hex = Key.toNostrHexAddress(this.props.id);
|
||||
hex && SocialNetwork.block(hex, newValue);
|
||||
this.props.onClick?.(e);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
useEffect(() => {
|
||||
SocialNetwork.getBlockedUsers((blocks) => {
|
||||
const blocked = blocks?.has(Key.toNostrHexAddress(this.props.id) as string);
|
||||
this.setState({ blocked });
|
||||
const blocked = blocks?.has(Key.toNostrHexAddress(id) as string);
|
||||
setIsBlocked(!!blocked);
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setHover(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setHover(false);
|
||||
};
|
||||
|
||||
const onButtonClick = (e) => {
|
||||
e.preventDefault();
|
||||
const newValue = !isBlocked;
|
||||
const hex = Key.toNostrHexAddress(id);
|
||||
hex && SocialNetwork.block(hex, newValue);
|
||||
onClick?.(e);
|
||||
};
|
||||
|
||||
let buttonText;
|
||||
if (isBlocked && hover) {
|
||||
buttonText = hoverAction;
|
||||
} else if (isBlocked && !hover) {
|
||||
buttonText = actionDone;
|
||||
} else {
|
||||
buttonText = action;
|
||||
}
|
||||
|
||||
render() {
|
||||
const isBlocked = this.state[this.key];
|
||||
const isHovering = this.state.hover;
|
||||
|
||||
let buttonText;
|
||||
|
||||
if (isBlocked && isHovering) {
|
||||
buttonText = this.hoverAction;
|
||||
} else if (isBlocked && !isHovering) {
|
||||
buttonText = this.actionDone;
|
||||
} else {
|
||||
buttonText = this.action;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`${this.cls || this.key} ${isBlocked ? this.activeClass : ''} ${
|
||||
this.props.className || ''
|
||||
}`}
|
||||
onClick={(e) => this.onClick(e)}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
<span>
|
||||
{t(buttonText)} {this.props.showName ? <Name pub={this.props.id} hideBadge={true} /> : ''}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<button
|
||||
className={`${cls || key} ${isBlocked ? activeClass : ''} ${className || ''}`}
|
||||
onClick={onButtonClick}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<span>
|
||||
{t(buttonText)} {showName ? <Name pub={id} hideBadge={true} /> : ''}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Component from '../../BaseComponent';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Key from '../../nostr/Key';
|
||||
import SocialNetwork from '../../nostr/SocialNetwork';
|
||||
import { translate as t } from '../../translations/Translation.mjs';
|
||||
@ -8,82 +9,69 @@ type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
class Follow extends Component<Props> {
|
||||
key: string;
|
||||
cls?: string;
|
||||
actionDone: string;
|
||||
action: string;
|
||||
activeClass: string;
|
||||
hoverAction: string;
|
||||
const Follow = ({ id, className }: Props) => {
|
||||
const key = 'follow';
|
||||
const activeClass = 'following';
|
||||
const action = t('follow_btn');
|
||||
const actionDone = t('following_btn');
|
||||
const hoverAction = t('unfollow_btn');
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.key = 'follow';
|
||||
this.activeClass = 'following';
|
||||
this.action = t('follow_btn');
|
||||
this.actionDone = t('following_btn');
|
||||
this.hoverAction = t('unfollow_btn');
|
||||
this.state = { ...this.state, hover: false };
|
||||
}
|
||||
const [hover, setHover] = useState(false);
|
||||
const [isFollowed, setIsFollowed] = useState(false);
|
||||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({ hover: true });
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ hover: false });
|
||||
};
|
||||
|
||||
onClick(e) {
|
||||
e.preventDefault();
|
||||
const newValue = !this.state[this.key];
|
||||
const hex = Key.toNostrHexAddress(this.props.id);
|
||||
if (!hex) return;
|
||||
if (this.key === 'follow') {
|
||||
SocialNetwork.setFollowed(hex, newValue);
|
||||
return;
|
||||
}
|
||||
if (this.key === 'block') {
|
||||
SocialNetwork.setBlocked(hex, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.key === 'follow') {
|
||||
useEffect(() => {
|
||||
if (key === 'follow') {
|
||||
SocialNetwork.getFollowedByUser(Key.getPubKey(), (follows) => {
|
||||
const hex = Key.toNostrHexAddress(this.props.id);
|
||||
const hex = Key.toNostrHexAddress(id);
|
||||
const follow = hex && follows?.has(hex);
|
||||
this.setState({ follow });
|
||||
setIsFollowed(!!follow);
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setHover(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setHover(false);
|
||||
};
|
||||
|
||||
const onClick = (e) => {
|
||||
e.preventDefault();
|
||||
const newValue = !isFollowed;
|
||||
const hex = Key.toNostrHexAddress(id);
|
||||
if (!hex) return;
|
||||
if (key === 'follow') {
|
||||
SocialNetwork.setFollowed(hex, newValue);
|
||||
setIsFollowed(newValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const isFollowed = this.state[this.key];
|
||||
const isHovering = this.state.hover;
|
||||
|
||||
let buttonText;
|
||||
|
||||
if (isFollowed && isHovering) {
|
||||
buttonText = this.hoverAction;
|
||||
} else if (isFollowed && !isHovering) {
|
||||
buttonText = this.actionDone;
|
||||
} else {
|
||||
buttonText = this.action;
|
||||
if (key === 'block') {
|
||||
SocialNetwork.setBlocked(hex, newValue);
|
||||
setIsFollowed(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`btn ${this.props.className || this.key} ${isFollowed ? this.activeClass : ''}`}
|
||||
onClick={(e) => this.onClick(e)}
|
||||
onMouseEnter={this.handleMouseEnter} // handle hover state
|
||||
onMouseLeave={this.handleMouseLeave} // handle hover state
|
||||
>
|
||||
{t(buttonText)}
|
||||
</button>
|
||||
);
|
||||
let buttonText;
|
||||
if (isFollowed && hover) {
|
||||
buttonText = hoverAction;
|
||||
} else if (isFollowed && !hover) {
|
||||
buttonText = actionDone;
|
||||
} else {
|
||||
buttonText = action;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`btn ${className || key} ${isFollowed ? activeClass : ''}`}
|
||||
onClick={onClick}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
{t(buttonText)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Follow;
|
||||
|
@ -1,36 +1,74 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Key from '../../nostr/Key';
|
||||
import SocialNetwork from '../../nostr/SocialNetwork';
|
||||
import { translate as t } from '../../translations/Translation.mjs';
|
||||
import Name from '../user/Name';
|
||||
|
||||
import Block from './Block';
|
||||
type Props = {
|
||||
id: string;
|
||||
showName?: boolean;
|
||||
className?: string;
|
||||
onClick?: (e) => void;
|
||||
};
|
||||
|
||||
class Report extends Block {
|
||||
constructor() {
|
||||
super();
|
||||
this.cls = 'block';
|
||||
this.key = 'reported';
|
||||
this.activeClass = 'blocked';
|
||||
this.action = t('report_public');
|
||||
this.actionDone = t('reported');
|
||||
this.hoverAction = t('unreport');
|
||||
}
|
||||
const Report = ({ id, showName = false, className, onClick }: Props) => {
|
||||
const cls = 'block'; // changed this from 'block-btn' to 'block'
|
||||
const key = 'reported'; // key updated for reporting
|
||||
const activeClass = 'blocked'; // activeClass remains the same
|
||||
const action = t('report_public'); // changed to report_public
|
||||
const actionDone = t('reported'); // changed to reported
|
||||
const hoverAction = t('unreport'); // changed to unreport
|
||||
|
||||
onClick(e) {
|
||||
e.preventDefault();
|
||||
const newValue = !this.state[this.key];
|
||||
if (confirm(newValue ? 'Publicly report this user?' : 'Unreport user?')) {
|
||||
const hex = Key.toNostrHexAddress(this.props.id);
|
||||
hex && SocialNetwork.flag(hex, newValue);
|
||||
}
|
||||
}
|
||||
const [hover, setHover] = useState(false);
|
||||
const [isReported, setIsReported] = useState(false);
|
||||
|
||||
componentDidMount() {
|
||||
useEffect(() => {
|
||||
SocialNetwork.getFlaggedUsers((flags) => {
|
||||
const hex = Key.toNostrHexAddress(this.props.id);
|
||||
const reported = hex && flags?.has(hex);
|
||||
this.setState({ reported });
|
||||
const reported = flags?.has(Key.toNostrHexAddress(id) as string);
|
||||
setIsReported(!!reported);
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setHover(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setHover(false);
|
||||
};
|
||||
|
||||
const onButtonClick = (e) => {
|
||||
e.preventDefault();
|
||||
const newValue = !isReported;
|
||||
if (window.confirm(newValue ? 'Publicly report this user?' : 'Unreport user?')) {
|
||||
const hex = Key.toNostrHexAddress(id);
|
||||
hex && SocialNetwork.flag(hex, newValue);
|
||||
onClick?.(e);
|
||||
}
|
||||
};
|
||||
|
||||
let buttonText;
|
||||
if (isReported && hover) {
|
||||
buttonText = hoverAction;
|
||||
} else if (isReported && !hover) {
|
||||
buttonText = actionDone;
|
||||
} else {
|
||||
buttonText = action;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`${cls || key} ${isReported ? activeClass : ''} ${className || ''}`}
|
||||
onClick={onButtonClick}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<span>
|
||||
{t(buttonText)} {showName ? <Name pub={id} hideBadge={true} /> : ''}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Report;
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Link } from 'preact-router';
|
||||
|
||||
import Component from '../BaseComponent';
|
||||
import Show from '@/components/helpers/Show.tsx';
|
||||
import { RouteProps } from '@/views/types.ts';
|
||||
|
||||
import Follow from '../components/buttons/Follow';
|
||||
import Header from '../components/Header';
|
||||
import Avatar from '../components/user/Avatar';
|
||||
@ -10,110 +12,98 @@ import Helpers from '../utils/Helpers.tsx';
|
||||
|
||||
const IRIS_INFO_ACCOUNT = 'npub1wnwwcv0a8wx0m9stck34ajlwhzuua68ts8mw3kjvspn42dcfyjxs4n95l8';
|
||||
|
||||
class About extends Component {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<div className="main-view prose" id="settings">
|
||||
<div className="px-2 md:px-4 py-2">
|
||||
<h2 className="mt-0">{t('about')}</h2>
|
||||
<p>Iris is like the social networking apps we're used to, but better:</p>
|
||||
const About: React.FC<RouteProps> = () => (
|
||||
<>
|
||||
<Header />
|
||||
<div className="main-view prose">
|
||||
<div className="px-2 md:px-4 py-2">
|
||||
<h2 className="mt-0">{t('about')}</h2>
|
||||
<p>Iris is like the social networking apps we're used to, but better:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Accessible.</b> No phone number or signup is required. Just type in your name or
|
||||
alias and go!
|
||||
</li>
|
||||
<li>
|
||||
<b>Secure.</b> It's open source. You can verify that your data stays safe.
|
||||
</li>
|
||||
<li>
|
||||
<b>Always available.</b> It works offline-first and is not dependent on any single
|
||||
centrally managed server. Users can even connect directly to each other.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<Show when={!Helpers.isStandalone()}>
|
||||
<h3>Versions</h3>
|
||||
<p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Accessible.</b> No phone number or signup is required. Just type in your name or
|
||||
alias and go!
|
||||
<a target="_blank" href="https://iris.to">
|
||||
iris.to
|
||||
</a>{' '}
|
||||
(web)
|
||||
</li>
|
||||
<li>
|
||||
<b>Secure.</b> It's open source. You can verify that your data stays safe.
|
||||
<a target="_blank" href="https://github.com/irislib/iris-messenger/releases/latest">
|
||||
Desktop
|
||||
</a>{' '}
|
||||
(macOS, Windows, Linux)
|
||||
</li>
|
||||
<li>
|
||||
<b>Always available.</b> It works offline-first and is not dependent on any single
|
||||
centrally managed server. Users can even connect directly to each other.
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://apps.apple.com/app/iris-the-nostr-client/id1665849007"
|
||||
>
|
||||
iOS
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://play.google.com/store/apps/details?id=to.iris.twa">
|
||||
Android
|
||||
</a>{' '}
|
||||
(
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/irislib/iris-messenger/releases/tag/jan2023"
|
||||
>
|
||||
apk
|
||||
</a>
|
||||
)
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</Show>
|
||||
|
||||
{!Helpers.isStandalone() && (
|
||||
<>
|
||||
<h3>Versions</h3>
|
||||
<p>
|
||||
<ul>
|
||||
<li>
|
||||
<a target="_blank" href="https://iris.to">
|
||||
iris.to
|
||||
</a>{' '}
|
||||
(web)
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/irislib/iris-messenger/releases/latest"
|
||||
>
|
||||
Desktop
|
||||
</a>{' '}
|
||||
(macOS, Windows, Linux)
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://apps.apple.com/app/iris-the-nostr-client/id1665849007"
|
||||
>
|
||||
iOS
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://play.google.com/store/apps/details?id=to.iris.twa"
|
||||
>
|
||||
Android
|
||||
</a>{' '}
|
||||
(
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/irislib/iris-messenger/releases/tag/jan2023"
|
||||
>
|
||||
apk
|
||||
</a>
|
||||
)
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
<h3>Iris docs</h3>
|
||||
<p>
|
||||
Visit Iris <a href="https://docs.iris.to">docs</a> for features, explanations and
|
||||
troubleshooting.
|
||||
</p>
|
||||
|
||||
<h3>Iris docs</h3>
|
||||
<p>
|
||||
Visit Iris <a href="https://docs.iris.to">docs</a> for features, explanations and
|
||||
troubleshooting.
|
||||
</p>
|
||||
<h3>Privacy</h3>
|
||||
<p>{t('application_security_warning')}</p>
|
||||
|
||||
<h3>Privacy</h3>
|
||||
<p>{t('application_security_warning')}</p>
|
||||
|
||||
<h3>Follow</h3>
|
||||
<div className="flex flex-row items-center w-full justify-between">
|
||||
<Link href={`/${IRIS_INFO_ACCOUNT}`} className="flex flex-row items-center gap-2">
|
||||
<Avatar str={IRIS_INFO_ACCOUNT} width={40} />
|
||||
<Name pub={IRIS_INFO_ACCOUNT} placeholder="Iris" />
|
||||
</Link>
|
||||
<Follow className="btn btn-neutral btn-sm" id={IRIS_INFO_ACCOUNT} />
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<a href="https://t.me/irismessenger">Telegram</a> channel.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Released under MIT license. Code:{' '}
|
||||
<a href="https://github.com/irislib/iris-messenger">Github</a>.
|
||||
</p>
|
||||
<br />
|
||||
</div>
|
||||
<h3>Follow</h3>
|
||||
<div className="flex flex-row items-center w-full justify-between">
|
||||
<Link href={`/${IRIS_INFO_ACCOUNT}`} className="flex flex-row items-center gap-2">
|
||||
<Avatar str={IRIS_INFO_ACCOUNT} width={40} />
|
||||
<Name pub={IRIS_INFO_ACCOUNT} placeholder="Iris" />
|
||||
</Link>
|
||||
<Follow className="btn btn-neutral btn-sm" id={IRIS_INFO_ACCOUNT} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
<p>
|
||||
<a href="https://t.me/irismessenger">Telegram</a> channel.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Released under MIT license. Code:{' '}
|
||||
<a href="https://github.com/irislib/iris-messenger">Github</a>.
|
||||
</p>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
export default About;
|
||||
|
@ -1,7 +1,9 @@
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { route } from 'preact-router';
|
||||
|
||||
import Component from '../BaseComponent';
|
||||
import { RouteProps } from '@/views/types.ts';
|
||||
|
||||
import Upload from '../components/buttons/Upload';
|
||||
import Header from '../components/Header';
|
||||
import SafeImg from '../components/SafeImg';
|
||||
@ -16,172 +18,153 @@ const explainers = {
|
||||
nip05: 'Nostr address (nip05)',
|
||||
};
|
||||
|
||||
export default class EditProfile extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
profile: {},
|
||||
newFieldName: '',
|
||||
newFieldValue: '',
|
||||
edited: false,
|
||||
};
|
||||
}
|
||||
const EditProfile: React.FC<RouteProps> = () => {
|
||||
const [profile, setProfile] = useState({});
|
||||
const [newFieldName, setNewFieldName] = useState('');
|
||||
const [newFieldValue, setNewFieldValue] = useState('');
|
||||
const [edited, setEdited] = useState(false);
|
||||
|
||||
componentDidMount() {
|
||||
SocialNetwork.getProfile(Key.getPubKey(), (p) => {
|
||||
if (!this.state.edited && Object.keys(this.state.profile).length === 0) {
|
||||
useEffect(() => {
|
||||
return SocialNetwork.getProfile(Key.getPubKey(), (p) => {
|
||||
if (!edited && Object.keys(profile).length === 0) {
|
||||
delete p['created_at'];
|
||||
this.setState({
|
||||
profile: p,
|
||||
});
|
||||
setProfile(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [profile, edited]);
|
||||
|
||||
saveOnChange = debounce(() => {
|
||||
const profile = this.state.profile;
|
||||
Object.keys(profile).forEach((key) => {
|
||||
if (typeof profile[key] === 'string') {
|
||||
profile[key] = profile[key].trim();
|
||||
const saveOnChange = debounce(() => {
|
||||
const trimmedProfile = { ...profile };
|
||||
Object.keys(trimmedProfile).forEach((key) => {
|
||||
if (typeof trimmedProfile[key] === 'string') {
|
||||
trimmedProfile[key] = trimmedProfile[key].trim();
|
||||
}
|
||||
});
|
||||
SocialNetwork.setMetadata(profile);
|
||||
SocialNetwork.setMetadata(trimmedProfile);
|
||||
}, 2000);
|
||||
|
||||
setProfileAttribute = (key, value) => {
|
||||
const setProfileAttribute = (key, value) => {
|
||||
key = key.trim();
|
||||
const profile = Object.assign({}, this.state.profile);
|
||||
const updatedProfile = { ...profile };
|
||||
if (value) {
|
||||
profile[key] = value;
|
||||
updatedProfile[key] = value;
|
||||
} else {
|
||||
delete profile[key];
|
||||
delete updatedProfile[key];
|
||||
}
|
||||
this.setState({ profile, edited: true });
|
||||
this.saveOnChange();
|
||||
setProfile(updatedProfile);
|
||||
setEdited(true);
|
||||
saveOnChange();
|
||||
};
|
||||
|
||||
handleSubmit = (event) => {
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
SocialNetwork.setMetadata(this.state.profile);
|
||||
SocialNetwork.setMetadata(profile);
|
||||
const myPub = Key.toNostrBech32Address(Key.getPubKey(), 'npub');
|
||||
route('/' + myPub);
|
||||
};
|
||||
|
||||
handleAddField = (event) => {
|
||||
const handleAddField = (event) => {
|
||||
event.preventDefault();
|
||||
const fieldName = this.state.newFieldName;
|
||||
const fieldValue = this.state.newFieldValue;
|
||||
if (fieldName && fieldValue) {
|
||||
this.setProfileAttribute(fieldName, fieldValue);
|
||||
this.setState({ newFieldName: '', newFieldValue: '' });
|
||||
SocialNetwork.setMetadata(this.state.profile);
|
||||
if (newFieldName && newFieldValue) {
|
||||
setProfileAttribute(newFieldName, newFieldValue);
|
||||
setNewFieldName('');
|
||||
setNewFieldValue('');
|
||||
SocialNetwork.setMetadata(profile);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const fields = ['name', 'picture', 'about', 'banner', 'website', 'lud16', 'nip05'];
|
||||
// add other possible fields from profile
|
||||
Object.keys(this.state.profile).forEach((key) => {
|
||||
if (!fields.includes(key)) {
|
||||
fields.push(key);
|
||||
}
|
||||
});
|
||||
const fields = ['name', 'picture', 'about', 'banner', 'website', 'lud16', 'nip05'];
|
||||
Object.keys(profile).forEach((key) => {
|
||||
if (!fields.includes(key)) {
|
||||
fields.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<div class="main-view" id="settings">
|
||||
<div class="centered-container prose">
|
||||
<h3>{t('edit_profile')}</h3>
|
||||
<form onSubmit={(e) => this.handleSubmit(e)}>
|
||||
{fields.map((field) => {
|
||||
const val = this.state.profile[field];
|
||||
const isString = typeof val === 'string' || typeof val === 'undefined';
|
||||
return (
|
||||
<p>
|
||||
<label htmlFor={field}>{explainers[field] || field}:</label>
|
||||
<br />
|
||||
<input
|
||||
className="input w-full"
|
||||
type="text"
|
||||
id={field}
|
||||
disabled={!isString}
|
||||
value={isString ? val || '' : JSON.stringify(val)}
|
||||
onInput={(e) =>
|
||||
isString &&
|
||||
this.setProfileAttribute(field, (e.target as HTMLInputElement).value)
|
||||
}
|
||||
/>
|
||||
{field === 'lud16' && !val && (
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<div class="mx-2 md:mx-4">
|
||||
<div class="centered-container prose">
|
||||
<h3>{t('edit_profile')}</h3>
|
||||
<form onSubmit={handleSubmit}>
|
||||
{fields.map((field) => {
|
||||
const val = profile[field];
|
||||
const isString = typeof val === 'string' || typeof val === 'undefined';
|
||||
return (
|
||||
<p>
|
||||
<label htmlFor={field}>{explainers[field] || field}:</label>
|
||||
<br />
|
||||
<input
|
||||
className="input w-full"
|
||||
type="text"
|
||||
id={field}
|
||||
disabled={!isString}
|
||||
value={isString ? val || '' : JSON.stringify(val)}
|
||||
onInput={(e: any) => isString && setProfileAttribute(field, e.target.value)}
|
||||
/>
|
||||
{field === 'lud16' && !val && (
|
||||
<p>
|
||||
<small>{t('install_lightning_wallet_prompt')}</small>
|
||||
</p>
|
||||
)}
|
||||
{(field === 'picture' || field === 'banner') && (
|
||||
<>
|
||||
<p>
|
||||
<small>{t('install_lightning_wallet_prompt')}</small>
|
||||
<Upload onUrl={(url) => setProfileAttribute(field, url)} />
|
||||
</p>
|
||||
)}
|
||||
{field === 'picture' || field === 'banner' ? (
|
||||
<>
|
||||
{val && (
|
||||
<p>
|
||||
<Upload onUrl={(url) => this.setProfileAttribute(field, url)} />
|
||||
<SafeImg key={val} src={val} />
|
||||
</p>
|
||||
{val && (
|
||||
<p>
|
||||
<SafeImg key={val} src={val} />
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
<p>
|
||||
<button className="btn btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
<p>
|
||||
<button className="btn btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<h4>Add new field</h4>
|
||||
<form onSubmit={(e) => this.handleAddField(e)}>
|
||||
<p>
|
||||
<label htmlFor="newFieldName">Field name:</label>
|
||||
<br />
|
||||
<input
|
||||
value={this.state.newFieldName}
|
||||
type="text"
|
||||
id="newFieldName"
|
||||
className="input w-full"
|
||||
placeholder={t('field_name')}
|
||||
onInput={(e) =>
|
||||
this.setState({
|
||||
newFieldName: (e.target as HTMLInputElement).value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<label htmlFor="newFieldValue">Field value:</label>
|
||||
<br />
|
||||
<input
|
||||
value={this.state.newFieldValue}
|
||||
type="text"
|
||||
id="newFieldValue"
|
||||
className="input w-full"
|
||||
placeholder={t('field_value')}
|
||||
onInput={(e) =>
|
||||
this.setState({
|
||||
newFieldValue: (e.target as HTMLInputElement).value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<button className="btn btn-primary" type="submit">
|
||||
Add new attribute
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
<h4>Add new field</h4>
|
||||
<form onSubmit={handleAddField}>
|
||||
<p>
|
||||
<label htmlFor="newFieldName">Field name:</label>
|
||||
<br />
|
||||
<input
|
||||
value={newFieldName}
|
||||
type="text"
|
||||
id="newFieldName"
|
||||
className="input w-full"
|
||||
placeholder={t('field_name')}
|
||||
onInput={(e: any) => setNewFieldName(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<label htmlFor="newFieldValue">Field value:</label>
|
||||
<br />
|
||||
<input
|
||||
value={newFieldValue}
|
||||
type="text"
|
||||
id="newFieldValue"
|
||||
className="input w-full"
|
||||
placeholder={t('field_value')}
|
||||
onInput={(e: any) => setNewFieldValue(e.target.value)}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<button className="btn btn-primary" type="submit">
|
||||
Add new attribute
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditProfile;
|
||||
|
@ -1,77 +1,73 @@
|
||||
import Component from '../BaseComponent';
|
||||
import { RouteProps } from '@/views/types.ts';
|
||||
|
||||
import Header from '../components/Header';
|
||||
import { translate as t } from '../translations/Translation.mjs';
|
||||
|
||||
class Subscribe extends Component {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<div className="main-view" id="settings">
|
||||
<div className="centered-container mobile-padding15">
|
||||
<h2>{t('subscribe')}</h2>
|
||||
<h3>Iris Supporter</h3>
|
||||
<p>Support open source development and get extra features!</p>
|
||||
<p>
|
||||
<ul>
|
||||
<li>Iris Supporter Badge</li>
|
||||
<li>Purple checkmark on Iris</li>
|
||||
<li>High-quality automatic translations (via deepl.com)</li>
|
||||
<li>Iris Supporters' private group chat</li>
|
||||
{/*
|
||||
const Subscribe: React.FC<RouteProps> = () => (
|
||||
<>
|
||||
<Header />
|
||||
<div className="main-view" id="settings">
|
||||
<div className="centered-container mobile-padding15">
|
||||
<h2>{t('subscribe')}</h2>
|
||||
<h3>Iris Supporter</h3>
|
||||
<p>Support open source development and get extra features!</p>
|
||||
<p>
|
||||
<ul>
|
||||
<li>Iris Supporter Badge</li>
|
||||
<li>Purple checkmark on Iris</li>
|
||||
<li>Iris Supporters' private group chat</li>
|
||||
{/*
|
||||
:D
|
||||
<li>Email-DM bridge for your Iris address</li>
|
||||
<li>Bitcoin Lightning proxy for your Iris address</li>
|
||||
<li>Custom themes for your profile page</li>
|
||||
<li>Profile view statistics</li>
|
||||
*/}
|
||||
<li>More features to come!</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
<input
|
||||
defaultChecked={true}
|
||||
type="radio"
|
||||
id="subscription_annually"
|
||||
name="subscription"
|
||||
value="1"
|
||||
/>
|
||||
<label htmlFor="subscription_annually">
|
||||
<b>8 € / month</b> charged annually (96 € / year)
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<input type="radio" id="subscription_monthly" name="subscription" value="2" />
|
||||
<label htmlFor="subscription_monthly">
|
||||
<b>10 € / month</b> charged monthly (120 € / year)
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<button className="btn btn-primary">Subscribe</button>
|
||||
</p>
|
||||
<li>More features to come!</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
<input
|
||||
defaultChecked={true}
|
||||
type="radio"
|
||||
id="subscription_annually"
|
||||
name="subscription"
|
||||
value="1"
|
||||
/>
|
||||
<label htmlFor="subscription_annually">
|
||||
<b>8 € / month</b> charged annually (96 € / year)
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<input type="radio" id="subscription_monthly" name="subscription" value="2" />
|
||||
<label htmlFor="subscription_monthly">
|
||||
<b>10 € / month</b> charged monthly (120 € / year)
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<button className="btn btn-primary">Subscribe</button>
|
||||
</p>
|
||||
|
||||
<h3>Iris Titan</h3>
|
||||
<p>
|
||||
True Mighty Titan status. Lifetime Iris Purple access, plus:
|
||||
<ul>
|
||||
<li>Iris Titan Badge</li>
|
||||
<li>Iris Titans private group chat</li>
|
||||
<li>Priority support</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
<b>1000 €</b> one-time payment.
|
||||
</p>
|
||||
<p>
|
||||
<button className="btn btn-primary">Subscribe</button>
|
||||
</p>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
<h3>Iris Titan</h3>
|
||||
<p>
|
||||
True Mighty Titan status. Lifetime Iris Purple access, plus:
|
||||
<ul>
|
||||
<li>Iris Titan Badge</li>
|
||||
<li>Iris Titans private group chat</li>
|
||||
<li>Priority support</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
<b>1000 €</b> one-time payment.
|
||||
</p>
|
||||
<p>
|
||||
<button className="btn btn-primary">Subscribe</button>
|
||||
</p>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
export default Subscribe;
|
||||
|
3
src/js/views/types.ts
Normal file
3
src/js/views/types.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export type RouteProps = {
|
||||
path: string;
|
||||
};
|
Loading…
Reference in New Issue
Block a user