mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
refactored component errors
This commit is contained in:
parent
7031084b09
commit
d4c2e7ee09
79 changed files with 573 additions and 462 deletions
|
@ -1,43 +1,42 @@
|
|||
import React, { FC, useCallback, useEffect, useState } from "react";
|
||||
import styles from "./styles.module.scss";
|
||||
import { connect } from "react-redux";
|
||||
import { getURL } from "~/utils/dom";
|
||||
import { pick } from "ramda";
|
||||
import { selectAuthProfile, selectAuthUser } from "~/redux/auth/selectors";
|
||||
import { PRESETS } from "~/constants/urls";
|
||||
import { selectUploads } from "~/redux/uploads/selectors";
|
||||
import { IFileWithUUID } from "~/redux/types";
|
||||
import uuid from "uuid4";
|
||||
import { UPLOAD_SUBJECTS, UPLOAD_TARGETS, UPLOAD_TYPES } from "~/redux/uploads/constants";
|
||||
import React, { FC, useCallback, useEffect, useState } from 'react';
|
||||
import styles from './styles.module.scss';
|
||||
import { connect } from 'react-redux';
|
||||
import { getURL } from '~/utils/dom';
|
||||
import { pick } from 'ramda';
|
||||
import { selectAuthProfile, selectAuthUser } from '~/redux/auth/selectors';
|
||||
import { PRESETS } from '~/constants/urls';
|
||||
import { selectUploads } from '~/redux/uploads/selectors';
|
||||
import { IFileWithUUID } from '~/redux/types';
|
||||
import uuid from 'uuid4';
|
||||
import { UPLOAD_SUBJECTS, UPLOAD_TARGETS, UPLOAD_TYPES } from '~/redux/uploads/constants';
|
||||
import { path } from 'ramda';
|
||||
import * as UPLOAD_ACTIONS from "~/redux/uploads/actions";
|
||||
import * as AUTH_ACTIONS from "~/redux/auth/actions";
|
||||
import { Icon } from "~/components/input/Icon";
|
||||
import * as UPLOAD_ACTIONS from '~/redux/uploads/actions';
|
||||
import * as AUTH_ACTIONS from '~/redux/auth/actions';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
user: pick(["id"], selectAuthUser(state)),
|
||||
profile: pick(["is_loading", "user"], selectAuthProfile(state)),
|
||||
uploads: pick(["statuses", "files"], selectUploads(state))
|
||||
user: pick(['id'], selectAuthUser(state)),
|
||||
profile: pick(['is_loading', 'user'], selectAuthProfile(state)),
|
||||
uploads: pick(['statuses', 'files'], selectUploads(state)),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
uploadUploadFiles: UPLOAD_ACTIONS.uploadUploadFiles,
|
||||
authPatchUser: AUTH_ACTIONS.authPatchUser
|
||||
authPatchUser: AUTH_ACTIONS.authPatchUser,
|
||||
};
|
||||
|
||||
type IProps = ReturnType<typeof mapStateToProps> &
|
||||
typeof mapDispatchToProps & {};
|
||||
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||
|
||||
const ProfileAvatarUnconnected: FC<IProps> = ({
|
||||
user: { id },
|
||||
profile: { is_loading, user },
|
||||
uploads: { statuses, files },
|
||||
uploadUploadFiles,
|
||||
authPatchUser
|
||||
authPatchUser,
|
||||
}) => {
|
||||
const can_edit = !is_loading && id && id === user.id;
|
||||
const can_edit = !is_loading && id && id === user?.id;
|
||||
|
||||
const [temp, setTemp] = useState<string>(null);
|
||||
const [temp, setTemp] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!can_edit) return;
|
||||
|
@ -45,7 +44,7 @@ const ProfileAvatarUnconnected: FC<IProps> = ({
|
|||
Object.entries(statuses).forEach(([id, status]) => {
|
||||
if (temp === id && !!status.uuid && files[status.uuid]) {
|
||||
authPatchUser({ photo: files[status.uuid] });
|
||||
setTemp(null);
|
||||
setTemp('');
|
||||
}
|
||||
});
|
||||
}, [statuses, files, temp, can_edit, authPatchUser]);
|
||||
|
@ -58,11 +57,11 @@ const ProfileAvatarUnconnected: FC<IProps> = ({
|
|||
temp_id: uuid(),
|
||||
subject: UPLOAD_SUBJECTS.AVATAR,
|
||||
target: UPLOAD_TARGETS.PROFILES,
|
||||
type: UPLOAD_TYPES.IMAGE
|
||||
type: UPLOAD_TYPES.IMAGE,
|
||||
})
|
||||
);
|
||||
|
||||
setTemp(path([0, "temp_id"], items));
|
||||
setTemp(path([0, 'temp_id'], items) || '');
|
||||
uploadUploadFiles(items.slice(0, 1));
|
||||
},
|
||||
[uploadUploadFiles, setTemp]
|
||||
|
@ -81,13 +80,15 @@ const ProfileAvatarUnconnected: FC<IProps> = ({
|
|||
[onUpload, can_edit]
|
||||
);
|
||||
|
||||
const backgroundImage = is_loading
|
||||
? undefined
|
||||
: `url("${user && getURL(user.photo, PRESETS.avatar)}")`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.avatar}
|
||||
style={{
|
||||
backgroundImage: is_loading
|
||||
? null
|
||||
: `url("${user && getURL(user.photo, PRESETS.avatar)}")`
|
||||
backgroundImage,
|
||||
}}
|
||||
>
|
||||
{can_edit && <input type="file" onInput={onInputChange} />}
|
||||
|
@ -100,9 +101,6 @@ const ProfileAvatarUnconnected: FC<IProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
const ProfileAvatar = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ProfileAvatarUnconnected);
|
||||
const ProfileAvatar = connect(mapStateToProps, mapDispatchToProps)(ProfileAvatarUnconnected);
|
||||
|
||||
export { ProfileAvatar };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { FC, ReactNode } from 'react';
|
||||
import { IUser } from '~/redux/auth/types';
|
||||
import { IAuthState, IUser } from '~/redux/auth/types';
|
||||
import styles from './styles.module.scss';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Placeholder } from '~/components/placeholders/Placeholder';
|
||||
|
@ -14,7 +14,7 @@ interface IProps {
|
|||
is_loading?: boolean;
|
||||
is_own?: boolean;
|
||||
|
||||
setTab?: (tab: string) => void;
|
||||
setTab?: (tab: IAuthState['profile']['tab']) => void;
|
||||
|
||||
content?: ReactNode;
|
||||
}
|
||||
|
@ -26,16 +26,16 @@ const ProfileInfo: FC<IProps> = ({ user, tab, is_loading, is_own, setTab, conten
|
|||
|
||||
<div className={styles.field}>
|
||||
<div className={styles.name}>
|
||||
{is_loading ? <Placeholder width="80%" /> : user.fullname || user.username}
|
||||
{is_loading ? <Placeholder width="80%" /> : user?.fullname || user?.username}
|
||||
</div>
|
||||
|
||||
<div className={styles.description}>
|
||||
{is_loading ? <Placeholder /> : getPrettyDate(user.last_seen)}
|
||||
{is_loading ? <Placeholder /> : getPrettyDate(user?.last_seen)}
|
||||
</div>
|
||||
</div>
|
||||
</Group>
|
||||
|
||||
<ProfileTabs tab={tab} is_own={is_own} setTab={setTab} />
|
||||
<ProfileTabs tab={tab} is_own={!!is_own} setTab={setTab} />
|
||||
|
||||
{content}
|
||||
</div>
|
||||
|
|
|
@ -20,10 +20,10 @@ const ProfileLayoutUnconnected: FC<IProps> = ({ history, nodeSetCoverImage }) =>
|
|||
const {
|
||||
params: { username },
|
||||
} = useRouteMatch<{ username: string }>();
|
||||
const [user, setUser] = useState<IUser>(null);
|
||||
const [user, setUser] = useState<IUser | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (user) setUser(null);
|
||||
if (user) setUser(undefined);
|
||||
}, [username]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -31,7 +31,7 @@ const ProfileMessagesUnconnected: FC<IProps> = ({
|
|||
messagesRefreshMessages,
|
||||
}) => {
|
||||
const wasAtBottom = useRef(true);
|
||||
const [wrap, setWrap] = useState<HTMLDivElement>(null);
|
||||
const [wrap, setWrap] = useState<HTMLDivElement | undefined>(undefined);
|
||||
const [editingMessageId, setEditingMessageId] = useState(0);
|
||||
|
||||
const onEditMessage = useCallback((id: number) => setEditingMessageId(id), [setEditingMessageId]);
|
||||
|
@ -95,31 +95,33 @@ const ProfileMessagesUnconnected: FC<IProps> = ({
|
|||
if (!messages.messages.length || profile.is_loading)
|
||||
return <NodeNoComments is_loading={messages.is_loading_messages || profile.is_loading} />;
|
||||
|
||||
return (
|
||||
messages.messages.length > 0 && (
|
||||
<div className={styles.messages} ref={storeRef}>
|
||||
{messages.messages
|
||||
.filter(message => !!message.text)
|
||||
.map((
|
||||
message // TODO: show files / memo
|
||||
) => (
|
||||
<Message
|
||||
message={message}
|
||||
incoming={id !== message.from.id}
|
||||
key={message.id}
|
||||
onEdit={onEditMessage}
|
||||
onDelete={onDeleteMessage}
|
||||
isEditing={editingMessageId === message.id}
|
||||
onCancelEdit={onCancelEdit}
|
||||
onRestore={onRestoreMessage}
|
||||
/>
|
||||
))}
|
||||
if (messages.messages.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
{!messages.is_loading_messages && messages.messages.length > 0 && (
|
||||
<div className={styles.placeholder}>Когда-нибудь здесь будут еще сообщения</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className={styles.messages} ref={storeRef}>
|
||||
{messages.messages
|
||||
.filter(message => !!message.text)
|
||||
.map((
|
||||
message // TODO: show files / memo
|
||||
) => (
|
||||
<Message
|
||||
message={message}
|
||||
incoming={id !== message.from.id}
|
||||
key={message.id}
|
||||
onEdit={onEditMessage}
|
||||
onDelete={onDeleteMessage}
|
||||
isEditing={editingMessageId === message.id}
|
||||
onCancelEdit={onCancelEdit}
|
||||
onRestore={onRestoreMessage}
|
||||
/>
|
||||
))}
|
||||
|
||||
{!messages.is_loading_messages && messages.messages.length > 0 && (
|
||||
<div className={styles.placeholder}>Когда-нибудь здесь будут еще сообщения</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import React, { FC, useMemo } from 'react';
|
||||
import styles from './styles.module.scss';
|
||||
import { IAuthState } from '~/redux/auth/types';
|
||||
import { getURL } from '~/utils/dom';
|
||||
import { formatText, getURL } from '~/utils/dom';
|
||||
import { PRESETS, URLS } from '~/constants/urls';
|
||||
import { Placeholder } from '~/components/placeholders/Placeholder';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
import markdown from '~/styles/common/markdown.module.scss';
|
||||
|
||||
interface IProps {
|
||||
profile: IAuthState['profile'];
|
||||
|
@ -26,11 +29,11 @@ const ProfilePageLeft: FC<IProps> = ({ username, profile }) => {
|
|||
<div className={styles.region_wrap}>
|
||||
<div className={styles.region}>
|
||||
<div className={styles.name}>
|
||||
{profile.is_loading ? <Placeholder /> : profile.user.fullname}
|
||||
{profile.is_loading ? <Placeholder /> : profile?.user?.fullname}
|
||||
</div>
|
||||
|
||||
<div className={styles.username}>
|
||||
{profile.is_loading ? <Placeholder /> : `~${profile.user.username}`}
|
||||
{profile.is_loading ? <Placeholder /> : `~${profile?.user?.username}`}
|
||||
</div>
|
||||
|
||||
<div className={styles.menu}>
|
||||
|
@ -53,7 +56,9 @@ const ProfilePageLeft: FC<IProps> = ({ username, profile }) => {
|
|||
</div>
|
||||
|
||||
{profile && profile.user && profile.user.description && false && (
|
||||
<div className={styles.description}>{profile.user.description}</div>
|
||||
<div className={classNames(styles.description, markdown.wrapper)}>
|
||||
{formatText(profile?.user?.description || '')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,38 +1,49 @@
|
|||
import React, { FC } from 'react';
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import styles from './styles.module.scss';
|
||||
import classNames from 'classnames';
|
||||
import { IAuthState } from '~/redux/auth/types';
|
||||
|
||||
interface IProps {
|
||||
tab: string;
|
||||
is_own: boolean;
|
||||
setTab: (tab: string) => void;
|
||||
setTab?: (tab: IAuthState['profile']['tab']) => void;
|
||||
}
|
||||
|
||||
const ProfileTabs: FC<IProps> = ({ tab, is_own, setTab }) => (
|
||||
<div className={styles.wrap}>
|
||||
<div
|
||||
className={classNames(styles.tab, { [styles.active]: tab === 'profile' })}
|
||||
onClick={() => setTab('profile')}
|
||||
>
|
||||
Профиль
|
||||
const ProfileTabs: FC<IProps> = ({ tab, is_own, setTab }) => {
|
||||
const changeTab = useCallback(
|
||||
(tab: IAuthState['profile']['tab']) => () => {
|
||||
if (!setTab) return;
|
||||
setTab(tab);
|
||||
},
|
||||
[setTab]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
<div
|
||||
className={classNames(styles.tab, { [styles.active]: tab === 'profile' })}
|
||||
onClick={changeTab('profile')}
|
||||
>
|
||||
Профиль
|
||||
</div>
|
||||
<div
|
||||
className={classNames(styles.tab, { [styles.active]: tab === 'messages' })}
|
||||
onClick={changeTab('messages')}
|
||||
>
|
||||
Сообщения
|
||||
</div>
|
||||
{is_own && (
|
||||
<>
|
||||
<div
|
||||
className={classNames(styles.tab, { [styles.active]: tab === 'settings' })}
|
||||
onClick={changeTab('settings')}
|
||||
>
|
||||
Настройки
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={classNames(styles.tab, { [styles.active]: tab === 'messages' })}
|
||||
onClick={() => setTab('messages')}
|
||||
>
|
||||
Сообщения
|
||||
</div>
|
||||
{is_own && (
|
||||
<>
|
||||
<div
|
||||
className={classNames(styles.tab, { [styles.active]: tab === 'settings' })}
|
||||
onClick={() => setTab('settings')}
|
||||
>
|
||||
Настройки
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export { ProfileTabs };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue