= ({
11 | className,
12 | followers = 0,
13 | posts = 0,
14 | id,
15 | }) => {
16 | const classname = className
17 | ? `${className} ${styles.followers}`
18 | : styles.followers;
19 | const { isVisible, toggleModal } = useModal();
20 | const { t } = useTranslation();
21 |
22 | return (
23 |
24 |
25 |
26 |
27 |
31 |
35 |
36 | );
37 | };
38 |
39 | export default FollowersBlock;
40 |
--------------------------------------------------------------------------------
/src/components/activity/feed/MyFeed.tsx:
--------------------------------------------------------------------------------
1 | import { AccountId } from '@subsocial/api/types/dto';
2 | import { ListType } from 'src/components/home/HomePage';
3 | import PostList from 'src/components/post/post-list/post-list';
4 |
5 | export const MyFeed = ({
6 | ids,
7 | type,
8 | address,
9 | }: {
10 | ids: string[];
11 | type: ListType;
12 | address?: AccountId;
13 | }) => {
14 | return (
15 |
21 | );
22 | };
23 |
24 | export default MyFeed;
25 |
--------------------------------------------------------------------------------
/src/components/api/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './ApiContext';
2 |
--------------------------------------------------------------------------------
/src/components/common/Embed.tsx:
--------------------------------------------------------------------------------
1 | import { allowEmbedList, getEmbedUrl } from '../utils/embed';
2 | import { EmbedProps } from '../../models/common/embed';
3 |
4 | const Embed = ({ link, className }: EmbedProps) => {
5 | const embed = allowEmbedList.find((embed) => link.includes(embed));
6 | const src = getEmbedUrl(link, embed);
7 |
8 | return (
9 | <>
10 | {src && (
11 |
20 |
31 |
32 | )}
33 | >
34 | );
35 | };
36 |
37 | export default Embed;
38 |
--------------------------------------------------------------------------------
/src/components/common/address/Address.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .address
4 | display: flex
5 | align-items: center
6 | column-gap: $space_small
7 | margin: 0
8 |
9 | .qr
10 | &:global(.MuiButton-root)
11 | min-width: auto
12 | padding: 0
13 |
--------------------------------------------------------------------------------
/src/components/common/avatar/AvatarElement.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .wrapper
4 | background: none !important
5 | border: 1px solid $color_avatar_outline
6 |
7 | .icon
8 | display: flex
9 | justify-content: center
10 | align-items: center
11 |
--------------------------------------------------------------------------------
/src/components/common/avatar/AvatarElement.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './AvatarElement.module.sass';
3 | import { AvatarProps } from '../../../models/common/avatar';
4 | import { Avatar } from '@mui/material';
5 | import { loadImgUrl } from 'src/utils';
6 | import { toSvg } from 'jdenticon';
7 |
8 | const AvatarElement: FC = (props) => {
9 | return props.src ? (
10 |
15 | ) : (
16 |
21 |
25 |
26 | );
27 | };
28 |
29 | export default AvatarElement;
30 |
--------------------------------------------------------------------------------
/src/components/common/balance/Balance.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .balance
4 | display: flex
5 | align-items: center
6 | gap: 1.5rem
7 | margin: 0
8 | font-size: $font-normal
9 | color: $color_text_secondary
10 |
11 | div
12 | margin: 0
13 | white-space: nowrap
14 |
15 | svg
16 | fill: $color_icon_dark_gray
17 |
18 | .gray
19 | color: $color_text_dark_gray
20 | transition: color .5ms linear
21 |
--------------------------------------------------------------------------------
/src/components/common/button/ButtonReply.tsx:
--------------------------------------------------------------------------------
1 | import ReplyIcon from '@mui/icons-material/Reply';
2 | import ButtonIcon from './button-icon/ButtonIcon';
3 | import { FC } from 'react';
4 | import { ButtonReplyProps } from 'src/models/common/button';
5 | import { useTranslation } from 'react-i18next';
6 | import Text from "../text/Text";
7 | import { TextSizes } from "../../../models/common/typography";
8 |
9 | const ButtonReply: FC = ({ onClick, withLabel= true }) => {
10 | const { t } = useTranslation();
11 |
12 | return (
13 |
14 |
15 | {withLabel && {t('buttons.Reply')}}
16 |
17 | );
18 | };
19 |
20 | export default ButtonReply;
21 |
--------------------------------------------------------------------------------
/src/components/common/button/button-cancel/ButtonCancel.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | &:global(.MuiButton-outlinedPrimary)
5 | border-color: $color_button_outline
6 | color: $color_text_secondary
7 |
8 | &:hover
9 | border-color: $color_button_outline_hover
10 |
--------------------------------------------------------------------------------
/src/components/common/button/button-cancel/ButtonCancel.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './ButtonCancel.module.sass';
3 | import ButtonComponent from '../button-component/ButtonComponent';
4 | import { ButtonCancelProps } from 'src/models/common/button';
5 |
6 | const ButtonCancel: FC = (props) => {
7 | const className = props.className
8 | ? `${styles.button} ${props.className}`
9 | : styles.button;
10 |
11 | return (
12 |
18 | {props.children}
19 |
20 | );
21 | };
22 |
23 | export default ButtonCancel;
24 |
--------------------------------------------------------------------------------
/src/components/common/button/button-close/ButtonClose.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 |
--------------------------------------------------------------------------------
/src/components/common/button/button-close/ButtonClose.tsx:
--------------------------------------------------------------------------------
1 | import styles from '../../../modal/Modal.module.sass';
2 | import CloseIcon from '@mui/icons-material/Close';
3 | import { IconButton } from '@mui/material';
4 | import { FC } from 'react';
5 | import { IconButtonProps } from '@mui/material/IconButton/IconButton';
6 |
7 | const ButtonClose: FC = ({ onClick, ...props }) => {
8 | return (
9 |
10 |
11 |
12 | );
13 | };
14 |
15 | export default ButtonClose;
16 |
--------------------------------------------------------------------------------
/src/components/common/button/button-comment/ButtonComment.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .value
4 | color: $color_text_dark_gray !important
5 | font-weight: 400 !important
6 |
--------------------------------------------------------------------------------
/src/components/common/button/button-comment/ButtonComment.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './ButtonComment.module.sass';
3 | import ButtonIcon from '../button-icon/ButtonIcon';
4 | import Text from '../../text/Text';
5 | import { TextSizes } from 'src/models/common/typography';
6 | import { ButtonCommentProps } from 'src/models/common/button';
7 | import IconComment from "../../icons/IconComment";
8 |
9 | const ButtonComment: FC = ({ value, ...props }) => {
10 | return (
11 |
12 |
13 | {value ? (
14 |
15 | {value}
16 |
17 | ) : null}
18 |
19 | );
20 | };
21 |
22 | export default ButtonComment;
23 |
--------------------------------------------------------------------------------
/src/components/common/button/button-component/ButtonComponent.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | display: inline-flex
5 | column-gap: 0.785rem
6 | min-width: 99px
7 | height: 36px
8 | padding-left: $space_tiny
9 | padding-right: $space_tiny
10 | font-size: $font_normal
11 | font-weight: 500
12 | box-shadow: none
13 |
14 | @media (max-width: 480px)
15 | width: 90px
16 |
17 | &:global(.MuiButton-containedPrimary)
18 | background-color: $color_button_primary_bg
19 | box-shadow: none
20 |
21 | &:hover
22 | background-color: $color_button_primary_bg
23 |
24 | &:global(.MuiButton-outlinedPrimary)
25 | border-color: $color_button_outline_primary
26 | color: $color_text_primary
27 |
28 | &:hover
29 | border-color: $color_button_outline_primary
30 |
31 | &:global(.MuiButton-root)[disabled]
32 | background-color: $color_button_inactive_bg
33 | color: $color_text_disabled_color
34 |
35 | svg
36 | color: $color_icon_dark_gray
37 |
--------------------------------------------------------------------------------
/src/components/common/button/button-component/ButtonComponent.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './ButtonComponent.module.sass';
3 | import Button from '@mui/material/Button';
4 | import { ButtonComponentProps } from 'src/models/common/button';
5 |
6 | const ButtonComponent: FC = (props) => {
7 | const {
8 | variant = 'outlined',
9 | children,
10 | disabled = false,
11 | className: inputClassName,
12 | } = props;
13 | const className = inputClassName
14 | ? `${styles.button} ${inputClassName}`
15 | : styles.button;
16 |
17 | return (
18 |
27 | );
28 | };
29 |
30 | export default ButtonComponent;
31 |
--------------------------------------------------------------------------------
/src/components/common/button/button-edit/ButtonEdit.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | $size_button: 2.25rem
4 | $size_svg: 20px
5 |
6 | .button
7 | &:global(.MuiButton-root)
8 | display: inline-flex
9 | max-width: $size_button
10 | min-width: $size_button
11 | height: $size_button
12 | border-radius: 50%
13 | border: 1px solid $color_button_outline
14 |
15 | &:hover
16 | border-color: $color_button_outline_primary
17 |
18 | svg
19 | width: $size_svg
20 | height: $size_svg
21 | fill: $color_icon_primary
22 |
--------------------------------------------------------------------------------
/src/components/common/button/button-edit/ButtonEdit.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './ButtonEdit.module.sass';
3 | import { ButtonProps } from '@mui/material/Button/Button';
4 | import ButtonIcon from '../button-icon/ButtonIcon';
5 | import ModeEditOutlineOutlinedIcon from '@mui/icons-material/ModeEditOutlineOutlined';
6 |
7 | const ButtonEdit: FC = (props) => {
8 | return (
9 |
10 |
11 |
12 | );
13 | };
14 |
15 | export default ButtonEdit;
16 |
--------------------------------------------------------------------------------
/src/components/common/button/button-entity/ButtonEntity.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .spaceButton
4 | height: 36px
5 | font-size: $font_normal
6 | letter-spacing: 0
7 | padding: 0 $space_normal
8 |
9 | span
10 | padding-right: 0.5rem
11 |
12 | .postButton
13 | color: $color_text_secondary
14 | border-color: $color_line_outline
15 |
--------------------------------------------------------------------------------
/src/components/common/button/button-entity/ButtonEntity.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { Button } from '@mui/material';
3 | import classNames from 'classnames';
4 | import styles from './ButtonEntity.module.sass';
5 | import { useRouter } from 'next/router';
6 | import { useTranslation } from 'react-i18next';
7 | import { ButtonEntityProps } from 'src/models/common/button';
8 |
9 |
10 | const ButtonEntity: FC = ({ typeEntity, ...props }) => {
11 | const router = useRouter();
12 | const { t } = useTranslation();
13 |
14 | const createEntity = () => router.push(typeEntity === 'post' ? '/posts/new' : '/new');
15 |
16 | return (
17 |
28 | );
29 | };
30 |
31 | export default ButtonEntity;
32 |
--------------------------------------------------------------------------------
/src/components/common/button/button-icon/ButtonIcon.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | display: flex
5 | align-items: center
6 | column-gap: .5rem
7 | color: $color_text_dark_gray !important
8 | box-shadow: none
9 |
10 | svg
11 | fill: $color_icon_dark_gray
12 |
13 | &:hover
14 | &:global(.MuiButton-root)
15 | background: none
16 |
17 | &:global(.MuiButton-contained)
18 | box-shadow: none
19 | &:hover
20 | background-color: $color_button_primary_bg
21 |
--------------------------------------------------------------------------------
/src/components/common/button/button-icon/ButtonIcon.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './ButtonIcon.module.sass';
3 | import { ButtonProps } from '@mui/material/Button/Button';
4 | import Button from '@mui/material/Button';
5 |
6 | const ButtonIcon: FC = (props) => {
7 | const className = props.className
8 | ? `${styles.button} ${props.className}`
9 | : styles.button;
10 |
11 | return (
12 |
15 | );
16 | };
17 |
18 | export default ButtonIcon;
19 |
--------------------------------------------------------------------------------
/src/components/common/button/button-notification/ButtonNotification.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import styles from './ButtonNotificatoin.module.sass';
3 | import NotificationsNoneIcon from '@mui/icons-material/NotificationsNone';
4 | import { useRouter } from 'next/router';
5 | import { Badge } from '@mui/material';
6 | import { ButtonNotificationProps } from 'src/models/common/button';
7 |
8 |
9 | const ButtonNotification: FC = ({ value = 0 }) => {
10 | const router = useRouter();
11 |
12 | return (
13 | router.push('/notifications')}
16 | className={styles.button}
17 | >
18 |
19 |
20 | );
21 | };
22 |
23 | export default ButtonNotification;
24 |
--------------------------------------------------------------------------------
/src/components/common/button/button-notification/ButtonNotificatoin.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | span
5 | background-color: $red-color
6 | color: $color_text_white
7 |
8 | svg
9 | fill: $color_icon_dark_gray !important
10 |
--------------------------------------------------------------------------------
/src/components/common/button/button-options/ButtonOptions.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .option
4 | svg
5 | fill: $color_icon_dark_gray
6 |
7 | .item
8 | padding: $space_normal 1.125rem $space_normal $space_small
9 |
10 | & :global(.MuiListItemText-root)
11 | margin-left: $space_small
12 | font-size: $font_normal
13 | color: $color_text_dark_gray
14 |
15 | & :global(.MuiListItemIcon-root)
16 | min-width: 2rem
17 | display: flex
18 | justify-content: center
19 |
20 | .menu
21 | display: inline-block
22 |
23 | & :global(.MuiSvgIcon-root)
24 | fill: $color_icon_dark_gray
25 |
--------------------------------------------------------------------------------
/src/components/common/button/button-profile/ButtonProfile.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .profile
4 | display: flex
5 | align-items: center
6 | column-gap: $space_tiny
7 | padding: 0
8 | cursor: pointer
9 |
10 | .name
11 | display: block
12 | text-align: left
13 | white-space: nowrap
14 | color: $color_text_secondary
15 | font-weight: 500
16 | line-height: 1.065rem !important
17 |
18 | .mobileName
19 | text-overflow: ellipsis
20 | max-width: 30vw
21 | overflow: hidden
22 |
23 |
24 | .balance
25 | font-size: $font_small !important
26 |
27 | *
28 | color: $color_text_dark_gray !important
29 | line-height: 1.065rem !important
30 |
--------------------------------------------------------------------------------
/src/components/common/button/button-profile/ButtonProfile.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import styles from './ButtonProfile.module.sass';
3 | import AvatarElement from '../../avatar/AvatarElement';
4 | import { AvatarSizes } from 'src/models/common/avatar';
5 | import Text from '../../text/Text';
6 | import { TextSizes } from 'src/models/common/typography';
7 | import classNames from 'classnames';
8 | import Balance from '../../balance/Balance';
9 | import { useResponsiveSize } from 'src/components/responsive/ResponsiveContext';
10 |
11 | interface ButtonProfileProps {
12 | onClick: () => void,
13 | avatar?: string,
14 | address: string,
15 | name?: string,
16 | }
17 |
18 | const ButtonProfile: FC = ({ onClick, name, avatar, address }) => {
19 | const { isMobile } = useResponsiveSize();
20 |
21 | return (
22 |
48 | );
49 | };
50 |
51 | export default ButtonProfile;
52 |
--------------------------------------------------------------------------------
/src/components/common/button/button-qr/ButtonQr.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import styles from '../../address/Address.module.sass';
3 | import ButtonIcon from '../button-icon/ButtonIcon';
4 | import Image from '../../image/Image';
5 | import { ButtonQrProps } from 'src/models/common/button';
6 |
7 | const ButtonQr: FC = ({ sizeIcon: size, onClick, ...props }) => {
8 | return (
9 |
10 |
16 |
17 | );
18 | };
19 |
20 | export default ButtonQr;
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/components/common/button/button-send-tips/ButtonSendTips.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | &:global(.MuiButton-root)
5 | color: $color_text_secondary
6 | border: 1px solid $color_button_outline
7 | background-color: $color_button_basic_bg
8 |
9 | &[disabled]
10 | color: $color_text_dark_gray
11 | background-color: $color_button_inactive_bg
12 | border: none
13 |
--------------------------------------------------------------------------------
/src/components/common/button/button-send-tips/ButtonSendTips.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import styles from './ButtonSendTips.module.sass';
3 | import ButtonComponent from '../button-component/ButtonComponent';
4 | import { useTranslation } from 'react-i18next';
5 | import { ButtonComponentProps } from 'src/models/common/button';
6 | import classNames from 'classnames';
7 |
8 | const ButtonSendTips: FC = ({ onClick, disabled, className, ...props }) => {
9 | const { t } = useTranslation();
10 |
11 | return (
12 |
18 | {t('buttons.sendTips')}
19 |
20 | );
21 | };
22 |
23 | export default ButtonSendTips;
24 |
--------------------------------------------------------------------------------
/src/components/common/button/button-share/ButtonShare.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .label
4 | font-weight: 400 !important
5 |
6 | .value
7 | color: $color_text_dark_gray !important
8 | font-weight: 400 !important
9 |
--------------------------------------------------------------------------------
/src/components/common/button/button-share/ButtonShare.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import Text from '../../text/Text';
3 | import { TextSizes } from 'src/models/common/typography';
4 | import styles from './ButtonShare.module.sass';
5 | import ButtonIcon from '../button-icon/ButtonIcon';
6 | import { ButtonShareProps } from 'src/models/common/button';
7 | import { useTranslation } from 'react-i18next';
8 | import RepeatIcon from '@mui/icons-material/Repeat'
9 |
10 | const ButtonShare: FC = ({
11 | onClick,
12 | isShowLabel,
13 | value ,
14 | }) => {
15 | const { t } = useTranslation()
16 |
17 | return (
18 |
19 |
20 | {isShowLabel && (
21 |
22 | {t('buttons.share')}
23 | {' '}
24 | {value || 0 > 0 ? `(${value})` : null}
25 |
26 | )}
27 | {!isShowLabel && value || 0 > 0 ? (
28 |
29 | {value}
30 |
31 | ) : null}
32 |
33 | );
34 | };
35 |
36 | export default ButtonShare;
37 |
--------------------------------------------------------------------------------
/src/components/common/button/button-sign-in/ButtonSignIn.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import ButtonComponent from '../button-component/ButtonComponent';
3 | import { ButtonComponentProps } from 'src/models/common/button';
4 | import { useTranslation } from 'react-i18next';
5 |
6 | const ButtonSignIn: FC = ({ onClick, ...props }) => {
7 | const { t } = useTranslation();
8 |
9 | return (
10 |
14 | {t('buttons.signIn')}
15 |
16 | );
17 | };
18 |
19 | export default ButtonSignIn;
20 |
--------------------------------------------------------------------------------
/src/components/common/button/button-sign-out/ButtonSignOut.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | width: 100% !important
5 | text-transform: uppercase !important
6 |
--------------------------------------------------------------------------------
/src/components/common/button/button-sign-out/ButtonSignOut.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import ButtonComponent from '../button-component/ButtonComponent';
3 | import { ButtonComponentProps } from 'src/models/common/button';
4 | import { useTranslation } from 'react-i18next';
5 | import styles from './ButtonSignOut.module.sass';
6 |
7 | const ButtonSignOut: FC = ({ onClick, ...props }) => {
8 | const { t } = useTranslation();
9 |
10 | return (
11 |
16 | {t('buttons.signOut')}
17 |
18 | );
19 | };
20 |
21 | export default ButtonSignOut;
22 |
--------------------------------------------------------------------------------
/src/components/common/button/button-toggler-visibility/ButtonsTogglerVisibility.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | white-space: nowrap
5 | &:global(.MuiButton-outlinedPrimary)
6 | min-height: 24px
7 | height: max-content
8 | padding: $space_mini 0.625rem
9 | font-size: $font_small
10 | font-weight: 500
11 | color: $color_text_secondary
12 | border-color: $color_button_outline
13 |
14 | &:disabled
15 | padding: $space_mini $space_tiny
16 |
--------------------------------------------------------------------------------
/src/components/common/button/button-wtire-post/ButtonWritePost.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import ButtonComponent from '../button-component/ButtonComponent';
3 | import { useTranslation } from 'react-i18next';
4 | import { ButtonComponentProps } from 'src/models/common/button';
5 | import { useRouter } from 'next/router';
6 |
7 | const ButtonWritePost: FC = ({
8 | onClick,
9 | disabled,
10 | ...props
11 | }) => {
12 | const { t } = useTranslation();
13 | const router = useRouter();
14 |
15 | const writePost = () => {
16 | onClick && onClick();
17 | router.push('/posts/new');
18 | };
19 |
20 | return (
21 |
27 | {t('buttons.writePost')}
28 |
29 | );
30 | };
31 |
32 | export default ButtonWritePost;
33 |
--------------------------------------------------------------------------------
/src/components/common/button/buttons-vote/ButtonsVote.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .downvote
4 | fill: $color_icon_downvote_active
5 |
6 | &:global(.MuiSvgIcon-root)
7 | fill: $color_icon_downvote_active
8 |
9 | .red
10 | color: $color_icon_downvote_active !important
11 | font-weight: 400 !important
12 |
13 | .label
14 | font-weight: 400 !important
15 |
16 | .upvote
17 | fill: $color_icon_upvote_active
18 |
19 | &:global(.MuiSvgIcon-root)
20 | fill: $color_icon_upvote_active
21 |
22 | .green
23 | color: $color_icon_upvote_active !important
24 | font-weight: 400 !important
25 |
26 | .value
27 | color: $color_text_dark_gray
28 | font-weight: 400
29 |
--------------------------------------------------------------------------------
/src/components/common/button/buttons-vote/voting.ts:
--------------------------------------------------------------------------------
1 | import { ReactionType, PostStruct } from '@subsocial/api/types/dto';
2 | import { Reaction } from 'src/store/features/reactions/myPostReactionsSlice';
3 |
4 | type Props = {
5 | post: PostStruct;
6 | oldReaction?: Reaction;
7 | newKind: ReactionType;
8 | };
9 |
10 | export const getPostStructWithUpdatedCounts = ({
11 | post,
12 | newKind,
13 | oldReaction,
14 | }: Props) => {
15 | let { upvotesCount, downvotesCount } = post;
16 | const { reactionId, kind: oldKind } = oldReaction || ({} as Reaction);
17 |
18 | const noOldReaction = !reactionId;
19 | const isNewKindUpvote = newKind === 'Upvote';
20 |
21 | if (noOldReaction) {
22 | isNewKindUpvote ? upvotesCount++ : downvotesCount++;
23 | } else if (oldKind === newKind) {
24 | isNewKindUpvote ? upvotesCount-- : downvotesCount--;
25 | } else {
26 | if (isNewKindUpvote) {
27 | upvotesCount++;
28 | downvotesCount--;
29 | } else {
30 | upvotesCount--;
31 | downvotesCount++;
32 | }
33 | }
34 |
35 | return { ...post, upvotesCount, downvotesCount };
36 | };
37 |
--------------------------------------------------------------------------------
/src/components/common/card-edit/CardEdit.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .warningText
4 | display: flex
5 | justify-content: center
6 | font-size: $font_huge
7 | color: $color_text_error
8 |
9 | .form
10 | display: flex
11 | flex-direction: column
12 | gap: $space_normal
13 | padding: $space_normal
14 |
15 |
16 | .button
17 | width: auto
18 | min-width: 110px
19 | letter-spacing: .15px
20 | font-size: $font_normal
21 | padding: 0 28px
22 |
23 | .actions
24 | justify-content: flex-end
25 | gap: 1rem
26 | padding: 0
27 |
28 | .editor
29 | &:global(.MarkdownEditor .CodeMirror-scroll)
30 | min-height: 8.625rem !important
31 |
32 | & :global(.CodeMirror-focused)
33 | border: 2px solid $color_button_outline
34 |
--------------------------------------------------------------------------------
/src/components/common/card-wrapper/CardWrapper.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .card
4 | max-width: 765px
5 | width: 100%
6 | margin: 0 auto
7 |
8 | & :global(.MuiCardContent-root:last-child)
9 | padding-bottom: $space_normal
10 |
--------------------------------------------------------------------------------
/src/components/common/card-wrapper/CardWrapper.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { CardProps } from '@mui/material/Card/Card';
3 | import { Card } from '@mui/material';
4 | import styles from './CardWrapper.module.sass';
5 |
6 | const CardWrapper: FC = ({
7 | children,
8 | className: inputClassName,
9 | ...props
10 | }) => {
11 | const className = inputClassName
12 | ? `${inputClassName} ${styles.card}`
13 | : styles.card;
14 |
15 | return (
16 |
17 | {children}
18 |
19 | );
20 | };
21 |
22 | export default CardWrapper;
23 |
--------------------------------------------------------------------------------
/src/components/common/comments/CommentAction.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import styles from './Comments.module.sass';
3 | import ButtonReply from '../button/ButtonReply';
4 | import { CommentActionProps } from 'src/models/comments';
5 | import ButtonVotes from '../button/buttons-vote/ButtonVotes';
6 | import { ReactionEnum } from '@subsocial/api/types/dto';
7 | import { useResponsiveSize } from "../../responsive/ResponsiveContext";
8 |
9 | const CommentAction: FC = ({ onReply, comment }) => {
10 | const { isMobile } = useResponsiveSize();
11 |
12 | return (
13 |
14 |
19 |
24 |
28 |
29 | );
30 | };
31 |
32 | export default CommentAction;
33 |
--------------------------------------------------------------------------------
/src/components/common/editor/Editor.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, useMemo } from 'react';
2 | import { EditorProps } from 'src/models/common/editor';
3 | import dynamic from 'next/dynamic';
4 | const SimpleMdeReact = dynamic(() => import('react-simplemde-editor'), {
5 | ssr: false,
6 | });
7 | import sanitizeHtml from 'sanitize-html';
8 | import classNames from 'classnames';
9 |
10 | const Editor: FC = ({
11 | placeholder,
12 | autofocus,
13 | className: inputClassName,
14 | toolbar = true,
15 | ...props
16 | }) => {
17 | const className = classNames('MarkdownEditor', {
18 | hideToolbar: !toolbar,
19 | [inputClassName as string]: inputClassName,
20 | });
21 |
22 | const options = useMemo(
23 | () => ({
24 | placeholder,
25 | autofocus,
26 | spellChecker: false,
27 | renderingConfig: {
28 | sanitizerFunction: (html: string) => sanitizeHtml(html),
29 | },
30 | }),
31 | []
32 | );
33 |
34 | return ;
35 | };
36 |
37 | export default Editor;
38 |
--------------------------------------------------------------------------------
/src/components/common/empty/EmptyComponent.module.sass:
--------------------------------------------------------------------------------
1 | @import "src/styles/vars"
2 |
3 | .empty
4 | display: flex
5 | justify-content: center
6 | align-items: center
7 | height: 70px
8 | padding: 0px 1em
9 | width: 100%
10 |
11 |
--------------------------------------------------------------------------------
/src/components/common/empty/EmptyComponent.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import CardWrapper from '../card-wrapper/CardWrapper';
3 | import Text from '../text/Text';
4 | import { TextSizes } from 'src/models/common/typography';
5 | import { EmptyProps } from 'src/models/common/empty';
6 | import styles from './EmptyComponent.module.sass';
7 |
8 | const EmptyComponent: FC = ({ text }) => {
9 | return (
10 |
11 |
12 | {text}
13 |
14 |
15 | );
16 | };
17 |
18 | export default EmptyComponent;
19 |
--------------------------------------------------------------------------------
/src/components/common/file/FileWarning.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from "./File.module.sass";
3 | import { useTranslation } from "react-i18next";
4 | import { config } from 'src/config'
5 |
6 | const FileWarning = () => {
7 | const { t } = useTranslation();
8 |
9 | return (
10 | {t('imageShouldBeLessThan', { limit: config.loadImageLimitMb / (1024 * 1024) })}
11 | );
12 | };
13 |
14 | export default FileWarning;
15 |
--------------------------------------------------------------------------------
/src/components/common/hidden-component/HiddenComponent.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .warning
4 | border-bottom: 1px solid $color_line_outline_warning
5 |
6 | & :global(.MuiAlert-action)
7 | padding: 0
8 | margin-right: 0
9 | align-items: center
10 |
11 | .button
12 | &:global(.MuiButton-outlinedPrimary)
13 | width: 100px
14 | height: 24px
15 | padding: 0
16 | font-size: $font_small
17 | font-weight: 500
18 | color: $color_text_secondary
19 | border-color: $color_button_outline
--------------------------------------------------------------------------------
/src/components/common/hidden-component/HiddenComponent.tsx:
--------------------------------------------------------------------------------
1 | import styles from './HiddenComponent.module.sass';
2 | import ErrorIcon from '@mui/icons-material/Error';
3 | import { Alert } from '@mui/material';
4 | import { ButtonTogglerVisibility } from '../button/button-toggler-visibility/ButtonTogglerVisibility';
5 | import { TypeContent } from 'src/models/common/button';
6 | import { PostData, PostStruct, SpaceData } from '@subsocial/api/types/dto';
7 | import { useTranslation } from 'react-i18next';
8 | import { memo } from 'react';
9 |
10 | const HiddenComponent = ({
11 | data,
12 | typeContent,
13 | }: {
14 | data: PostData | SpaceData;
15 | typeContent: TypeContent;
16 | }) => {
17 | const { t } = useTranslation();
18 |
19 | const message = ((): string => {
20 | switch (typeContent) {
21 | case TypeContent.Post:
22 | return t('generalMessages.hiddenMessage', {type: t('Post').toLowerCase()});
23 | case TypeContent.Space:
24 | return t('generalMessages.hiddenMessage', {type: t('Space').toLowerCase()});
25 | case TypeContent.Comment:
26 | return t('generalMessages.hiddenMessage', {type: t('comment')});
27 | default:
28 | return '';
29 | }
30 | })();
31 |
32 | return (
33 |
42 | }
43 | icon={}
44 | >
45 | {message}
46 |
47 | );
48 | };
49 |
50 | export default memo(HiddenComponent);
51 |
--------------------------------------------------------------------------------
/src/components/common/icons/IconComment.tsx:
--------------------------------------------------------------------------------
1 | import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon";
2 | import { FC } from "react";
3 |
4 | const IconComment: FC = (props) => {
5 | return (
6 |
7 |
8 |
9 | );
10 | };
11 |
12 | export default IconComment;
13 |
--------------------------------------------------------------------------------
/src/components/common/icons/IconDislike.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './Icons.module.sass';
3 | import ThumbDownAltOutlinedIcon from '@mui/icons-material/ThumbDownAltOutlined';
4 | import ThumbDownIcon from '@mui/icons-material/ThumbDown';
5 | import { IconVoteProps } from 'src/models/common/icon';
6 |
7 | const IconDislike: FC = ({ type }) => {
8 | switch (type) {
9 | case 'outline':
10 | return ;
11 | case 'contained':
12 | return ;
13 | default:
14 | return ;
15 | }
16 | };
17 |
18 | export default IconDislike;
19 |
--------------------------------------------------------------------------------
/src/components/common/icons/IconLike.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './Icons.module.sass';
3 | import ThumbUpIcon from '@mui/icons-material/ThumbUp';
4 | import ThumbUpOutlinedIcon from '@mui/icons-material/ThumbUpOutlined';
5 | import { IconVoteProps } from 'src/models/common/icon';
6 |
7 | const IconLike: FC = ({ type }) => {
8 | switch (type) {
9 | case 'outline':
10 | return ;
11 | case 'contained':
12 | return ;
13 | default:
14 | return ;
15 | }
16 | };
17 |
18 | export default IconLike;
19 |
--------------------------------------------------------------------------------
/src/components/common/icons/Icons.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .like
4 | fill: $color_icon_upvote_active
5 | &:global(.MuiSvgIcon-root)
6 | fill: $color_icon_upvote_active
7 |
8 | .dislike
9 | fill: $color_icon_downvote_active
10 | &:global(.MuiSvgIcon-root)
11 | fill: $color_icon_downvote_active
12 |
--------------------------------------------------------------------------------
/src/components/common/image/Image.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, ImgHTMLAttributes } from 'react';
2 |
3 | const Image: FC> = ({ alt, ...props }) => (
4 |
5 | );
6 |
7 | export default Image;
8 |
--------------------------------------------------------------------------------
/src/components/common/infinity-list/InfinityListScroll.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .list
4 | display: flex
5 | flex-direction: column
6 | align-items: center
7 | row-gap: 1rem
8 | overflow: hidden
9 | & :global(.MuiContainer-root)
10 | width: 300px
11 |
--------------------------------------------------------------------------------
/src/components/common/inputs/input-money/InputMoney.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .input
4 | height: 36px
5 | width: 100%
6 |
7 | .currency
8 | color: $color_text_secondary !important
9 | font-size: $font_normal
10 |
11 | .error
12 | color: $color_text_error
--------------------------------------------------------------------------------
/src/components/common/inputs/input/Input.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .input
4 | & :global(.MuiOutlinedInput-root)
5 | min-height: 54px !important
6 | padding: 0
7 |
8 | &:global(.Mui-focused .MuiOutlinedInput-notchedOutline)
9 | border: 2px solid $color_button_outline !important
10 |
11 | & :global(.MuiOutlinedInput-notchedOutline)
12 | border-color: $color_button_outline !important
13 |
14 | & :global(.MuiInputLabel-root.Mui-error)
15 | color: $color_text_dark_gray !important
16 |
17 | & :global(.MuiFormLabel-asterisk)
18 | color: $color_text_error !important
19 |
--------------------------------------------------------------------------------
/src/components/common/inputs/input/Input.tsx:
--------------------------------------------------------------------------------
1 | import { TextField } from '@mui/material';
2 | import { FC } from 'react';
3 | import styles from './Input.module.sass';
4 | import { InputProps } from 'src/models/common/input';
5 |
6 | const Input: FC = (props) => {
7 | return (
8 |
26 | );
27 | };
28 |
29 | export default Input;
30 |
--------------------------------------------------------------------------------
/src/components/common/inputs/tags-input/TagsInput.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .tags
4 | & :global(.MuiInputBase-root)
5 | flex-wrap: wrap
6 | gap: 0.5rem
7 |
8 | & :global(.MuiOutlinedInput-input)
9 | height: 26px !important
10 | width: min-content
11 | min-width: 270px
12 | padding: 0 !important
13 |
14 | & label ~ :global(.MuiInputBase-root)
15 | padding: $space_tiny $space_normal
16 |
17 | textarea
18 | padding: 0 !important
19 |
20 |
--------------------------------------------------------------------------------
/src/components/common/links/icon-link/IconLink.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .link
4 | &:hover
5 | img,
6 | svg
7 | filter: invert(35%) sepia(90%) saturate(4187%) hue-rotate(308deg) brightness(95%) contrast(93%)
8 | fill: $color_icon_primary
9 |
--------------------------------------------------------------------------------
/src/components/common/links/icon-link/IconLink.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './IconLink.module.sass';
3 | import { getLinkBrand, getLinkIcon } from '../../../utils/socialIcons';
4 | import { IconLinkProps } from 'src/models/common/link';
5 |
6 | const IconLink: FC = ({ link }) => {
7 | const brand = getLinkBrand(link);
8 |
9 | return (
10 |
11 | {getLinkIcon(brand)}
12 |
13 | );
14 | };
15 |
16 | export default IconLink;
17 |
--------------------------------------------------------------------------------
/src/components/common/links/link/Link.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .link
4 | transition: color 100ms ease-in
5 | cursor: pointer
6 |
7 | &:not(.image):hover
8 | color: $color_text_primary
9 | img
10 | filter: invert(35%) sepia(90%) saturate(4187%) hue-rotate(308deg) brightness(95%) contrast(93%)
11 | svg
12 | fill: $color_icon_primary
13 |
14 | .external
15 | color: $color_text_primary
16 |
--------------------------------------------------------------------------------
/src/components/common/links/link/Link.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import NextLink from 'next/link';
3 | import styles from './Link.module.sass';
4 | import { LinkProps } from 'src/models/common/link';
5 |
6 | const Link: FC = ({
7 | href,
8 | ext = false,
9 | image,
10 | as,
11 | className: inputClassName,
12 | children,
13 | ...props
14 | }) => {
15 | const className = inputClassName
16 | ? `${styles.link} ${inputClassName}`
17 | : styles.link;
18 | return ext ? (
19 |
26 | {children}
27 |
28 | ) : (
29 |
30 |
31 | {children}
32 |
33 |
34 | );
35 | };
36 |
37 | export default Link;
38 |
--------------------------------------------------------------------------------
/src/components/common/links/see-more/SeeMore.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .link
4 | font-size: $font_normal
5 | color: $color_text_primary
6 | font-weight: 500
7 | white-space: nowrap
8 |
--------------------------------------------------------------------------------
/src/components/common/links/see-more/SeeMore.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './SeeMore.module.sass';
3 | import Link from '../link/Link';
4 | import { SeeMoreProps } from 'src/models/common/link';
5 | import { useTranslation } from 'react-i18next';
6 |
7 | const SeeMore: FC = ({
8 | href,
9 | className: inputClassName,
10 | ...props
11 | }) => {
12 | const { t } = useTranslation();
13 | const className = inputClassName
14 | ? `${styles.link} ${inputClassName}`
15 | : styles.link;
16 | return (
17 |
18 | {t('general.seeMore')}
19 |
20 | );
21 | };
22 |
23 | export default SeeMore;
24 |
--------------------------------------------------------------------------------
/src/components/common/links/small-link/SmallLink.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .link
4 | font-size: $font-small
5 | &:hover
6 | text-decoration: underline
7 |
--------------------------------------------------------------------------------
/src/components/common/links/small-link/SmallLink.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import NextLink from 'next/link';
3 | import styles from './SmallLink.module.sass';
4 | import { SmallLinkProps } from '../../../../models/common/link';
5 |
6 | const SmallLink: FC = ({
7 | href,
8 | as,
9 | children,
10 | className: inputClassName,
11 | ...props
12 | }) => {
13 | const className = inputClassName
14 | ? `${styles.link} ${inputClassName}`
15 | : styles.link;
16 |
17 | return (
18 |
19 |
20 | {children}
21 |
22 |
23 | );
24 | };
25 |
26 | export default SmallLink;
27 |
--------------------------------------------------------------------------------
/src/components/common/loader/Loader.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .loader
4 | display: flex
5 | align-items: center
6 | column-gap: 1rem
7 | height: 70px
8 | justify-content: center
9 | padding: 0 !important
10 |
11 | .text
12 | font-style: italic
13 | color: $color_text_dark_gray
14 |
15 | .loader
16 | height: 20px
17 |
--------------------------------------------------------------------------------
/src/components/common/loader/Loader.tsx:
--------------------------------------------------------------------------------
1 | import { CircularProgress } from '@mui/material';
2 | import styles from './Loader.module.sass';
3 | import Text from '../text/Text';
4 | import { TextSizes } from 'src/models/common/typography';
5 | import { FC } from 'react';
6 |
7 | const Loader: FC<{ label?: string; className?: string }> = ({
8 | label,
9 | className: inputClassName,
10 | }) => {
11 | const className = inputClassName
12 | ? `${styles.loader} ${inputClassName}`
13 | : styles.loader;
14 | return (
15 |
16 |
17 | {label && (
18 |
19 | {label}
20 |
21 | )}
22 |
23 | );
24 | };
25 |
26 | export default Loader;
27 |
--------------------------------------------------------------------------------
/src/components/common/profile-followers/ProfileFollowers.module.sass:
--------------------------------------------------------------------------------
1 | @import "src/styles/vars"
2 |
3 | .follow
4 | display: flex
5 | column-gap: 0.6rem
6 |
7 | button
8 | padding: 0
9 | color: $color_text_secondary
10 |
11 | p
12 | margin: 0
13 |
--------------------------------------------------------------------------------
/src/components/common/select-spaces/SelectSpaces.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .form
4 | width: 100%
5 |
6 | .select
7 | & :global(.MuiSelect-select)
8 | display: flex
9 | gap: $space_small
10 | justify-content: flex-start
11 | align-items: center
12 | padding: 0 $space_normal 0 $space_small !important
13 | height: 40px
14 |
15 | & :global(.MuiAvatar-root)
16 | margin-right: 0
17 |
18 | .menu
19 | display: flex
20 | gap: $space_small
21 | justify-content: flex-start
22 | padding-left: $space_small
23 |
24 | .item
25 | min-width: auto
26 |
--------------------------------------------------------------------------------
/src/components/common/snackbar/Snackbar.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .snackbar
4 | max-width: 336px
5 | width: 100%
6 |
7 | & :global(.MuiAlert-action)
8 | svg
9 | fill: $color_icon_dark_gray
10 |
11 | .info
12 | & :global(.MuiAlert-icon)
13 | fill: $color_text_primary
14 |
15 | & :global(.MuiAlert-message)
16 | color: $color_text_secondary
17 |
18 | .error
19 | & :global(.MuiAlert-icon)
20 | fill: $color_icon_error
21 |
22 | & :global(.MuiAlert-message)
23 | color: $color_text_error
24 |
25 | .alert
26 | align-items: center
27 | background-color: $color_snackbar_bg
28 | box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0 , .05)
29 | width: 100%
30 |
31 | &:global(.MuiAlert-root)
32 | font-size: $font_small
33 | color: $color_text_secondary
34 |
35 | & :global(.MuiAlert-action)
36 | padding-top: 0
37 |
--------------------------------------------------------------------------------
/src/components/common/snackbar/Snackbar.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, useEffect } from 'react';
2 | import { Alert, Snackbar as SnackbarComponent } from '@mui/material';
3 | import styles from './Snackbar.module.sass';
4 | import { SnackbarProps } from 'src/models/common/snackbar';
5 | import Loader from '../loader/Loader';
6 |
7 | const Snackbar: FC = ({
8 | type,
9 | open,
10 | onClose,
11 | message,
12 | withAutoHide = true,
13 | withLoader = false,
14 | }) => {
15 | const classNameSnackbar = `${styles.snackbar} ${styles[type]}`;
16 |
17 | useEffect(() => () => onClose && onClose(), []);
18 |
19 | return (
20 |
26 | : undefined}
31 | >
32 | {message}
33 |
34 |
35 | );
36 | };
37 |
38 | export default Snackbar;
39 |
--------------------------------------------------------------------------------
/src/components/common/space-hidden-component/SpaceHiddenComponent.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .warning
4 | border-bottom: 1px solid $color_line_outline_warning
5 | align-items: center
6 |
7 | & :global(.MuiAlert-action)
8 | padding: 0
9 | margin-right: 0
10 | align-items: center
11 |
--------------------------------------------------------------------------------
/src/components/common/tabs/Tabs.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .tabs
4 | width: 100%
5 | & :global(.MuiButtonBase-root.MuiTab-root)
6 | font-size: $font_normal
7 |
8 | .Tab
9 | width: 100%
10 |
11 | .unselected
12 | & :global(.Mui-selected)
13 | color: $color_text_dark_gray !important
14 |
15 | & :global(.MuiTabs-indicator)
16 | display: none
17 |
--------------------------------------------------------------------------------
/src/components/common/tabs/Tabs.tsx:
--------------------------------------------------------------------------------
1 | import { SyntheticEvent, FC } from 'react';
2 | import styles from './Tabs.module.sass';
3 | import { Tab } from '@mui/material';
4 | import MaterialTabs from '@mui/material/Tabs';
5 | import { Box } from '@mui/system';
6 | import { TabsProps } from 'src/models/common/tabs';
7 |
8 | const Tabs: FC = ({
9 | className: inputClassName,
10 | tabs,
11 | setValue,
12 | value,
13 | unselected = false,
14 | }) => {
15 | const className = inputClassName
16 | ? `${inputClassName} ${styles.tabs} ${unselected && styles.unselected}`
17 | : inputClassName;
18 |
19 | const handleChange = (event: SyntheticEvent, newValue: number) => {
20 | setValue(newValue);
21 | };
22 |
23 | return (
24 | <>
25 |
26 |
34 | {tabs.map(({ label, tabValue }) => {
35 | return (
36 |
42 | );
43 | })}
44 |
45 |
46 | >
47 | );
48 | };
49 |
50 | export default Tabs;
51 |
--------------------------------------------------------------------------------
/src/components/common/tag/Tag.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .tags
4 | display: flex
5 | flex-wrap: wrap
6 | gap: 0.5rem
7 | padding: 0 $space_normal $space_normal
8 |
9 | .tag
10 | margin: 0
11 | font-size: $font_small !important
12 | color: $color_text_dark_gray !important
13 | text-transform: lowercase !important
14 |
15 | svg
16 | fill: $color_icon_dark_gray
17 |
--------------------------------------------------------------------------------
/src/components/common/tag/Tag.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './Tag.module.sass';
3 | import { Chip, ChipProps } from '@mui/material';
4 | import CloseIcon from '@mui/icons-material/Close';
5 |
6 | const Tag: FC = ({ label, className: inputClassName, ...props }) => {
7 | const className = inputClassName
8 | ? `${inputClassName} ${styles.tag}`
9 | : styles.tag;
10 |
11 | return (
12 | }
17 | />
18 | );
19 | };
20 |
21 | export default Tag;
22 |
--------------------------------------------------------------------------------
/src/components/common/tag/TagList.tsx:
--------------------------------------------------------------------------------
1 | import Tag from './Tag';
2 | import styles from './Tag.module.sass';
3 | import { FC } from 'react';
4 | import { TagListProps } from 'src/models/common/tags';
5 |
6 | const TagList: FC = ({ tags, className: inputClassName }) => {
7 | if (!tags || !tags.length) return null;
8 | const className = inputClassName
9 | ? `${styles.tags} ${inputClassName}`
10 | : styles.tags;
11 |
12 | return (
13 |
14 | {tags.map((tag) => (
15 |
16 | ))}
17 |
18 | );
19 | };
20 |
21 | export default TagList;
22 |
--------------------------------------------------------------------------------
/src/components/common/text/Text.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .text
4 | &:global(.MuiTypography-root)
5 | line-height: 1.5rem
6 | font-weight: 400
7 | text-wrap: wrap
8 |
9 | .normal
10 | &:global(.MuiTypography-root)
11 | font-size: $font_normal
12 |
13 | .secondary
14 | &:global(.MuiTypography-root)
15 | font-size: $font_small
16 |
--------------------------------------------------------------------------------
/src/components/common/text/Text.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './Text.module.sass';
3 | import { TextProps } from '../../../models/common/typography';
4 | import { Typography } from '@mui/material';
5 |
6 | const Text: FC = ({
7 | type,
8 | children,
9 | className: inputClassName,
10 | ...props
11 | }) => {
12 | const className = inputClassName
13 | ? `${styles.text} ${styles[type]} ${inputClassName}`
14 | : `${styles.text} ${styles[type]}`;
15 |
16 | return (
17 |
18 | {children}
19 |
20 | );
21 | };
22 |
23 | export default Text;
24 |
--------------------------------------------------------------------------------
/src/components/common/title/Title.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | $letter_spacing: .15px
4 |
5 | .title
6 | font-weight: 500
7 |
8 | &:global(.MuiTypography-root)
9 | font-weight: 500
10 |
11 | .profile
12 | &:global(.MuiTypography-root)
13 | font-size: $font_normal
14 |
15 | .preview
16 | &:global(.MuiTypography-root)
17 | font-size: $font_large
18 | letter-spacing: $letter_spacing
19 |
20 | .details
21 | &:global(.MuiTypography-root)
22 | font-size: $font_huge
23 | letter-spacing: $letter_spacing
24 |
--------------------------------------------------------------------------------
/src/components/common/title/Title.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './Title.module.sass';
3 | import { TitleProps } from '../../../models/common/typography';
4 | import { Typography } from '@mui/material';
5 |
6 | const Title: FC = ({
7 | type,
8 | variant = 'body1',
9 | children,
10 | className: inputClassName,
11 | ...props
12 | }) => {
13 | const className = inputClassName
14 | ? `${styles.title} ${styles[type]} ${inputClassName}`
15 | : `${styles.title} ${styles[type]}`;
16 |
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | };
23 |
24 | export default Title;
25 |
--------------------------------------------------------------------------------
/src/components/common/vote-user-item/VoteUserListItem.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .item
4 | max-width: 700px
5 | padding-top: 0
6 | padding-bottom: 0
7 | background-color: $color_modal-list_bg
8 |
9 | .info
10 | flex-grow: 1
11 | margin: 0.375rem 0
12 |
13 | .address
14 | color: $color_text_dark_gray
15 |
--------------------------------------------------------------------------------
/src/components/header/Header.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .bar
4 | box-shadow: none
5 | z-index: 1201
6 |
7 | @include tablet
8 | box-shadow: none
9 |
10 | .barWithMenu
11 | box-shadow: none
12 |
13 | .header
14 | display: flex
15 | justify-content: space-between
16 | width: 100%
17 | grid-template-columns: 1fr minmax(0, 765px) 1fr
18 | padding-left: 0
19 |
20 | @include tablet
21 | padding: 0
22 | grid-template-columns: 1fr 1fr
23 | grid-template-rows: 1fr 1fr
24 |
25 | .icon
26 | height: min-content
27 | margin-left: 0
28 | color: $color_text_secondary
29 |
30 |
31 | .label
32 | color: $color_text_secondary
33 |
34 | a
35 | white-space: nowrap
36 |
37 | @include tablet
38 | grid-area: 2 / 1 / 2 / 3
39 | justify-self: center
40 | max-width: 100%
41 |
42 | & :global(.MuiButtonBase-root.MuiTab-root)
43 | height: $header_height_on_desktop
44 |
45 | @include tablet
46 | height: $header_height_on_mobile
47 |
48 | & button:first-child
49 | margin-left: 1px
50 |
51 | & button:last-child
52 | margin-right: 1px
53 |
54 | .user
55 | display: flex
56 | align-items: center
57 | justify-self: flex-end
58 | column-gap: $space_normal
59 | cursor: pointer
60 |
61 | @include tablet
62 | padding-right: 1.1875rem
63 |
64 | .menu
65 | display: flex
66 | gap: $space_small
67 | align-items: center
68 | padding-left: $space_small
69 |
70 | @include tablet
71 | gap: $space_normal
72 | padding-left: $space_normal
73 |
--------------------------------------------------------------------------------
/src/components/home/HomePage.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .main
4 | align-items: center
5 |
6 | .box
7 | position: fixed
8 | z-index: 1
9 | max-width: 772px
10 | height: 72px
11 | width: 100%
12 | padding: $space_normal $space_mini 0
13 | margin-top: -$space_normal
14 | background-color: $color_page_bg
15 |
16 | @include tablet
17 | padding: 0
18 | max-width: 100vw
19 | height: 56px
20 |
21 | .tabs
22 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.2)
23 | background-color: $color_tabs_bg
24 |
25 | & :global(.MuiTab-root)
26 | height: 56px
27 |
28 | .content
29 | margin-top: 72px
30 | width: 100%
31 |
32 | @include tablet
33 | margin-top: 56px
34 |
--------------------------------------------------------------------------------
/src/components/layout/Layout.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .layout
4 | display: flex
5 | flex-direction: column
6 | row-gap: 1rem
7 | margin: 0 auto
8 | padding: $space_normal
9 |
--------------------------------------------------------------------------------
/src/components/layout/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import styles from './Layout.module.sass';
3 | import { Container, ContainerProps } from '@mui/material';
4 |
5 | const Layout: FC = ({
6 | children,
7 | className: inputClassName,
8 | ...props
9 | }) => {
10 | const className = inputClassName
11 | ? `${styles.layout} ${inputClassName}`
12 | : styles.layout;
13 |
14 | return (
15 | <>
16 |
17 | {children}
18 |
19 | >
20 | );
21 | };
22 |
23 | export default Layout;
24 |
--------------------------------------------------------------------------------
/src/components/layout/MainPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Page: React.FunctionComponent = ({ children }) => <>{children}>;
4 |
5 | const NextLayout: React.FunctionComponent = (props) => {
6 | return ;
7 | };
8 |
9 | export default NextLayout;
10 |
--------------------------------------------------------------------------------
/src/components/modal/Modal.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .modal
4 | display: flex
5 | justify-content: center
6 | align-items: center
7 |
8 | .close
9 | position: absolute
10 | right: 0
11 | top: 0
12 | margin: $space_normal
13 | padding: 0
14 | z-index: 2
15 |
16 | .box
17 | max-width: 500px
18 | width: 100%
19 | position: relative
20 | margin: $space_normal
21 | border-radius: 0.125rem
22 | overflow: hidden
23 |
--------------------------------------------------------------------------------
/src/components/modal/Modal.tsx:
--------------------------------------------------------------------------------
1 | import MaterialModal from '@mui/material/Modal';
2 | import { FC } from 'react';
3 | import styles from './Modal.module.sass';
4 | import { Box } from '@mui/system';
5 | import ButtonClose from '../common/button/button-close/ButtonClose';
6 | import { ModalProps } from 'src/models/modal';
7 |
8 | const Modal: FC = ({
9 | open,
10 | onClose,
11 | className: inputClassName,
12 | children,
13 | ...props
14 | }) => {
15 | const className = inputClassName
16 | ? `${inputClassName} ${styles.modal}`
17 | : styles.modal;
18 |
19 | return (
20 |
26 |
27 |
28 | {children}
29 |
30 |
31 | );
32 | };
33 |
34 | export default Modal;
35 |
--------------------------------------------------------------------------------
/src/components/modal/modal-adblock/ModalAdBlock.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | $size_svg: 18px
4 |
5 | .modal
6 | display: flex
7 | justify-content: center
8 | align-items: center
9 |
10 | .title
11 | padding-top: $space_big
12 | padding-bottom: $space_big
13 | display: flex
14 | align-items: center
15 | justify-content: center
16 |
17 | &:first-child
18 | margin-right: $space_small
19 |
20 | .content
21 | display: flex
22 | flex-direction: column
23 | align-items: center
24 | row-gap: 1.2rem
25 |
26 | .button
27 | min-width: 384px
28 |
29 | .main_text
30 | &:global(.MuiTypography-root)
31 | width: 340px
32 | text-align: center
33 | font-weight: 500
34 | word-break: break-all
35 | font-size: $font_huge
36 |
37 | .secondary_text
38 | &:global(.MuiTypography-root)
39 | width: 387px
40 | text-align: center
41 | font-weight: 400
42 | word-break: break-all
43 | font-size: $font_normal
44 |
45 | .box
46 | padding: 0.625rem $space_normal $space_normal
47 | max-width: 432px
48 | width: 100%
49 | max-height: 303px
50 | height: 100%
51 | position: relative
52 | margin: $space_normal
53 | border-radius: 0.125rem
54 | overflow: hidden
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/components/modal/modal-create-shared-post/ModalCreateSharedPost.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .modal
4 | display: flex
5 | justify-content: center
6 | align-items: center
7 |
8 | .box
9 | display: flex
10 | flex-direction: column
11 | gap: $space_big
12 | max-width: 780px
13 | min-height: 200px
14 | max-height: 90%
15 | width: 100%
16 | position: relative
17 | padding: $space_normal
18 | border-radius: 0.25rem
19 | overflow: auto
20 |
21 | .header
22 | display: flex
23 | justify-content: space-between
24 |
25 | .title
26 | line-height: 1.25rem
27 |
28 | .buttonClose
29 | padding: 0
30 |
31 | .form
32 | display: flex
33 | flex-direction: column
34 | gap: $space_normal
35 |
36 | .post
37 | border: 1px solid $color_line_outline
38 |
39 | & :global(.MuiCardContent-root)
40 | padding-bottom: $space_tiny
41 |
42 | .select
43 | padding: 0
44 |
45 | .buttons
46 | display: flex
47 | justify-content: flex-end
48 | gap: $space_normal
49 |
50 | .button
51 | padding: $space_tiny $space_large
52 | width: max-content
53 |
--------------------------------------------------------------------------------
/src/components/modal/modal-qr/ModalQr.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | $size_svg: 18px
4 |
5 | .modal
6 | display: flex
7 | justify-content: center
8 | align-items: center
9 |
10 | .close
11 | position: absolute
12 | right: 0
13 | top: 0
14 | margin: $space_small
15 | padding: 0
16 | z-index: 2
17 |
18 | svg
19 | width: $size_svg
20 | height: $size_svg
21 |
22 | .content
23 | display: flex
24 | flex-direction: column
25 | align-items: center
26 | row-gap: 1.2rem
27 |
28 | .buttons
29 | display: flex
30 | column-gap: 1rem
31 |
32 | .button
33 | min-width: 140px
34 |
35 | .address
36 | &:global(.MuiTypography-root)
37 | text-align: center
38 | font-weight: 500
39 | word-break: break-all
40 |
41 | .box
42 | padding: 0.625rem $space_normal $space_normal
43 | max-width: 330px
44 | width: 100%
45 | position: relative
46 | margin: $space_normal
47 | border-radius: 0.125rem
48 | overflow: hidden
49 |
50 | .title
51 | padding-bottom: $space_normal
52 |
--------------------------------------------------------------------------------
/src/components/modal/modal-reactions/ModalReactions.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .header
4 | position: relative
5 | box-shadow: 0 1px 8px rgba(0, 0, 0, 0.2)
6 | z-index: 1
7 |
8 | .title
9 | display: inline-block
10 | margin: $space_tiny $space_normal
11 |
12 | .scroll
13 | overflow-y: auto
14 | height: 60vh
15 | @include scroll
16 | .noData
17 | display: flex
18 | justify-content: center
19 | padding-top: 1em
20 |
21 | .label
22 | color: $color_text_dark_gray
23 | font-weight: 400
24 |
25 | .follow
26 | :global(.MuiListItem-root)
27 | &:first-child
28 | padding-top: 0 !important
29 |
30 | .tab
31 | width: 100%
32 |
33 |
--------------------------------------------------------------------------------
/src/components/modal/modal-sign-in/Accounts.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, Fragment } from 'react';
2 | import { Divider, List } from '@mui/material';
3 | import styles from './ModalSignIn.module.sass';
4 | import ModalListUserItem from './ModalListUserItem';
5 | import ModalSignInInfo from './ModalSignInInfo';
6 | import { fetchProfile } from 'src/store/features/profiles/profilesSlice';
7 | import { setMyAddress } from 'src/store/features/myAccount/myAccountSlice';
8 | import { useApi } from '../../api';
9 | import { AccountsModalProps } from 'src/models/account';
10 | import { useAppDispatch } from 'src/store/app/store';
11 |
12 | const Accounts: FC = (props) => {
13 | const dispatch = useAppDispatch();
14 | const { api } = useApi();
15 |
16 | const chooseAccount = async (address: string) => {
17 | await dispatch(fetchProfile({ api, id: address, reload: true }));
18 | await dispatch(setMyAddress(address));
19 | props.onClose();
20 | };
21 |
22 | return (
23 | <>
24 |
25 | {props.accounts.map((account) => (
26 |
27 |
28 |
29 |
30 | ))}
31 |
32 |
33 | >
34 | );
35 | };
36 |
37 | export default Accounts;
38 |
--------------------------------------------------------------------------------
/src/components/modal/modal-sign-in/ModalSignInInfo.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { TextSizes } from 'src/models/common/typography';
3 | import styles from './ModalSignIn.module.sass';
4 | import SmallLink from '../../common/links/small-link/SmallLink';
5 | import Text from '../../common/text/Text';
6 | import { useTranslation } from 'react-i18next';
7 |
8 | const ModalSignInInfo = () => {
9 | const { t } = useTranslation();
10 |
11 | return (
12 |
13 |
14 | {t('buttons.privacyPolicy')}
15 |
16 |
17 |
18 | {t('buttons.termsOfUse')}
19 |
20 |
21 | );
22 | };
23 |
24 | export default ModalSignInInfo;
25 |
--------------------------------------------------------------------------------
/src/components/modal/modal-sign-in/NoAccount.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Alert } from '@mui/material';
3 | import Link from '../../common/links/link/Link';
4 | import styles from './ModalSignIn.module.sass';
5 | import ModalSignInInfo from './ModalSignInInfo';
6 | import { useTranslation } from 'react-i18next';
7 |
8 | const NoAccount = () => {
9 | const { t } = useTranslation();
10 |
11 | return (
12 | <>
13 |
14 | {t('modals.login.noAccountScreen.noAccountFound')}{' '}
15 |
20 | Polkadot Extension
21 | {' '}
22 | {t('modals.login.noAccountScreen.createNew')}
23 | {' '}
24 | {t('modals.login.noAccountScreen.importExisting')}
25 |
26 |
27 |
28 | >
29 | );
30 | };
31 |
32 | export default NoAccount;
33 |
--------------------------------------------------------------------------------
/src/components/modal/modal-sign-in/Tokens.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ModalSignInInfo from './ModalSignInInfo';
3 | import ButtonComponent from '../../common/button/button-component/ButtonComponent';
4 | import styles from './ModalSignIn.module.sass';
5 | import { useTranslation } from 'react-i18next';
6 |
7 | const Tokens = () => {
8 | const { t } = useTranslation();
9 |
10 | return (
11 | <>
12 |
13 | {t('modals.noToken.button')}
14 |
15 |
16 |
17 | >
18 | )};
19 |
20 | export default Tokens;
21 |
--------------------------------------------------------------------------------
/src/components/post/loadSuggestedPostIdsFromEnv.ts:
--------------------------------------------------------------------------------
1 | import { getPageOfIds } from '../utils/getIds';
2 | import { AnySpaceId } from '@subsocial/api/types';
3 | import { bnsToIds } from '@subsocial/utils';
4 | import { PostId, SpaceId } from '@subsocial/api/types/dto';
5 | import { SubsocialApi } from '@subsocial/api';
6 |
7 | let suggestedPostIds: string[] | undefined = undefined;
8 |
9 | export const loadSuggestedPostIds = async (
10 | api: SubsocialApi,
11 | ids: SpaceId[]
12 | ) => {
13 | if (!api.subsocial) return [];
14 |
15 | const suggestedPostIdsPromises = ids.map((spaceId) =>
16 | api.subsocial.substrate.postIdsBySpaceId(spaceId as unknown as AnySpaceId)
17 | );
18 |
19 | const suggestedPostIdsArray = await Promise.all(suggestedPostIdsPromises);
20 |
21 | suggestedPostIds = bnsToIds(
22 | suggestedPostIdsArray.flat().sort((a, b) => b.sub(a).toNumber())
23 | );
24 |
25 | return suggestedPostIds;
26 | };
27 |
28 | export const getSuggestedPostIdsByPage = (
29 | ids: PostId[],
30 | size: number,
31 | page: number
32 | ) => getPageOfIds(ids, { page, size });
33 |
--------------------------------------------------------------------------------
/src/components/post/post-full/PostFull.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .post
4 | &:global(.MuiCardContent-root:last-child)
5 | padding: 0
6 |
7 | .title
8 | margin-bottom: $space_tiny
9 |
10 | .tags
11 | padding-top: 0
12 |
13 | .content
14 | font-size: $font_normal
15 |
16 | .mainPostContent
17 | display: flex
18 | width: 100%
19 | padding-top: 0
20 | padding-bottom: 0
21 |
22 | .mainSharedPostContent
23 | &:global(.MuiCardContent-root)
24 | padding-bottom: 0
25 |
26 | .sharedPostContent
27 | padding: 0 !important
28 |
29 | .sharedPostActions
30 | margin-top: -$space_small
31 |
32 | .postContent
33 | padding: 0
34 | flex-grow: 1
35 |
36 | .postActions
37 | margin: $space_mini 0 0
38 |
39 | .accountContent
40 | padding-right: 0
41 | padding-left: 0
42 |
43 | .imgContent
44 | width: 100%
45 | margin-top: $space_normal
46 | margin-bottom: $space_normal
47 | border-radius: 0.25rem
48 |
49 | .video
50 | margin-bottom: $space_normal
51 |
52 | .cardActions
53 | justify-content: space-evenly
--------------------------------------------------------------------------------
/src/components/post/post-page/PostPage.module.sass:
--------------------------------------------------------------------------------
1 | @import ../../../styles/vars
2 |
3 | .box
4 | box-shadow: 0 3px 10px rgba(153, 153, 153, .14), 0 3px 9px rgba(153, 153, 153, .12), 0 1px 20px rgba(153, 153, 153, .2)
5 |
6 | .panel
7 | padding: $space_normal
8 | display: flex
9 | align-items: center
10 | flex-direction: column
11 | gap: 0.75rem
12 |
--------------------------------------------------------------------------------
/src/components/post/upsert-page/UpsertPost.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | $letter_spacing: 0.15px
4 |
5 | .wrapper
6 | max-width: 628px
7 |
8 | .title
9 | padding-bottom: 0
10 |
11 | .tabs
12 | border-bottom: 1px solid $color_button_outline
13 |
14 | .select
15 | padding-top: $space_normal
16 |
17 | .file
18 | margin-top: $space_normal !important
19 |
20 | .form
21 | display: flex
22 | flex-direction: column
23 | gap: $space_normal
24 | padding-top: $space_big
25 |
26 | & :global(.mat-form-field-wrapper)
27 | margin: 0
28 |
29 | .buttonCancel
30 | letter-spacing: $letter_spacing
31 | width: auto
32 | min-width: 132px
33 |
34 | .buttonEdit
35 | letter-spacing: $letter_spacing
36 | width: auto
37 | min-width: 144px
38 |
39 | .buttons
40 | gap: $space_normal
41 | padding: 0
42 | justify-content: flex-end
43 | margin-top: -$space_mini
44 |
45 | .post
46 | border: 1px solid $color_button_outline
47 |
--------------------------------------------------------------------------------
/src/components/post/upsert-page/UpsertPost.tsx:
--------------------------------------------------------------------------------
1 | import { EntityId } from '@subsocial/api/types/dto';
2 | import { useRouter } from 'next/router';
3 | import { FC, useEffect } from 'react';
4 | import { useApi } from 'src/components/api';
5 | import { useSelectPost } from 'src/store/app/hooks';
6 | import { useAppDispatch } from 'src/store/app/store';
7 | import { fetchPosts } from 'src/store/features/posts/postsSlice';
8 | import { EditorPost } from './EditorPost';
9 |
10 | export const UpsertPost = () => {
11 | const router = useRouter();
12 | const { post } = router.query;
13 | const postId = post as string | undefined;
14 | const dispatch = useAppDispatch();
15 | const { api } = useApi();
16 | const postData = useSelectPost(postId as string | undefined);
17 | const isWithLink = !!postData?.post.content?.link;
18 |
19 | useEffect(() => {
20 | if (postId) {
21 | dispatch(fetchPosts({ ids: [postId as EntityId], api }));
22 | }
23 | }, []);
24 |
25 | return ;
26 | };
27 |
--------------------------------------------------------------------------------
/src/components/profile/profile-account/ProfileAccount.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | width: 100%
5 |
6 | .tooltip
7 | width: 100%
8 |
9 | .tabs
10 | & :global(.MuiButtonBase-root.MuiTab-root)
11 | font-size: $font_normal
12 |
13 | .option
14 | margin-left: $space_tiny
15 |
--------------------------------------------------------------------------------
/src/components/responsive/ResponsiveContext.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, PropsWithChildren, useContext } from 'react';
2 | import { useMediaQuery } from 'react-responsive';
3 |
4 | export type ResponsiveSizeState = {
5 | isMobile: boolean;
6 | isTablet: boolean;
7 | isDesktop: boolean;
8 | };
9 |
10 | export const ResponsiveContext = createContext({
11 | isDesktop: true,
12 | isMobile: false,
13 | isTablet: false,
14 | });
15 |
16 | export function ResponsiveProvider(props: PropsWithChildren) {
17 | const value = {
18 | isDesktop: useMediaQuery({ minWidth: 992 }),
19 | isTablet: useMediaQuery({ minWidth: 768, maxWidth: 991 }),
20 | isMobile: useMediaQuery({ maxWidth: 767 }),
21 | };
22 |
23 | return (
24 |
25 | {props.children}
26 |
27 | );
28 | }
29 |
30 | export function useResponsiveSize() {
31 | return useContext(ResponsiveContext);
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/space/reloadSpaceIdsFollowedByAccount.ts:
--------------------------------------------------------------------------------
1 | import { SubsocialSubstrateApi } from '@subsocial/api'
2 | import { AppDispatch } from 'src/store/app/store'
3 | import { upsertFollowedSpaceIdsByAccount } from 'src/store/features/spaceIds/followedSpaceIdsSlice'
4 | import { AccountId } from '@subsocial/api/types/dto'
5 | import { bnsToIds } from '@subsocial/utils'
6 | import BN from 'bn.js'
7 |
8 | type ReloadSpaceIds = {
9 | substrate: SubsocialSubstrateApi
10 | dispatch: AppDispatch
11 | account: AccountId
12 | }
13 |
14 | export const reloadSpaceIdsFollowedByAccount = async (props: ReloadSpaceIds) => {
15 | const { substrate, dispatch, account } = props
16 |
17 | const readyApi = await substrate.api
18 | const res = await readyApi.query.spaceFollows.spacesFollowedByAccount(account)
19 | const followedSpaceIds = bnsToIds(res as unknown as BN[])
20 |
21 | dispatch(upsertFollowedSpaceIdsByAccount({
22 | id: account,
23 | followedSpaceIds
24 | }))
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/space/space-account/SpaceAccount.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .button
4 | width: 100% !important
5 |
6 | .option
7 | margin-left: $space_tiny
8 |
--------------------------------------------------------------------------------
/src/components/space/space-edit-page/SpaceEditPage.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/common
2 |
3 | .form
4 | display: flex
5 | flex-direction: column
6 | gap: 1.5rem
7 | padding: $space_normal
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/space/space-item/Space.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .header
4 | background-color: $white-color
5 |
6 | :global(.MuiCardHeader-action)
7 | margin-top: 0
8 |
9 | .content
10 | font-size: $font_normal
11 |
12 | .tags
13 | padding-top: 0
14 |
15 | .followers
16 | cursor: pointer
17 |
18 | &:hover
19 | text-decoration: underline
20 |
21 | .options
22 | margin-left: $space_tiny
23 |
24 | .content
25 | padding-top: 0
26 |
--------------------------------------------------------------------------------
/src/components/space/space-new-page/SpaceNewPage.tsx:
--------------------------------------------------------------------------------
1 | import Layout from '../../layout/Layout';
2 | import CardEdit from '../../common/card-edit/CardEdit';
3 | import { useRouter } from 'next/router';
4 | import { useTranslation } from 'react-i18next';
5 | import { CardEditType } from "../../../models/common/card-edit";
6 |
7 | const SpaceNewPage = () => {
8 | const router = useRouter();
9 | const { t } = useTranslation();
10 | const onCancel = () => {
11 | router.back();
12 | };
13 |
14 | return (
15 |
16 |
23 |
24 | );
25 | };
26 |
27 | export default SpaceNewPage;
28 |
--------------------------------------------------------------------------------
/src/components/space/space-page/SpacePage.module.sass:
--------------------------------------------------------------------------------
1 | @import src/styles/vars
2 |
3 | .box
4 | box-shadow: 0 3px 10px rgba(153, 153, 153, .14), 0 3px 9px rgba(153, 153, 153, .12), 0 1px 20px rgba(153, 153, 153, .2)
5 |
6 | .panel
7 | padding: $space_normal
8 | display: flex
9 | align-items: center
10 | flex-direction: column
11 | gap: 0.75rem
12 |
--------------------------------------------------------------------------------
/src/components/space/space-page/loadSpaceOnNextReq.ts:
--------------------------------------------------------------------------------
1 | import { NextContextWithRedux } from 'src/store/app';
2 | import { fetchSpace, selectSpace } from 'src/store/features/spaces/spacesSlice';
3 | import { SpaceWithSomeDetails } from '@subsocial/api/types/dto';
4 | import { SpaceId } from '@subsocial/api/types/substrate';
5 |
6 | export async function loadSpaceOnNextReq(
7 | props: NextContextWithRedux,
8 | ): Promise {
9 |
10 | const { context, subsocial, dispatch, reduxStore } = props;
11 | const { query } = context;
12 | const { spaceId } = query;
13 | const idOrHandle = spaceId as string;
14 | if (!idOrHandle) return;
15 |
16 | let id: SpaceId | string | undefined;
17 |
18 | if (idOrHandle[0] === '@') {
19 | const handle = idOrHandle.slice(1).toLowerCase();
20 | const domain = await subsocial.findDomain(handle);
21 |
22 | id = domain?.innerSpace
23 | } else {
24 | id = idOrHandle;
25 | }
26 |
27 | if (!id) {
28 | return;
29 | }
30 |
31 | const idStr = id.toString();
32 |
33 | await dispatch(fetchSpace({ api: subsocial, id: idStr, reload: true, withUnlisted: true }));
34 | const spaceData = selectSpace(reduxStore.getState(), { id: idStr });
35 |
36 | if (!spaceData) {
37 | return;
38 | }
39 |
40 | return spaceData;
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/space/toEdit.ts:
--------------------------------------------------------------------------------
1 | import Router from 'next/router';
2 |
3 | export const toEdit = (id: string) => {
4 | Router.push(`/${id}/edit`);
5 | };
6 |
--------------------------------------------------------------------------------
/src/components/switch-account/SwitchAccount.tsx:
--------------------------------------------------------------------------------
1 | import Drawer from '@mui/material/Drawer';
2 | import ButtonClose from '../common/button/button-close/ButtonClose';
3 | import styles from './SwitchAccount.module.sass';
4 | import Modal from '../modal/Modal';
5 | import { useAppDispatch, useAppSelector } from 'src/store/app/store';
6 | import { toggleAccount } from 'src/store/features/mainSlice';
7 | import SwitchAccountContent from './SwitchAccountContent';
8 | import { useResponsiveSize } from '../responsive/ResponsiveContext';
9 |
10 | export const SwitchAccount = () => {
11 | const { isOpenAccount } = useAppSelector((state) => state.main);
12 | const dispatch = useAppDispatch();
13 | const { isMobile } = useResponsiveSize();
14 |
15 | return !isMobile ? (
16 | dispatch(toggleAccount())}
20 | className={styles.drawer}
21 | >
22 | dispatch(toggleAccount())}
24 | className={styles.close}
25 | />
26 | dispatch(toggleAccount())} />
27 |
28 | ) : (
29 | dispatch(toggleAccount())}
32 | className={styles.drawer}
33 | >
34 | dispatch(toggleAccount())} />
35 |
36 | );
37 | };
38 |
--------------------------------------------------------------------------------
/src/components/utils/Section.tsx:
--------------------------------------------------------------------------------
1 | import React, { CSSProperties } from 'react';
2 |
3 | export interface BareProps {
4 | className?: string;
5 | style?: CSSProperties;
6 | }
7 |
8 | type Props = React.PropsWithChildren<
9 | BareProps & {
10 | id?: string;
11 | className?: string;
12 | title?: React.ReactNode;
13 | level?: number;
14 | }
15 | >;
16 |
17 | export const Section = ({
18 | title,
19 | level = 2,
20 | className,
21 | id,
22 | children,
23 | }: Props) => {
24 | const renderTitle = () => {
25 | if (!title) return null;
26 |
27 | const className = 'DfSection-title';
28 | return React.createElement(`h${level}`, { className }, title);
29 | };
30 |
31 | return (
32 |
33 |
34 | {renderTitle()}
35 | {children}
36 |
37 |
38 | );
39 | };
40 |
41 | export default Section;
42 |
--------------------------------------------------------------------------------
/src/components/utils/address.tsx:
--------------------------------------------------------------------------------
1 | export const toShortAddress = (address: string, lengthOfAddress = 12) => {
2 | const visibleChars = Math.floor(lengthOfAddress / 2);
3 |
4 | if (address.length > lengthOfAddress) {
5 | return (
6 | <>
7 | {address.slice(0, visibleChars)}…{address.slice(-visibleChars)}
8 | >
9 | );
10 | }
11 |
12 | return address;
13 | };
14 |
--------------------------------------------------------------------------------
/src/components/utils/embed.ts:
--------------------------------------------------------------------------------
1 | export const allowEmbedList = ['vimeo', 'youtube', 'youtu.be', 'soundcloud'];
2 |
3 | export const getEmbedUrl = (url: string, embed: string | undefined) => {
4 | if (!embed) return;
5 |
6 | const urls: Record = {
7 | vimeo: `https://player.vimeo.com/video/${url.split('/').pop()}`,
8 | youtube: `https://www.youtube.com/embed/${url.split('=').pop()}`,
9 | 'youtu.be': `https://www.youtube.com/embed/${url.split('/').pop()}`,
10 | soundcloud: `https://w.soundcloud.com/player/
11 | ?url=${url}&auto_play=false&hide_related=true&show_comments=true&show_user=true&show_reposts=false&visual=true`,
12 | };
13 |
14 | return urls[embed];
15 | };
16 |
--------------------------------------------------------------------------------
/src/components/utils/getInitialPostValue.tsx:
--------------------------------------------------------------------------------
1 | import {PostContent} from "@subsocial/api/types";
2 |
3 | export const getInitialPostValue = (
4 | post: PostContent | undefined
5 | ): {
6 | title?: string;
7 | body?: string;
8 | image?: string;
9 | link?: string;
10 | tags?: string[];
11 | } => {
12 | if (post) {
13 | return {...post};
14 | }
15 | return {};
16 | };
17 |
--------------------------------------------------------------------------------
/src/components/utils/getTxParams.ts:
--------------------------------------------------------------------------------
1 | import { newLogger } from '@subsocial/utils';
2 | import { IpfsCommonContent } from '@subsocial/api/types';
3 | import { IpfsCid } from '@subsocial/api/types/substrate';
4 | import { SubsocialIpfsApi } from '@subsocial/api';
5 |
6 | const log = newLogger('BuildTxParams');
7 |
8 | type Params = {
9 | ipfs: SubsocialIpfsApi;
10 | json: C;
11 | buildTxParamsCallback: (cid: string) => any[];
12 | };
13 |
14 | export const getTxParams = async ({
15 | ipfs,
16 | json,
17 | buildTxParamsCallback,
18 | }: Params) => {
19 | try {
20 | const cid = await ipfs.saveContent(json);
21 |
22 | if (cid) {
23 | return buildTxParamsCallback(cid.toString());
24 | } else {
25 | log.error('Save to IPFS returned an undefined CID');
26 | }
27 | } catch (err) {
28 | log.error(`Failed to build tx params. ${err}`);
29 | }
30 | return [];
31 | };
32 |
--------------------------------------------------------------------------------
/src/components/utils/index.ts:
--------------------------------------------------------------------------------
1 | import { AnyAccountId } from '@subsocial/api/types';
2 |
3 | export const toShortAddress = (_address: AnyAccountId) => {
4 | const address = (_address || '').toString();
5 |
6 | return address.length > 13
7 | ? `${address.slice(0, 6)}…${address.slice(-6)}`
8 | : address;
9 | };
10 |
--------------------------------------------------------------------------------
/src/components/utils/insertDash.ts:
--------------------------------------------------------------------------------
1 | export const insertDash = (str: string) => {
2 | return str
3 | .trim()
4 | .toLowerCase()
5 | .replace(/[^a-zA-Z0-9 -]/, '')
6 | .replace(/\s/g, '-');
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/utils/labelForMenuItem.tsx:
--------------------------------------------------------------------------------
1 | import { ListItemIcon, ListItemText } from '@mui/material';
2 | import Image from '../common/image/Image';
3 |
4 | const labelForMenuItem = (
5 | label: string,
6 | image: string,
7 | width: number,
8 | height: number
9 | ) => (
10 | <>
11 |
12 |
13 |
14 | {label}
15 | >
16 | );
17 |
18 | export default labelForMenuItem;
19 |
--------------------------------------------------------------------------------
/src/components/utils/next.ts:
--------------------------------------------------------------------------------
1 | import { NextPageContext } from 'next'
2 |
3 | export const return404 = ({ res }: NextPageContext) => {
4 | if (res) {
5 | res.statusCode = 404
6 | }
7 |
8 | return { notFound: true } as unknown as T
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/utils/num.ts:
--------------------------------------------------------------------------------
1 | import BN from 'bn.js'
2 |
3 | /** `def` is a default number that will be returned in case the fuction fails to parse `maybeNum` */
4 | export const tryParseInt = (maybeNum: string | number, def: number): number => {
5 | if (typeof maybeNum === 'number') {
6 | return maybeNum
7 | }
8 | try {
9 | return parseInt(maybeNum)
10 | } catch (err) {
11 | return def
12 | }
13 | }
14 |
15 | // TODO get rid of this function once we move from BN ids to string ids.
16 | export function stringifyBns (ids: BN[]): string[] {
17 | return ids.map(id => id?.toString())
18 | }
--------------------------------------------------------------------------------
/src/components/utils/subsocial.ts:
--------------------------------------------------------------------------------
1 | import { newLogger, nonEmptyArr, nonEmptyStr, notDef } from "@subsocial/utils";
2 |
3 | const log = newLogger('URL helpers')
4 |
5 | type Id = string;
6 |
7 | export type Cid = string;
8 |
9 | export type RoleId = string;
10 |
11 | export type HasId = {
12 | id: Id;
13 | };
14 | export type HasHandle = {
15 | handle: string;
16 | };
17 | export type HasSpaceIdOrHandle = HasId | HasHandle;
18 |
19 | export function slugifyHandle (slug?: string): string | undefined {
20 |
21 | if (slug && !slug.startsWith('@')) {
22 | slug = '@' + slug
23 | }
24 |
25 | return slug
26 | }
27 | export function stringifySubUrls (...subUrls: string[]): string {
28 | if (nonEmptyArr(subUrls)) {
29 | const res: string[] = [ '' ]
30 | subUrls.forEach(url => {
31 | if (nonEmptyStr(url)) {
32 | if (url.startsWith('/')) {
33 | url = url.substring(1) // Drop the first '/'
34 | }
35 | res.push(url)
36 | }
37 | })
38 | return res.join('/')
39 | }
40 | return ''
41 | }
42 |
43 | export function spaceIdForUrl(props: HasSpaceIdOrHandle): string {
44 | const id = (props as HasId).id;
45 | const handle = (props as HasHandle).handle;
46 |
47 | if (notDef(id) && notDef(handle)) {
48 | log.warn(`${spaceIdForUrl.name}: Both id and handle are undefined`);
49 | return '';
50 | }
51 |
52 | return slugifyHandle(handle) || id;
53 | }
54 |
55 | /** /[spaceId] */
56 | export function spaceUrl (space: HasSpaceIdOrHandle, ...subUrls: string[]): string {
57 | const idForUrl = spaceIdForUrl(space)
58 | const ending = stringifySubUrls(...subUrls)
59 | return '/' + idForUrl + ending
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/utils/unpinIpfsCid.ts:
--------------------------------------------------------------------------------
1 | import { SubsocialIpfsApi } from '@subsocial/api';
2 | import { IpfsCid } from '@subsocial/api/types';
3 |
4 | export const unpinIpfsCid = (
5 | ipfs: SubsocialIpfsApi,
6 | newCid?: IpfsCid,
7 | oldCid?: IpfsCid
8 | ) => {
9 | if (newCid && newCid !== oldCid) {
10 | return ipfs
11 | .removeContent(newCid)
12 | .then((data) => {
13 | return data;
14 | })
15 | .catch((err) => new Error(err));
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/src/components/utils/useLinkParams.ts:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import { useRouter } from 'next/router'
3 | import { config } from 'src/config'
4 |
5 | type ParamsHookProps = {
6 | triggers?: any[]
7 | defaultSize: number
8 | }
9 |
10 | export const useLinkParams = ({ triggers = [], defaultSize }: ParamsHookProps) => {
11 | const { pathname, asPath } = useRouter()
12 |
13 | return useCallback((page: number, currentSize?: number) => {
14 | const size = currentSize || defaultSize
15 | const sizeParam = size && size !== config.infinityScrollOffset ? `&size=${size}` : ''
16 | const pageParam = page !== config.infinityScrollFirstPage ? `page=${page}` : ''
17 | const params = `${pageParam}${sizeParam}`
18 | const query = params ? `?${params}` : ''
19 | return {
20 | href: `${pathname}${query}`,
21 | as: `${asPath.split('?')[0]}${query}`
22 | }
23 | }, [ pathname, asPath, ...triggers ])
24 | }
25 |
--------------------------------------------------------------------------------
/src/config/index.ts:
--------------------------------------------------------------------------------
1 | export const config = {
2 | appName: 'rSocial',
3 | ipfsUrl: `https://ipfs.subsocial.network/ipfs/`,
4 | substrateNodeUrl: `wss://para.f3joule.space`,
5 | ipfsNodeUrl: `https://crustwebsites.net`,
6 | infinityScrollOffset: 20,
7 | infinityScrollFirstPage: 1,
8 | addressLengthShort: 13,
9 | addressLengthLong: 17,
10 | loadImageLimitMb: 2 * 1024 * 1024,
11 | isOffChainFeed: false,
12 | enableTips: false,
13 | recommendedSpaceIds: [
14 | '1',
15 | '1001',
16 | '1002',
17 | ],
18 | }
19 |
--------------------------------------------------------------------------------
/src/hooks/useDrawerSize.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { useResponsiveSize } from 'src/components/responsive/ResponsiveContext';
3 |
4 | export interface Size {
5 | height: number | undefined;
6 | }
7 |
8 | export function useDrawerSize(): Size {
9 | const [windowSize, setWindowSize] = useState({
10 | height: undefined,
11 | });
12 | const { isDesktop } = useResponsiveSize();
13 | const headerHeight = isDesktop ? 56 : 54;
14 | useEffect(() => {
15 | function handleResize() {
16 | setWindowSize({
17 | height: window.innerHeight - headerHeight,
18 | });
19 | }
20 |
21 | window.addEventListener('resize', handleResize);
22 |
23 | handleResize();
24 |
25 | return () => window.removeEventListener('resize', handleResize);
26 | }, []);
27 | return windowSize;
28 | }
29 |
--------------------------------------------------------------------------------
/src/hooks/useIsMySpace.ts:
--------------------------------------------------------------------------------
1 | import { SpaceStruct } from '@subsocial/api/types/dto';
2 | import { isDef } from '@subsocial/utils';
3 | import { asAccountId } from '@subsocial/api';
4 | import { useMyAddress } from '../store/features/myAccount/myAccountHooks';
5 | import { AnyAccountId } from '@subsocial/api/types';
6 |
7 | export const useIsMySpace = (space?: SpaceStruct) =>
8 | useIsMyAddress(space?.ownerId) && isDef(space);
9 |
10 | export function useIsMyAddress(anotherAddress?: AnyAccountId) {
11 | return equalAddresses(useMyAddress(), anotherAddress);
12 | }
13 |
14 | type MaybeAccAddr = undefined | AnyAccountId;
15 |
16 | export function equalAddresses(
17 | addr1: MaybeAccAddr,
18 | addr2: MaybeAccAddr
19 | ): boolean {
20 | if (addr1 === addr2) {
21 | return true;
22 | } else if (!addr1 || !addr2) {
23 | return false;
24 | } else {
25 | return asAccountId(addr1)?.eq(asAccountId(addr2)) || false;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/hooks/useIsUnlistedPost.tsx:
--------------------------------------------------------------------------------
1 | import { useIsMyAddress, useIsMySpace } from './useIsMySpace';
2 | import { PostStruct, SpaceStruct } from '@subsocial/api/types/dto';
3 | import { isHidden } from '@subsocial/api/filters';
4 | import { Post } from '@subsocial/api/types/substrate';
5 |
6 |
7 | type IsUnlistedPostProps = {
8 | post?: PostStruct,
9 | space?: SpaceStruct
10 | }
11 |
12 | export const useIsUnlistedPost = ({ post, space }: IsUnlistedPostProps) => {
13 | const notMyPost = !useIsMyAddress(post?.ownerId)
14 | const notMySpace = !useIsMySpace(space)
15 |
16 | return notMyPost && notMySpace && isHidden(post as unknown as Post)
17 | }
18 |
--------------------------------------------------------------------------------
/src/hooks/useLoader.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | const useLoader = () => {
4 | const [isLoader, setIsLoader] = useState(false);
5 |
6 | const toggleLoader = () => {
7 | setIsLoader((current) => !current);
8 | };
9 |
10 | return {
11 | isLoader,
12 | toggleLoader,
13 | };
14 | };
15 |
16 | export default useLoader;
17 |
--------------------------------------------------------------------------------
/src/hooks/useLocalStorage.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | export const useLocalStorage = (
4 | key: string,
5 | initialValue: T
6 | ): [string, typeof setValue] => {
7 | const [storedValue, setStoredValue] = useState(() => {
8 | try {
9 | const item = window.localStorage.getItem(key);
10 | return item ? JSON.parse(item) : initialValue;
11 | } catch (error) {
12 | return initialValue;
13 | }
14 | });
15 |
16 | const setValue = (value: T): void => {
17 | const valueToStore = value instanceof Function ? value(storedValue) : value;
18 | setStoredValue(valueToStore);
19 | window.localStorage.setItem(key, JSON.stringify(valueToStore));
20 | };
21 |
22 | return [storedValue, setValue];
23 | };
24 |
--------------------------------------------------------------------------------
/src/hooks/useModal.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | export const useModal = () => {
4 | const [isVisible, setIsVisible] = useState(false);
5 |
6 | const toggleModal = () => {
7 | setIsVisible(!isVisible);
8 | };
9 |
10 | return {
11 | isVisible,
12 | toggleModal,
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/src/hooks/useSnackbar.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { SnackbarType } from 'src/models/common/snackbar';
3 |
4 | interface SnackConfigProps {
5 | message: string;
6 | type?: SnackbarType;
7 | isLoader?: boolean;
8 | }
9 |
10 | export const useSnackbar = () => {
11 | const [message, setMessage] = useState('');
12 | const [type, setType] = useState(SnackbarType.Info);
13 | const [isLoader, setLoader] = useState(false);
14 |
15 | const setSnackConfig = ({
16 | message,
17 | type = SnackbarType.Info,
18 | isLoader = false,
19 | }: SnackConfigProps) => {
20 | setType(type);
21 | setMessage(message);
22 | setLoader(isLoader);
23 | };
24 |
25 | const removeSnackbar = () => {
26 | setType(SnackbarType.Info);
27 | setMessage('');
28 | setLoader(false);
29 | };
30 |
31 | return {
32 | message,
33 | type,
34 | isLoader,
35 | setSnackConfig,
36 | removeSnackbar,
37 | };
38 | };
39 |
--------------------------------------------------------------------------------
/src/i18n/index.js:
--------------------------------------------------------------------------------
1 | import i18n from 'i18next';
2 | import { initReactI18next } from 'react-i18next';
3 | import LanguageDetector from 'i18next-browser-languagedetector';
4 |
5 | import translation_en from './en.json';
6 | import translation_ru from './ru.json';
7 | import languages from './languages';
8 | import { DateService } from '../utils';
9 |
10 | i18n
11 | .use(LanguageDetector)
12 | .use(initReactI18next)
13 | .init({
14 | resources: {
15 | en: {
16 | translation: translation_en
17 | },
18 | ru: {
19 | translation: translation_ru
20 | }
21 | },
22 | });
23 |
24 | const lng = i18n.language.replace(/"/g, '');
25 |
26 | i18n.changeLanguage(lng, () => DateService.updateLocale(lng));
27 | i18n.on('languageChanged', (lang) => DateService.updateLocale(lang));
28 |
29 | if (!Object.keys(languages).includes(i18n.language)) {
30 | i18n.changeLanguage(Object.keys(languages)[0]);
31 | }
32 |
33 | export default i18n;
34 |
--------------------------------------------------------------------------------
/src/i18n/languages.ts:
--------------------------------------------------------------------------------
1 | interface Languages {
2 | [key: string]: string;
3 | }
4 |
5 | const languages: Languages = {
6 | en: 'English',
7 | ru: 'Русский',
8 | };
9 |
10 | export default languages;
11 |
--------------------------------------------------------------------------------
/src/models/account.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 | import { NamedLink } from '@subsocial/api/types';
3 | import { AccountId } from '@subsocial/api/types/dto';
4 |
5 | export interface AccountProps {
6 | tabs?: ReactNode;
7 | avatar?: string;
8 | name: string | ReactNode;
9 | id: string;
10 | followersCount: number;
11 | followingCount?: number;
12 | about?: string;
13 | summary?: string;
14 | isShowMore?: boolean;
15 | links?: string[] | NamedLink[];
16 | buttons: ReactNode;
17 | tags?: string[];
18 | isEditable?: boolean;
19 | action: ReactNode;
20 | posts?: number;
21 | withBalance?: boolean;
22 | hiddenBlock?: ReactNode;
23 | }
24 |
25 | export type AccountDescriptionProps = Pick<
26 | AccountProps,
27 | 'about' | 'summary' | 'isShowMore'
28 | >;
29 |
30 | export interface Account {
31 | address: string;
32 | name: string;
33 | }
34 |
35 | export interface AccountsProps {
36 | accounts: Account[];
37 | }
38 |
39 | export interface AccountsModalProps extends AccountsProps {
40 | onClose: () => void;
41 | }
42 |
43 | export interface SwitchAccountContentProps {
44 | onClose: () => void;
45 | }
46 |
47 | export interface AccountFollowersProps {
48 | following?: number;
49 | followers?: number;
50 | className?: string;
51 | accountId: AccountId;
52 | }
53 |
--------------------------------------------------------------------------------
/src/models/auth.ts:
--------------------------------------------------------------------------------
1 | export enum ACCOUNT_STATUS {
2 | INIT,
3 | EXTENSION_NOT_FOUND,
4 | ACCOUNTS_NOT_FOUND,
5 | UNAUTHORIZED,
6 | AUTHORIZED,
7 | }
8 |
--------------------------------------------------------------------------------
/src/models/comments.ts:
--------------------------------------------------------------------------------
1 | import { PostId, PostWithSomeDetails, PostStruct, PostData } from '@subsocial/api/types/dto';
2 |
3 | export interface CommentsProps {
4 | parentStruct: PostStruct;
5 | }
6 |
7 | export interface CommentProps {
8 | commentId: PostId;
9 | commentDetails: PostWithSomeDetails;
10 | }
11 |
12 | export interface NewCommentProps {
13 | parentStruct: PostStruct;
14 | placeholder: string;
15 | className?: string;
16 | autofocus?: boolean;
17 | onClickCancel?: () => void;
18 | addNewComment: (id: string) => void;
19 | }
20 |
21 | export interface EditCommentProps {
22 | comment: PostData;
23 | onClickCancel: () => void;
24 | className?: string;
25 | autofocus?: boolean;
26 | }
27 |
28 | export interface CommentActionProps {
29 | onReply: () => void;
30 | comment: PostStruct;
31 | }
32 |
33 | export interface CommentExtension {
34 | parentId: PostId | null;
35 | rootPostId: PostId;
36 | }
37 |
--------------------------------------------------------------------------------
/src/models/common/address-props.ts:
--------------------------------------------------------------------------------
1 | import { TextProps } from './typography';
2 |
3 | export type AddressSize = 'sm' | 'lg';
4 |
5 | export interface AddressProps {
6 | label: string;
7 | lengthOfAddress?: number;
8 | size: AddressSize;
9 | isCopy: boolean;
10 | className?: string;
11 | isQr?: boolean;
12 | isIcon?: boolean;
13 | textProps?: Omit;
14 | isShort?: boolean;
15 | }
16 |
--------------------------------------------------------------------------------
/src/models/common/avatar.ts:
--------------------------------------------------------------------------------
1 | export enum AvatarSizes {
2 | SMALLEST = 22,
3 | SMALLER = 36,
4 | SMALL = 38,
5 | MEDIUM = 40,
6 | LARGE = 46,
7 | HUGE = 60,
8 | HUGEST = 80,
9 | }
10 |
11 | export interface AvatarProps {
12 | src?: any;
13 | size: AvatarSizes;
14 | id: string | undefined;
15 | }
16 |
--------------------------------------------------------------------------------
/src/models/common/balance.ts:
--------------------------------------------------------------------------------
1 | import BN from 'bn.js';
2 |
3 | export type BalanceType = BN | string | number;
4 |
5 | export interface BalanceProps {
6 | address?: string;
7 | isIcon: boolean;
8 | className?: string;
9 | }
10 |
--------------------------------------------------------------------------------
/src/models/common/card-edit.ts:
--------------------------------------------------------------------------------
1 | import { SpaceData } from '@subsocial/api/types/dto';
2 |
3 | export enum CardEditType {
4 | Space = 'Space',
5 | Profile = 'Profile',
6 | Post = 'Post',
7 | Comment = 'Comment',
8 | }
9 |
10 | export interface CardEditProps {
11 | spaceData?: SpaceData;
12 | profileData?: SpaceData;
13 | title: string;
14 | cancelButton: string;
15 | saveButton: string;
16 | onCancel?: () => void;
17 | type: CardEditType;
18 | }
19 |
--------------------------------------------------------------------------------
/src/models/common/editor.ts:
--------------------------------------------------------------------------------
1 | import { SimpleMDEReactProps } from 'react-simplemde-editor';
2 |
3 | export type EditorProps = Omit & {
4 | toolbar?: boolean;
5 | onChange?: (value: string) => any | void;
6 | autofocus?: boolean;
7 | };
8 |
--------------------------------------------------------------------------------
/src/models/common/embed.ts:
--------------------------------------------------------------------------------
1 | export interface EmbedProps {
2 | link: string;
3 | className?: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/models/common/empty.ts:
--------------------------------------------------------------------------------
1 | export interface EmptyProps {
2 | text: string
3 | }
4 |
--------------------------------------------------------------------------------
/src/models/common/file.ts:
--------------------------------------------------------------------------------
1 | import { IpfsCid } from '@subsocial/api/types';
2 | import {Dispatch, SetStateAction} from 'react';
3 |
4 | export interface FileProps {
5 | type: 'avatar' | 'image';
6 | image?: string;
7 | setImage?: (image: string) => void;
8 | setCidImage: Dispatch>;
9 | setMbError: (value: boolean) => void;
10 | className?: string;
11 | }
12 |
--------------------------------------------------------------------------------
/src/models/common/followers-block.ts:
--------------------------------------------------------------------------------
1 | import { AccountId, SpaceId } from '@subsocial/api/types/dto';
2 |
3 | export interface FollowersBlockProps {
4 | posts?: number;
5 | followers?: number;
6 | following?: number;
7 | className?: string;
8 | id: AccountId | SpaceId;
9 | onClose?: () => void;
10 | }
11 |
--------------------------------------------------------------------------------
/src/models/common/icon.ts:
--------------------------------------------------------------------------------
1 | export interface IconVoteProps {
2 | type: 'outline' | 'contained';
3 | }
4 |
--------------------------------------------------------------------------------
/src/models/common/input.ts:
--------------------------------------------------------------------------------
1 | import { ChangeEvent } from "react";
2 |
3 | export interface InputProps {
4 | label?: string;
5 | placeholder?: string;
6 | isError?: boolean;
7 | errorText?: string;
8 | value?: any;
9 | onChange?: (event: any) => void;
10 | isRequired?: boolean;
11 | isMultiline?: boolean;
12 | onKeyDown?: (event: any) => void;
13 | onKeyUp?: (event: any) => void;
14 | InputProps?: any;
15 | defaultValue?: string;
16 | minRows?: number;
17 | }
18 |
19 | export interface TagsInputProps {
20 | tags: string[];
21 | setTags: any;
22 | className?: string;
23 | }
24 |
25 | export interface InputMoneyProps {
26 | amount: string;
27 | onChange: (event: ChangeEvent) => void;
28 | currency: string;
29 | placeholder: string;
30 | onClose: () => void;
31 | className?: string
32 | }
33 |
--------------------------------------------------------------------------------
/src/models/common/link.ts:
--------------------------------------------------------------------------------
1 | import { LinkProps as NextLinkProps } from 'next/dist/client/link';
2 | import { HTMLAttributes } from 'react';
3 |
4 | export interface IconLinkProps {
5 | link: string;
6 | }
7 |
8 | interface Link extends NextLinkProps {
9 | image?: boolean;
10 | }
11 |
12 | export type LinkProps = Link &
13 | HTMLAttributes & { ext?: boolean };
14 |
15 | export type SeeMoreProps = HTMLAttributes & NextLinkProps;
16 |
17 | export type SmallLinkProps = HTMLAttributes & NextLinkProps;
18 |
--------------------------------------------------------------------------------
/src/models/common/profile-followers.ts:
--------------------------------------------------------------------------------
1 | import { AccountId } from '@subsocial/api/types/dto';
2 |
3 | export interface ProfileFollowersProps {
4 | following: number;
5 | followers: number;
6 | className?: string;
7 | accountId: AccountId;
8 | }
9 |
--------------------------------------------------------------------------------
/src/models/common/snackbar.ts:
--------------------------------------------------------------------------------
1 | export enum SnackbarType {
2 | Info = 'info',
3 | Error = 'error',
4 | }
5 |
6 | export interface SnackbarProps {
7 | type: SnackbarType;
8 | open: boolean;
9 | onClose?: () => void;
10 | message: string;
11 | withAutoHide?: boolean;
12 | withLoader?: boolean;
13 | }
14 |
--------------------------------------------------------------------------------
/src/models/common/tabs.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 |
3 | export interface Tab {
4 | label: string;
5 | tabValue: string;
6 | count?: number;
7 | }
8 |
9 | export interface TabProps extends Tab {
10 | component?: ReactNode | ReactNode[] | JSX.Element[];
11 | }
12 |
13 | export interface TabsProps {
14 | value: string;
15 | tabs: TabProps[];
16 | className?: string;
17 | setValue: (T: any) => void;
18 | unselected?: boolean;
19 | }
20 |
--------------------------------------------------------------------------------
/src/models/common/tags.ts:
--------------------------------------------------------------------------------
1 | export interface TagListProps {
2 | tags?: string[];
3 | className?: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/models/common/typography.ts:
--------------------------------------------------------------------------------
1 | import { TypographyProps } from '@mui/material/Typography/Typography';
2 | import { ElementType } from 'react';
3 |
4 | export enum TitleSizes {
5 | DETAILS = 'details',
6 | PREVIEW = 'preview',
7 | PROFILE = 'profile',
8 | }
9 |
10 | export interface TitleProps extends TypographyProps {
11 | type: TitleSizes;
12 | component?: ElementType;
13 | }
14 |
15 | export enum TextSizes {
16 | NORMAL = 'normal',
17 | SECONDARY = 'secondary',
18 | }
19 |
20 | export interface TextProps extends TypographyProps {
21 | type: TextSizes;
22 | component?: ElementType;
23 | }
24 |
--------------------------------------------------------------------------------
/src/models/header.ts:
--------------------------------------------------------------------------------
1 | export interface HeaderProps {
2 | label: string;
3 | isShowingMobileBurger: boolean;
4 | onMobileBurgerClick: () => void;
5 | }
6 |
--------------------------------------------------------------------------------
/src/models/infinity-scroll.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 | import { SubsocialApi } from '@subsocial/api';
3 | import { HasHiddenVisibility } from '../store/app/helpers';
4 | import { AccountId } from '@subsocial/api/types/dto';
5 |
6 | export type RenderItemFn = (item: string) => ReactNode;
7 |
8 | export type InnerLoadMoreFn = (
9 | page: number,
10 | size: number
11 | ) => Promise;
12 |
13 | export type loadMoreValuesArgs = {
14 | size: number;
15 | page: number;
16 | api: SubsocialApi;
17 | dispatch: any;
18 | visibility?: HasHiddenVisibility;
19 | myAddress?: AccountId;
20 | ids: string[];
21 | withUnlisted?: boolean;
22 | withSpace?: boolean;
23 | };
24 | export type DataListItemProps = {
25 | renderItem: RenderItemFn;
26 | };
27 |
--------------------------------------------------------------------------------
/src/models/notifications.ts:
--------------------------------------------------------------------------------
1 | export interface NotificationsItemProps {
2 | image?: string;
3 | ownerName: string | undefined;
4 | ownerImage: any;
5 | ownerId: string | undefined;
6 | subject: string | undefined;
7 | spaceName?: string;
8 | date: string | number;
9 | link?: string;
10 | msg: string;
11 | msgType: string;
12 | aggregationCount: number;
13 | spaceId: string | undefined;
14 | postId?: string;
15 | subjectLink: any;
16 | }
17 |
--------------------------------------------------------------------------------
/src/models/post.ts:
--------------------------------------------------------------------------------
1 | import {
2 | PostData,
3 | PostWithAllDetails,
4 | PostWithSomeDetails,
5 | PostStruct,
6 | SpaceData, SpaceId, AccountId
7 | } from '@subsocial/api/types/dto';
8 | import { IpfsCid } from '@subsocial/api/types';
9 | import { Visibility } from '@subsocial/api/filters';
10 | import { ListType } from '../components/home/HomePage';
11 |
12 | export type PostActionsProps = {
13 | toggleComments: () => void;
14 | post: PostData;
15 | reaction: any;
16 | isSharedPost?: boolean;
17 | };
18 |
19 | export type PostDetailsProps = {
20 | postData: PostWithAllDetails;
21 | };
22 |
23 | export interface EditorPostProps {
24 | postId: string | undefined;
25 | isWithLink: boolean;
26 | }
27 |
28 | interface PostDataWithRootPostId extends PostData {
29 | struct: PostStruct & { rootPostId?: string };
30 | }
31 |
32 | export interface PostFullProps extends PostWithSomeDetails {
33 | post: PostDataWithRootPostId;
34 | }
35 |
36 | export interface PostStructWithHidden extends PostStruct {
37 | hidden: boolean;
38 | }
39 |
40 | export enum TypePostTabs {
41 | Article = 'article',
42 | Video = 'video',
43 | }
44 |
45 | export interface dataPost {
46 | title: string;
47 | body: string;
48 | tags: string[];
49 | image?: string | IpfsCid;
50 | link?: string;
51 | }
52 |
53 | export interface PostInfoProps {
54 | profile: SpaceData;
55 | post: PostData;
56 | space: SpaceData;
57 | }
58 |
59 | export interface PostListProps {
60 | ids: SpaceId[];
61 | visibility?: Visibility;
62 | myAddress?: AccountId;
63 | withSpace?: boolean;
64 | type?: ListType;
65 | }
66 |
--------------------------------------------------------------------------------
/src/models/profile.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AccountId,
3 | SpaceData,
4 | SpaceId,
5 | } from '@subsocial/api/types/dto';
6 | import { AnyAccountId } from '@subsocial/api/types';
7 |
8 | export interface SpaceIds {
9 | ids: SpaceId[];
10 | withUnlisted?: boolean;
11 | }
12 |
13 | export interface ProfilePageProps {
14 | address: AnyAccountId;
15 | owner?: SpaceData;
16 | followers?: AccountId[];
17 | mySpaceIds?: SpaceId[];
18 | spacesData?: SpaceData[];
19 | size?: number;
20 | }
21 |
22 | export type ProfileTabValues = 'userPosts' | 'userSpaces';
23 |
24 | export interface ProfileContentProps {
25 | ids: SpaceId[];
26 | activeTab: ProfileTabValues;
27 | address?: AnyAccountId;
28 | }
29 |
30 | export interface ProfileAccountProps extends Partial {
31 | activeTab: ProfileTabValues;
32 | changeTab: (T: any) => void;
33 | }
34 |
--------------------------------------------------------------------------------
/src/models/sidebar.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 |
3 | export interface SidebarProps {
4 | isShowingMobileBurger: boolean;
5 | onSidebarClose: () => void;
6 | }
7 |
8 | export type ItemType = {
9 | name: string;
10 | icon: ReactNode;
11 | href: string;
12 | }
13 |
--------------------------------------------------------------------------------
/src/models/space.ts:
--------------------------------------------------------------------------------
1 | import {
2 | PostId,
3 | PostWithSomeDetails,
4 | SpaceWithSomeDetails,
5 | } from '@subsocial/api/types/dto';
6 | import { Visibility } from '@subsocial/api/filters';
7 |
8 | export interface SpacePageProps {
9 | spaceData: SpaceWithSomeDetails;
10 | postIds: PostId[];
11 | posts: PostWithSomeDetails[];
12 | notFound?: boolean;
13 | }
14 |
15 | export interface ViewSpaceProps {
16 | spaceData: SpaceWithSomeDetails;
17 | postIds: PostId[];
18 | posts: PostWithSomeDetails[];
19 | }
20 |
21 | export interface ViewSpaceProps {
22 | spaceData: SpaceWithSomeDetails
23 | postIds: PostId[],
24 | posts: PostWithSomeDetails[],
25 | visibility?: Visibility
26 | }
27 |
28 | export interface ViewSpaceProps {
29 | spaceData: SpaceWithSomeDetails
30 | postIds: PostId[],
31 | posts: PostWithSomeDetails[],
32 | visibility?: Visibility
33 | }
34 |
35 | export interface ViewSpaceProps {
36 | spaceData: SpaceWithSomeDetails
37 | postIds: PostId[],
38 | posts: PostWithSomeDetails[],
39 | visibility?: Visibility
40 | }
41 |
--------------------------------------------------------------------------------
/src/pages/[spaceId]/[post]/edit.ts:
--------------------------------------------------------------------------------
1 | import { UpsertPost } from 'src/components/post/upsert-page/UpsertPost';
2 |
3 | export default UpsertPost;
4 |
--------------------------------------------------------------------------------
/src/pages/[spaceId]/[post]/index.ts:
--------------------------------------------------------------------------------
1 | import PostPage from 'src/components/post/post-page/PostPage';
2 |
3 | export default PostPage;
4 |
--------------------------------------------------------------------------------
/src/pages/[spaceId]/edit.ts:
--------------------------------------------------------------------------------
1 | import SpaceEditPage from '../../components/space/space-edit-page/SpaceEditPage';
2 |
3 | export default SpaceEditPage;
4 |
--------------------------------------------------------------------------------
/src/pages/[spaceId]/index.ts:
--------------------------------------------------------------------------------
1 | import SpacePage from '../../components/space/space-page/SpacePage';
2 |
3 | export default SpacePage;
4 |
--------------------------------------------------------------------------------
/src/pages/accounts/[address]/index.ts:
--------------------------------------------------------------------------------
1 | import ProfilePage from '../../../components/profile/profile-page/ProfilePage';
2 |
3 | export default ProfilePage;
4 |
--------------------------------------------------------------------------------
/src/pages/index.ts:
--------------------------------------------------------------------------------
1 | import HomePage from '../components/home/HomePage';
2 |
3 | export default HomePage;
4 |
5 | export async function getServerSideProps({ query }: any) {
6 | if (query.tab) {
7 | return {
8 | props: {
9 | router: {
10 | query
11 | },
12 | },
13 | };
14 | }
15 |
16 | return { props: {}}
17 | }
18 |
--------------------------------------------------------------------------------
/src/pages/new.ts:
--------------------------------------------------------------------------------
1 | import SpaceNewPage from '../components/space/space-new-page/SpaceNewPage';
2 |
3 | export default SpaceNewPage;
4 |
--------------------------------------------------------------------------------
/src/pages/posts/new.ts:
--------------------------------------------------------------------------------
1 | import { UpsertPost } from 'src/components/post/upsert-page/UpsertPost';
2 |
3 | export default UpsertPost;
4 |
--------------------------------------------------------------------------------
/src/store/app/hooks.ts:
--------------------------------------------------------------------------------
1 | export * from '../features/profiles/profilesHooks';
2 | export * from '../features/spaces/spacesHooks';
3 | export * from '../features/posts/postsHooks';
4 |
--------------------------------------------------------------------------------
/src/store/app/hooksCommon.ts:
--------------------------------------------------------------------------------
1 | import { AsyncThunkAction } from '@reduxjs/toolkit';
2 | import {
3 | FetchManyArgs,
4 | FetchOneArgs,
5 | SelectManyArgs,
6 | SelectOneArgs,
7 | ThunkApiConfig,
8 | } from 'src/store/app/helpers';
9 | import { RootState } from 'src/store/app/rootReducer';
10 |
11 | type CommonResult = {
12 | loading?: boolean;
13 | error?: Error;
14 | };
15 |
16 | export type FetchCommonResult = T & CommonResult;
17 |
18 | type FetchOneResult = FetchCommonResult<{
19 | entity?: Entity;
20 | }>;
21 |
22 | export type FetchManyResult = FetchCommonResult<{
23 | entities: Entity[];
24 | }>;
25 |
26 | type SelectFn = (state: RootState, args: Args) => Entity;
27 |
28 | export type SelectManyFn = SelectFn<
29 | SelectManyArgs,
30 | Entity[]
31 | >;
32 | export type SelectOneFn = (
33 | state: RootState,
34 | args: SelectOneArgs
35 | ) => Entity;
36 |
37 | type FetchFn = (
38 | args: Args
39 | ) => AsyncThunkAction;
40 |
41 | export type FetchManyFn = FetchFn, Struct[]>;
42 | export type FetchOneFn = FetchFn, Struct>;
43 |
--------------------------------------------------------------------------------
/src/store/app/index.ts:
--------------------------------------------------------------------------------
1 | export * from './nextHelpers';
2 |
--------------------------------------------------------------------------------
/src/store/app/nextHelpers.tsx:
--------------------------------------------------------------------------------
1 | import { AppDispatch, AppStore, initializeStore } from 'src/store/app/store';
2 | import { NextComponentType, NextPageContext } from 'next';
3 | import { initSubsocialApi } from 'src/components/api';
4 | import { SubsocialApi } from '@subsocial/api';
5 |
6 | export type NextContextWithRedux = {
7 | context: NextPageContext;
8 | subsocial: SubsocialApi;
9 | dispatch: AppDispatch;
10 | reduxStore: AppStore;
11 | };
12 |
13 | type CbFn = (props: NextContextWithRedux) => Promise;
14 |
15 | export const getInitialPropsWithRedux = async (
16 | component: NextComponentType,
17 | cb?: CbFn
18 | ) =>
19 | (component.getInitialProps = async (context: NextPageContext) => {
20 | const reduxStore = initializeStore();
21 | let resultProps = {} as ResultProps;
22 |
23 | if (typeof cb === 'function') {
24 | const { dispatch } = reduxStore;
25 | const subsocial = await initSubsocialApi();
26 |
27 | resultProps = await cb({ context, subsocial, dispatch, reduxStore });
28 | }
29 |
30 | return {
31 | initialReduxState: reduxStore.getState(),
32 | ...resultProps,
33 | };
34 | });
35 |
--------------------------------------------------------------------------------
/src/store/app/rootReducer.ts:
--------------------------------------------------------------------------------
1 | import { combineReducers } from '@reduxjs/toolkit';
2 | import contents from '../features/contents/contentsSlice';
3 | import profiles from '../features/profiles/profilesSlice';
4 | import spaces from '../features/spaces/spacesSlice';
5 | import posts from '../features/posts/postsSlice';
6 | import replyIds from '../features/replies/repliesSlice';
7 | import myAccount from '../features/myAccount/myAccountSlice';
8 | import reactions from '../features/reactions/myPostReactionsSlice';
9 | import followedSpaceIds from '../features/spaceIds/followedSpaceIdsSlice';
10 | import followedAccountIds from '../features/profiles/followedAccountIdsSlice';
11 | import main from '../features/mainSlice';
12 |
13 | const rootReducer = combineReducers({
14 | contents,
15 | profiles,
16 | spaces,
17 | posts,
18 | replyIds,
19 | followedSpaceIds,
20 | followedAccountIds,
21 | myAccount,
22 | reactions,
23 | main,
24 | });
25 |
26 | export type RootState = ReturnType;
27 |
28 | export default rootReducer;
29 |
--------------------------------------------------------------------------------
/src/store/features/mainSlice.ts:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | export interface MainState {
4 | value: string;
5 | isOpenAccount: boolean;
6 | }
7 |
8 | const initialState: MainState = {
9 | value: 'posts',
10 | isOpenAccount: false,
11 | };
12 |
13 | export const mainSlice = createSlice({
14 | name: 'mainOld',
15 | initialState,
16 | reducers: {
17 | changeTab(state, action) {
18 | state.value = action.payload;
19 | },
20 | toggleAccount(state) {
21 | state.isOpenAccount = !state.isOpenAccount;
22 | },
23 | },
24 | });
25 |
26 | export const { changeTab, toggleAccount } = mainSlice.actions;
27 |
28 | export default mainSlice.reducer;
29 |
--------------------------------------------------------------------------------
/src/store/features/myAccount/myAccountHooks.ts:
--------------------------------------------------------------------------------
1 | import { useAppSelector } from '../../app/store';
2 |
3 | export function useMyAddress() {
4 | return useAppSelector((state) => state.myAccount.address);
5 | }
6 |
--------------------------------------------------------------------------------
/src/store/features/myAccount/myAccountSlice.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2 | import { AccountId } from '@subsocial/api/types/dto';
3 | import { Account } from 'src/models/account';
4 | import store from 'store';
5 | import { ACCOUNT_STATUS } from 'src/models/auth';
6 |
7 | type MyAddressState = {
8 | address?: string;
9 | accounts?: Account[];
10 | status: ACCOUNT_STATUS;
11 | signer?: any;
12 | };
13 |
14 | type MyAccountState = MyAddressState;
15 |
16 | const initialState: MyAccountState = { status: ACCOUNT_STATUS.INIT };
17 |
18 | export const MY_ADDRESS = 'myAddress';
19 |
20 | const myAccountSlice = createSlice({
21 | name: 'myAccount',
22 | initialState,
23 | reducers: {
24 | setMyAddress(state, action: PayloadAction) {
25 | const address = action.payload;
26 |
27 | if (address) {
28 | store.set(MY_ADDRESS, address);
29 | state.address = address;
30 | }
31 | },
32 | setAccounts(state, action: PayloadAction) {
33 | const accounts = action.payload;
34 |
35 | if (accounts) {
36 | state.accounts = accounts;
37 | }
38 | },
39 | signOut(state) {
40 | store.remove(MY_ADDRESS);
41 | delete state.address;
42 | },
43 | setSigner(state, action: PayloadAction) {
44 | state.signer = action.payload;
45 | },
46 | },
47 | });
48 |
49 | export const { setMyAddress, setAccounts, signOut, setSigner } =
50 | myAccountSlice.actions;
51 |
52 | export default myAccountSlice.reducer;
53 |
--------------------------------------------------------------------------------
/src/store/features/profiles/profilesHooks.ts:
--------------------------------------------------------------------------------
1 | import { useAppSelector } from 'src/store/app/store';
2 | import { selectProfileContentById } from '../contents/contentsSlice';
3 | import {
4 | fetchProfiles,
5 | SelectProfileArgs,
6 | selectProfileStructById,
7 | } from './profilesSlice';
8 | import { AccountId, SpaceData } from '@subsocial/api/types/dto';
9 | import { useActions } from '../../app/helpers';
10 | import { fetchEntityOfAccountIdsByFollower } from './followedAccountIdsSlice';
11 |
12 | export const useSelectProfile = (
13 | accountId?: AccountId
14 | ): SpaceData | undefined => {
15 | const struct = useAppSelector((state) => {
16 | return accountId ? selectProfileStructById(state, accountId) : undefined;
17 | });
18 |
19 | const cid = struct?.contentId;
20 | const content = useAppSelector((state) =>
21 | cid ? selectProfileContentById(state, cid) : undefined
22 | );
23 |
24 | if (!struct || !content) return undefined;
25 |
26 | return {
27 | id: struct.id,
28 | struct,
29 | content,
30 | };
31 | };
32 |
33 | export const useCreateReloadProfile = () => {
34 | return useActions(({ dispatch, api, args: { id } }) =>
35 | dispatch(fetchProfiles({ api, ids: [id], reload: true }))
36 | );
37 | };
38 |
39 | export const useCreateReloadAccountIdsByFollower = () => {
40 | return useActions(({ dispatch, args: id, ...props }) => {
41 | dispatch(fetchEntityOfAccountIdsByFollower({ id, reload: true, ...props }));
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/src/store/features/reactions/myPostReactionsHooks.ts:
--------------------------------------------------------------------------------
1 | import {
2 | fetchMyReactionsByPostIds,
3 | prependPostIdWithMyAddress,
4 | ReactionStruct,
5 | upsertMyReaction,
6 | } from './myPostReactionsSlice';
7 | import { AccountId, PostId } from '@subsocial/api/types/dto';
8 | import { useApi } from '../../../components/api';
9 | import { useAppDispatch } from '../../app/store';
10 | import { useMyAddress } from '../myAccount/myAccountHooks';
11 | import { useActions } from '../../app/helpers';
12 |
13 | export const useFetchMyReactionsByPostId = (
14 | postId: PostId,
15 | myAddress: AccountId
16 | ) => {
17 | return useFetchMyReactionsByPostIds([postId], myAddress);
18 | };
19 |
20 | export const useFetchMyReactionsByPostIds = (
21 | postIds: PostId[],
22 | myAddress: AccountId
23 | ) => {
24 | const dispatch = useAppDispatch();
25 | const { api } = useApi();
26 |
27 | return dispatch(fetchMyReactionsByPostIds({ ids: postIds, myAddress, api }));
28 | };
29 |
30 | export const useCreateUpsertMyReaction = () => {
31 | const myAddress = useMyAddress();
32 | return useActions(
33 | ({ dispatch, args: { id: postId, reactionId, kind } }) => {
34 | myAddress &&
35 | dispatch(
36 | upsertMyReaction({
37 | id: prependPostIdWithMyAddress(postId, myAddress),
38 | reactionId,
39 | kind,
40 | })
41 | );
42 | }
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/src/store/features/spaces/spacesHooks.ts:
--------------------------------------------------------------------------------
1 | import {
2 | fetchSpaces,
3 | SelectSpaceArgs,
4 | selectSpaces,
5 | selectSpaceStructById,
6 | } from './spacesSlice';
7 | import { useAppSelector } from '../../app/store';
8 | import { selectSpaceContentById } from '../contents/contentsSlice';
9 | import {
10 | SpaceId,
11 | SpaceWithSomeDetails,
12 | } from '@subsocial/api/types/dto';
13 | import { useActions } from '../../app/helpers';
14 |
15 | export const useSelectSpace = (
16 | spaceId?: SpaceId
17 | ): SpaceWithSomeDetails | undefined => {
18 | const struct = useAppSelector((state) =>
19 | spaceId ? selectSpaceStructById(state, spaceId) : undefined
20 | );
21 |
22 | const cid = struct?.contentId;
23 | const content = useAppSelector((state) =>
24 | cid ? selectSpaceContentById(state, cid) : undefined
25 | );
26 |
27 | if (!struct) return undefined;
28 |
29 | return {
30 | id: struct.id,
31 | struct,
32 | content,
33 | };
34 | };
35 |
36 | export const useSelectSpaces = (
37 | spaceIds?: SpaceId[]
38 | ): SpaceWithSomeDetails[] => {
39 | const spaces = useAppSelector((state) =>
40 | spaceIds?.length ? selectSpaces(state, { ids: spaceIds }) : []
41 | );
42 |
43 | return spaces;
44 | };
45 |
46 | export const useCreateReloadSpace = () => {
47 | return useActions(({ dispatch, api, args: { id } }) => {
48 | const args = { api, ids: [id], reload: true };
49 | dispatch(fetchSpaces(args));
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/src/stylesheets/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from '@mui/material/styles';
2 |
3 | export const theme = createTheme({
4 | palette: {
5 | primary: {
6 | main: '#eb2f96',
7 | },
8 | secondary: {
9 | main: '#888',
10 | },
11 | info: {
12 | main: '#eb2f96',
13 | },
14 | },
15 | breakpoints: {
16 | values: {
17 | xs: 0,
18 | sm: 600,
19 | md: 810,
20 | lg: 1200,
21 | xl: 1536,
22 | },
23 | },
24 | components: {
25 | MuiAlert: {
26 | styleOverrides: {
27 | message: {
28 | color: '#000',
29 | },
30 | standardWarning: {
31 | backgroundColor: '#FEFBE8',
32 | },
33 | icon: {
34 | color: '#EFB041',
35 | },
36 | },
37 | },
38 | },
39 | });
40 |
--------------------------------------------------------------------------------
/stories/Header.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import { ComponentProps } from 'react';
3 | import HeaderComponent from 'src/components/header/Header';
4 |
5 | export default {
6 | component: HeaderComponent,
7 | title: 'Header/Header',
8 | } as Meta;
9 |
10 | const Template: Story> = (args) => {
11 | return ;
12 | };
13 |
14 | export const Header = Template.bind({});
15 | Header.args = {
16 | label: 'rSocial',
17 | };
18 |
--------------------------------------------------------------------------------
/stories/buttons/ButtonDownvote.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import ButtonComponent from 'src/components/common/button/buttons-vote/ButtonVotes';
3 | import { ComponentProps } from 'react';
4 | import { post, sharedPost } from 'stories/mocked-data/posts';
5 | import { ReactionEnum } from '@subsocial/api/types/dto';
6 |
7 | export default {
8 | component: ButtonComponent,
9 | title: 'Buttons/Vote buttons/Downvote Button',
10 | argTypes: {
11 | post: {
12 | control: {
13 | disable: true
14 | }
15 | },
16 | reactionEnum: {
17 | control: {
18 | disable: true
19 | }
20 | },
21 | },
22 | args: {
23 | reactionEnum: ReactionEnum.Downvote,
24 | }
25 | } as Meta;
26 |
27 | const Template: Story> = (args) => {
28 | return ;
29 | };
30 |
31 | export const Active = Template.bind({});
32 | Active.args = {
33 | post: post.struct,
34 | };
35 |
36 | export const Default = Template.bind({});
37 | Default.args = {
38 | post: sharedPost.struct,
39 | };
40 |
41 | export const Label = Template.bind({});
42 | Label.args = {
43 | post: sharedPost.struct,
44 | withLabel: true
45 | };
46 |
--------------------------------------------------------------------------------
/stories/buttons/ButtonUpvote.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import ButtonComponent from 'src/components/common/button/buttons-vote/ButtonVotes';
3 | import { ComponentProps } from 'react';
4 | import { sharedPost } from 'stories/mocked-data/posts';
5 | import { ReactionEnum } from '@subsocial/api/types/dto';
6 | import { comment } from '../mocked-data';
7 |
8 | export default {
9 | component: ButtonComponent,
10 | title: 'Buttons/Vote buttons/Upvote Button',
11 | argTypes: {
12 | post: {
13 | control: {
14 | disable: true
15 | }
16 | },
17 | reactionEnum: {
18 | control: {
19 | disable: true
20 | }
21 | },
22 | },
23 | args: {
24 | reactionEnum: ReactionEnum.Upvote,
25 | }
26 | } as Meta;
27 |
28 | const Template: Story> = (args) => {
29 | return ;
30 | };
31 |
32 | export const Active = Template.bind({});
33 | Active.args = {
34 | post: comment.struct,
35 | };
36 |
37 | export const Default = Template.bind({});
38 | Default.args = {
39 | post: sharedPost.struct,
40 | };
41 |
42 | export const Label = Template.bind({});
43 | Label.args = {
44 | post: sharedPost.struct,
45 | withLabel: true
46 | };
47 |
--------------------------------------------------------------------------------
/stories/buttons/CloseButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import React from 'react';
3 | import ButtonCloseComponent from 'src/components/common/button/button-close/ButtonClose';
4 |
5 | export default {
6 | component: ButtonCloseComponent,
7 | title: 'Buttons/Close Button',
8 | decorators: [
9 | (Story) => (
10 |
11 |
12 |
13 | ),
14 | ],
15 | } as Meta;
16 |
17 | export const CloseButton = () => ;
18 |
--------------------------------------------------------------------------------
/stories/buttons/EditButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import ButtonEdit from 'src/components/common/button/button-edit/ButtonEdit';
3 |
4 | export default {
5 | component: ButtonEdit,
6 | title: 'Buttons/Edit Button',
7 | } as Meta;
8 |
9 | export const EditButton = () => ;
10 |
--------------------------------------------------------------------------------
/stories/buttons/NotificationButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import ButtonNotification from 'src/components/common/button/button-notification/ButtonNotification';
3 | import { ComponentProps } from 'react';
4 |
5 | export default {
6 | component: ButtonNotification,
7 | title: 'Buttons/Notification Button',
8 | args: {
9 | value: 10
10 | }
11 | } as Meta;
12 |
13 | const Template: Story> = (args) => {
14 | return ;
15 | };
16 |
17 | export const NotificationButton = Template.bind({});
18 |
--------------------------------------------------------------------------------
/stories/buttons/Option.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import ButtonOptionsComponent from 'src/components/common/button/button-options/ButtonOptions';
3 |
4 | export default {
5 | component: ButtonOptionsComponent,
6 | title: 'Buttons/Option Button',
7 | } as Meta;
8 |
9 | export const OptionButton = () => ;
10 |
--------------------------------------------------------------------------------
/stories/buttons/QrButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import React from 'react';
3 | import ButtonQr from 'src/components/common/button/button-qr/ButtonQr';
4 |
5 | export default {
6 | component: ButtonQr,
7 | title: 'Buttons/Qr Button',
8 | } as Meta;
9 |
10 | export const QrButton = () => ;
11 |
--------------------------------------------------------------------------------
/stories/buttons/ReplyButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import ButtonReply from 'src/components/common/button/ButtonReply';
3 | import { ComponentProps } from 'react';
4 |
5 | export default {
6 | component: ButtonReply,
7 | title: 'Buttons/Reply Button',
8 | args: {
9 | withLabel: true
10 | }
11 | } as Meta;
12 |
13 | const Template: Story> = (args) => {
14 | return ;
15 | };
16 |
17 | export const ReplyButton = Template.bind({});
18 |
--------------------------------------------------------------------------------
/stories/buttons/ShareButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import ButtonShareComponent from 'src/components/common/button/button-share/ButtonShare';
3 | import { ComponentProps } from 'react';
4 |
5 | export default {
6 | component: ButtonShareComponent,
7 | title: 'Buttons/Shared Button',
8 | argTypes: {
9 | onClick: {
10 | control: {
11 | disable: true
12 | }
13 | },
14 | },
15 | args: {
16 | isShowLabel: false,
17 | value: 0
18 | }
19 | } as Meta;
20 |
21 | const Template: Story> = (args) => {
22 | return ;
23 | };
24 |
25 | export const SharedButton = Template.bind({});
26 |
--------------------------------------------------------------------------------
/stories/comments/CommentDefault.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import Comment from 'src/components/common/comments/Comment';
3 | import { Box } from '@mui/system';
4 | import React from 'react';
5 | import { comment } from '../mocked-data';
6 |
7 | export default {
8 | component: Comment,
9 | title: 'Comments/Comment Item',
10 | decorators: [
11 | (Story) => (
12 |
13 |
14 |
15 | ),
16 | ],
17 | } as Meta;
18 |
19 | export const Default = () => ;
20 |
--------------------------------------------------------------------------------
/stories/comments/CommentEdit.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import { Box } from '@mui/system';
3 | import React, { ComponentProps } from 'react';
4 | import { comment } from '../mocked-data';
5 | import EditComment from 'src/components/common/comments/EditComment';
6 | import { PostData } from '@subsocial/api/types/dto';
7 |
8 | export default {
9 | component: EditComment,
10 | title: 'Comments/Comment Item',
11 | decorators: [
12 | (Story) => (
13 |
14 |
15 |
16 | ),
17 | ],
18 | argTypes: {
19 | className: {
20 | control: false
21 | },
22 | onClickCancel: {
23 | control: false,
24 | }
25 | },
26 | args: {
27 | comment: comment as unknown as PostData,
28 | autofocus: true,
29 | }
30 | } as Meta;
31 |
32 | const TemplateCommentEdit: Story> = (args) => (
33 |
34 | );
35 |
36 | export const Edit = TemplateCommentEdit.bind({});
37 |
--------------------------------------------------------------------------------
/stories/comments/CommentInput.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import NewComment from 'src/components/common/comments/NewComment';
3 | import { Box } from '@mui/system';
4 | import React, { ComponentProps } from 'react';
5 | import { post } from '../mocked-data';
6 |
7 | export default {
8 | component: NewComment,
9 | title: 'Comments/Comment Input',
10 | decorators: [
11 | (Story) => (
12 |
13 |
14 |
15 | ),
16 | ],
17 | argTypes: {
18 | addNewComment: {
19 | control: false
20 | },
21 | className: {
22 | control: false
23 | },
24 | onClickCancel: {
25 | control: false
26 | }
27 | },
28 | args: {
29 | placeholder: 'Add a comment',
30 | autofocus: true,
31 | parentStruct: post.struct
32 | }
33 | } as Meta;
34 |
35 | const TemplateNewComment: Story> = (args) => (
36 |
37 | );
38 |
39 | export const CommentInput = TemplateNewComment.bind({});
40 |
--------------------------------------------------------------------------------
/stories/comments/CommentList.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import React from 'react';
3 | import { post } from '../mocked-data';
4 | import Comments from 'src/components/common/comments/Comments';
5 |
6 | export default {
7 | component: Comments,
8 | title: 'Comments/Comment List',
9 | } as Meta;
10 |
11 | export const CommentList = () => ;
12 |
--------------------------------------------------------------------------------
/stories/content/HiddenContent.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import React, { ComponentProps } from 'react';
3 | import HiddenComponent from 'src/components/common/hidden-component/HiddenComponent';
4 | import { comment, post, space } from '../mocked-data';
5 | import { TypeContent } from 'src/models/common/button';
6 | import { PostData, SpaceData } from '@subsocial/api/types/dto';
7 |
8 | export default {
9 | component: HiddenComponent,
10 | title: 'Content/Hidden Content',
11 | } as Meta;
12 |
13 | const Template: Story> = (args) => {
14 | return ;
15 | };
16 |
17 | export const HiddenPost = Template.bind({});
18 | HiddenPost.args = {
19 | data: post as unknown as PostData,
20 | typeContent: TypeContent.Post
21 | };
22 |
23 | export const HiddenSpace = Template.bind({});
24 | HiddenSpace.args = {
25 | data: space as unknown as SpaceData,
26 | typeContent: TypeContent.Space
27 | };
28 |
29 | export const HiddenComment = Template.bind({});
30 | HiddenComment.args = {
31 | data: comment as unknown as SpaceData,
32 | typeContent: TypeContent.Comment
33 | };
34 |
--------------------------------------------------------------------------------
/stories/content/NoContent.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import React, { ComponentProps } from 'react';
3 | import EmptyComponent from 'src/components/common/empty/EmptyComponent';
4 |
5 | export default {
6 | component: EmptyComponent,
7 | title: 'Content/No Content',
8 | args: {
9 | text: 'Posts not found'
10 | }
11 | } as Meta;
12 |
13 | const Template: Story> = (args) => {
14 | return ;
15 | };
16 |
17 | export const NoContent = Template.bind({});
18 |
--------------------------------------------------------------------------------
/stories/content/PostWithHiddenSpace.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import React, { ComponentProps } from 'react';
3 | import { postWithHiddenPost } from '../mocked-data';
4 | import { PostData } from '@subsocial/api/types/dto';
5 | import SpaceHiddenComponent from 'src/components/common/space-hidden-component/SpaceHiddenComponent';
6 |
7 | export default {
8 | component: SpaceHiddenComponent,
9 | title: 'Content/Hidden Content',
10 | args: {
11 | content: postWithHiddenPost as unknown as PostData
12 | }
13 | } as Meta;
14 |
15 | const Template: Story> = (args) => {
16 | return ;
17 | };
18 |
19 | export const PostWithHiddenSpace = Template.bind({});
20 |
--------------------------------------------------------------------------------
/stories/inputs/ImageLoader.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import { ComponentProps } from 'react';
3 | import File from 'src/components/common/file/File';
4 | import { Container } from '@mui/material';
5 | import { Box } from '@mui/system';
6 |
7 | export default {
8 | component: File,
9 | title: 'Inputs/Image Loader',
10 | decorators: [
11 | (Story) => (
12 |
13 |
14 |
15 |
16 |
17 | ),
18 | ],
19 | argTypes: {
20 | className: {
21 | control: false,
22 | },
23 | setMbError: {
24 | control: false,
25 | },
26 | setCidImage: {
27 | control: false,
28 | },
29 | setImage: {
30 | control: false,
31 | },
32 | },
33 | } as Meta;
34 |
35 | const Template: Story> = (args) => {
36 | return ;
37 | };
38 |
39 | export const Avatar = Template.bind({});
40 | Avatar.args = {
41 | type: 'avatar',
42 | };
43 |
44 | export const AvatarWithImage = Template.bind({});
45 | AvatarWithImage.args = {
46 | type: 'avatar',
47 | image: 'https://app.subsocial.network/ipfs/ipfs/QmZfJmzeEQfp1WW7AUGkNSW6AL8ibdvoEyhRZubc3sVL6L',
48 | };
49 |
50 | export const Image = Template.bind({});
51 | Image.args = {
52 | type: 'image',
53 | };
54 |
55 | export const ImageWithImage = Template.bind({});
56 | ImageWithImage.args = {
57 | type: 'image',
58 | image: 'https://app.subsocial.network/ipfs/ipfs/QmZfJmzeEQfp1WW7AUGkNSW6AL8ibdvoEyhRZubc3sVL6L',
59 | };
60 |
--------------------------------------------------------------------------------
/stories/inputs/Input.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import { ComponentProps } from 'react';
3 | import InputComponent from 'src/components/common/inputs/input/Input';
4 | import { Container } from '@mui/material';
5 | import { Box } from '@mui/system';
6 |
7 | export default {
8 | component: InputComponent,
9 | title: 'Inputs/Text Input',
10 | decorators: [
11 | (Story) => (
12 |
13 |
14 |
15 |
16 |
17 | )
18 | ],
19 | argTypes: {
20 | isError: {
21 | type: 'boolean',
22 | },
23 | value: {
24 | control: false,
25 | },
26 | onChange: {
27 | control: false,
28 | },
29 | isMultiline: {
30 | control: false,
31 | },
32 | onKeyDown: {
33 | control: false,
34 | },
35 | onKeyUp: {
36 | control: false,
37 | },
38 | InputProps: {
39 | control: false,
40 | },
41 | defaultValue: {
42 | control: false,
43 | },
44 | minRows: {
45 | control: false,
46 | },
47 | },
48 | args: {
49 | label: 'Text input',
50 | isRequired: true,
51 | isError: false,
52 | errorText: '',
53 | placeholder: '',
54 | }
55 | } as Meta;
56 |
57 | const Template: Story> = (args) => {
58 | return ;
59 | };
60 |
61 | export const TextInput = Template.bind({});
62 | export const TextInputError = Template.bind({});
63 | TextInputError.args = {
64 | errorText: 'Field is required',
65 | isError: true,
66 | };
67 |
--------------------------------------------------------------------------------
/stories/inputs/MarkdownEditor.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import { ComponentProps } from 'react';
3 | import Editor from "../../src/components/common/editor/Editor";
4 | import { Box } from '@mui/system';
5 | import { Container } from '@mui/material';
6 |
7 | export default {
8 | component: Editor,
9 | title: 'Inputs/Markdown Editor',
10 | decorators: [
11 | (Story) => (
12 |
13 |
14 |
15 |
16 |
17 | )
18 | ],
19 | argType: {
20 | onChange: {
21 | control: false,
22 | },
23 | },
24 | args: {
25 | toolbar: true,
26 | placeholder: 'Placeholder',
27 | autofocus: true,
28 | }
29 | } as Meta;
30 |
31 | const Template: Story> = (arg) => {
32 | return ;
33 | }
34 |
35 | export const MarkdownEditor = Template.bind({});
36 |
37 |
--------------------------------------------------------------------------------
/stories/inputs/Tag.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react'
2 | import TagComponent from '../../src/components/common/tag/Tag'
3 | import { ComponentProps } from "react";
4 |
5 | export default {
6 | component: TagComponent,
7 | title: 'Inputs/Tag',
8 | argTypes: {
9 | label: {
10 | type: 'string'
11 | },
12 | avatar: {
13 | control: false,
14 | },
15 | children: {
16 | control: false,
17 | },
18 | classes: {
19 | control: false,
20 | },
21 | color: {
22 | control: false,
23 | },
24 | deleteIcon: {
25 | control: false,
26 | },
27 | clickable: {
28 | control: false,
29 | },
30 | disabled: {
31 | control: false,
32 | },
33 | icon: {
34 | control: false,
35 | },
36 | onDelete: {
37 | control: false,
38 | },
39 | sx: {
40 | control: false,
41 | },
42 | variant: {
43 | control: false,
44 | },
45 | ref: {
46 | control: false,
47 | },
48 | },
49 | args: {
50 | label: 'subsocial',
51 | size: 'medium',
52 | }
53 | } as Meta
54 |
55 | const Template: Story> = (arg) => {
56 | return ;
57 | };
58 |
59 | export const Tag = Template.bind({});
60 |
--------------------------------------------------------------------------------
/stories/inputs/TagInput.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import { ComponentProps } from 'react';
3 | import TagsInputComponent from '../../src/components/common/inputs/tags-input/TagsInput';
4 | import { Box } from '@mui/system';
5 | import { Container } from '@mui/material';
6 |
7 | export default {
8 | component: TagsInputComponent,
9 | title: 'Inputs/Tag Input',
10 | decorators: [
11 | (Story) => (
12 |
13 |
14 |
15 |
16 |
17 | )
18 | ],
19 | argTypes: {
20 | setTags: {
21 | control: false,
22 | },
23 | className: {
24 | control: false,
25 | },
26 | placeholder: {
27 | table: {
28 | disable: true,
29 | }
30 | },
31 | },
32 | args: {
33 | tags: ['subsocial', 'polkadot'],
34 | }
35 | } as Meta;
36 |
37 | const Template: Story> = (args) => {
38 | return
39 | };
40 |
41 | export const TagInput = Template.bind({})
42 |
43 |
--------------------------------------------------------------------------------
/stories/mocked-data/consts.ts:
--------------------------------------------------------------------------------
1 | export const MY_ADDRESS = '3obS9hbc4kATiYuqyHER4B1TuKfbtYC5JsL1FLKq5pjCTk7E';
2 | export const ADDRESS = '3obS9hbc4kATiYuqyHER4B1TuKfbtYC5JsL1FLKq5pjCTgyt';
3 | export const POST_WITHOUT_VOTE = '506';
4 |
--------------------------------------------------------------------------------
/stories/mocked-data/index.ts:
--------------------------------------------------------------------------------
1 | export * from './space';
2 | export * from './posts';
3 | export * from './comment';
4 | export * from './consts';
5 |
--------------------------------------------------------------------------------
/stories/mocked-data/profile.ts:
--------------------------------------------------------------------------------
1 | export const profile = {
2 | struct: {
3 | id:"3obS9hbc4kATiYuqyHER4B1TuKfbtYC5JsL1FLKq5pjCTk7E",
4 | followersCount:0,
5 | followingAccountsCount:0,
6 | followingSpacesCount:8,
7 | reputation:1,
8 | hasProfile:true,
9 | createdByAccount:"3obS9hbc4kATiYuqyHER4B1TuKfbtYC5JsL1FLKq5pjCTk7E",
10 | createdAtBlock:568558,
11 | createdAtTime:1643383512000,
12 | isUpdated:true,
13 | updatedByAccount:"3obS9hbc4kATiYuqyHER4B1TuKfbtYC5JsL1FLKq5pjCTk7E",
14 | updatedAtBlock:824399,
15 | updatedAtTime:1644922584001,
16 | contentId:"bafyreihk2smkjcgn2ojk4vvqcbd5yexegqyscfnzu3konvqtihrpms37ri",
17 | },
18 | id:"bafyreihk2smkjcgn2ojk4vvqcbd5yexegqyscfnzu3konvqtihrpms37ri",
19 | content: {
20 | about:"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
21 | avatar:"",
22 | name:"Subsocial user",
23 | summary:"",
24 | isShowMore:false,
25 | id:"bafyreihk2smkjcgn2ojk4vvqcbd5yexegqyscfnzu3konvqtihrpms37ri",
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/stories/modal/ModalConnections.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import Connections from 'src/components/modal/modal-reactions/ModalConnections';
3 | import { ComponentProps } from 'react';
4 | import MaterialModal from '@mui/material/Modal';
5 |
6 | export default {
7 | component: Connections,
8 | title: 'Modal/Modal Connections',
9 | argTypes: {
10 | activeTab: {
11 | control: {
12 | disable: true,
13 | },
14 | },
15 | count: {
16 | control: {
17 | disable: true,
18 | },
19 | },
20 | accountId: {
21 | control: {
22 | disable: true,
23 | },
24 | },
25 | },
26 | } as Meta;
27 |
28 | const Template: Story> = (args) => {
29 | return (
30 |
38 |
45 |
46 |
47 |
48 | );
49 | };
50 |
51 | export const ModalConnections = Template.bind({});
52 | ModalConnections.args = {
53 | countFollowing: 0,
54 | countFollowers: 0,
55 | };
56 |
--------------------------------------------------------------------------------
/stories/modal/ModalFollowers.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import ModalFollow from 'src/components/modal/modal-reactions/modal-follow/ModalFollow';
3 | import { ComponentProps } from 'react';
4 | import MaterialModal from '@mui/material/Modal';
5 |
6 | export default {
7 | component: ModalFollow,
8 | title: 'Modal/Modal Followers',
9 | argTypes: {
10 | children: {
11 | control: {
12 | disable: true,
13 | },
14 | },
15 | id: {
16 | control: {
17 | disable: true,
18 | },
19 | },
20 | },
21 | } as Meta;
22 |
23 | const Template: Story> = (args) => {
24 | return (
25 |
33 |
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | export const ModalFollowers = Template.bind({});
47 | ModalFollowers.args = {
48 | count: 1,
49 | id: '1020',
50 | };
51 |
--------------------------------------------------------------------------------
/stories/modal/ModalSendTips.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import SendTips from 'src/components/modal/modal-send-tips/ModalSendTips';
3 | import { ComponentProps } from 'react';
4 |
5 | export default {
6 | component: SendTips,
7 | title: 'Modal/Modal Send Tips',
8 | argTypes: {
9 | children: {
10 | control: {
11 | disable: true,
12 | },
13 | },
14 | },
15 | } as Meta;
16 |
17 | const Template: Story> = (args) => {
18 | return ;
19 | };
20 |
21 | export const ModalSendTips = Template.bind({});
22 | ModalSendTips.args = {
23 | open: true,
24 | ownerId: '3oGmzsdeGToQxtNbNrNYVP1mYVza5J2k9foBXav6WdbQTZqF',
25 | };
26 |
--------------------------------------------------------------------------------
/stories/modal/ModalSharePost.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import SharePost from 'src/components/modal/modal-create-shared-post/ModalCreateSharedPost';
3 | import { ComponentProps } from 'react';
4 | import { post } from '../mocked-data/posts';
5 |
6 | export default {
7 | component: SharePost,
8 | title: 'Modal/Modal Share Post',
9 | argTypes: {
10 | children: {
11 | control: {
12 | disable: true,
13 | },
14 | },
15 | },
16 | } as Meta;
17 |
18 | const Template: Story> = (args) => {
19 | return ;
20 | };
21 |
22 | export const ModalSharePost = Template.bind({});
23 | ModalSharePost.args = {
24 | open: true,
25 | postId: post.id,
26 | };
27 |
--------------------------------------------------------------------------------
/stories/post/Post.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import Post from 'src/components/post/post-item/Post';
3 | import { ComponentProps } from 'react';
4 | import { post } from "../mocked-data";
5 |
6 | export default {
7 | component: Post,
8 | title: 'Post/Post Item',
9 | argTypes: {
10 | className: {
11 | control: false,
12 | }
13 | },
14 | args: {
15 | postId: post.id,
16 | isShowActions: true,
17 | withSpace: true,
18 | },
19 | } as Meta;
20 |
21 | const Template: Story> = (arg) => {
22 | return ;
23 | }
24 |
25 | export const PostItem = Template.bind({});
26 |
--------------------------------------------------------------------------------
/stories/post/PostInfo.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import { Container } from '@mui/material';
3 | import React, { ComponentProps } from 'react';
4 | import { profile, space, post } from '../mocked-data';
5 | import PostInfoComponent from 'src/components/post/post-item/PostInfo';
6 | import { Box } from '@mui/system';
7 |
8 | export default {
9 | component: PostInfoComponent,
10 | title: 'Post/Post Info',
11 | decorators: [
12 | (Story) => (
13 |
14 |
15 |
16 |
17 |
18 | ),
19 | ],
20 | args: {
21 | post: post,
22 | space: space,
23 | profile: profile
24 | }
25 | } as Meta;
26 |
27 | const Template: Story> = (arg) => {
28 | return ;
29 | };
30 |
31 | export const PostInfo = Template.bind({});
32 |
--------------------------------------------------------------------------------
/stories/post/SharedPostItem.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import { ComponentProps } from 'react';
3 | import Post from '../../src/components/post/post-item/Post';
4 | import { sharedHiddenPost, sharedPost } from "../mocked-data";
5 |
6 | export default {
7 | component: Post,
8 | title: 'Post/Shared Post Item',
9 | argTypes: {
10 | className: {
11 | control: false,
12 | },
13 | },
14 | args: {
15 | isShowActions: true,
16 | withSpace: true,
17 | },
18 | } as Meta;
19 |
20 | const Template: Story> = (arg) => {
21 | return ;
22 | };
23 |
24 | export const SharedPostItem = Template.bind({});
25 | SharedPostItem.args = {
26 | postId: sharedPost.id,
27 | };
28 |
29 | export const HiddenSharedPostItem = Template.bind({});
30 | HiddenSharedPostItem.args = {
31 | postId: sharedHiddenPost.id,
32 | }
33 |
--------------------------------------------------------------------------------
/stories/profile/AccountListItem.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import VoteUserItemComponent from 'src/components/common/vote-user-item/VoteUserItem';
3 | import { profile } from '../mocked-data';
4 | import { Container, List } from '@mui/material';
5 | import React from 'react';
6 | import { Box } from '@mui/system';
7 |
8 | export default {
9 | component: VoteUserItemComponent,
10 | title: 'Profile/Account List/Account List Item',
11 | decorators: [
12 | (Story) => (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ),
21 | ],
22 | } as Meta;
23 |
24 | export const AccountListItem = () => ;
25 |
--------------------------------------------------------------------------------
/stories/profile/ProfileAccount.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import ProfileAccountComponent from 'src/components/profile/profile-account/ProfileAccount';
3 | import React, { ComponentProps } from 'react';
4 | import { profile } from '../mocked-data';
5 |
6 | export default {
7 | component: ProfileAccountComponent,
8 | title: 'Profile/Profile Account',
9 | decorators: [
10 | (Story) => (
11 |
12 | ),
13 | ],
14 | args: {
15 | ...profile,
16 | activeTab: 'userPosts'
17 | }
18 | } as Meta;
19 |
20 | const Template: Story> = (args) => {
21 | return ;
22 | };
23 |
24 | export const ProfileAccount = Template.bind({});
25 |
--------------------------------------------------------------------------------
/stories/profile/SwitchAccountListItem.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import { profile } from '../mocked-data';
3 | import { SwitchAccountsExtensionItem } from 'src/components/switch-account/SwitchAccountsExtension';
4 | import { Container, List } from '@mui/material';
5 | import React, { ComponentProps } from 'react';
6 | import { Box } from '@mui/system';
7 |
8 | export default {
9 | component: SwitchAccountsExtensionItem,
10 | title: 'Profile/Account List/Account Switch List Item',
11 | decorators: [
12 | (Story) => (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ),
21 | ],
22 | args: {
23 | address: profile.id,
24 | name: 'Subsocial user'
25 | }
26 | } as Meta;
27 |
28 | const Template: Story> = (args) => {
29 | return ;
30 | };
31 |
32 | export const AccountSwitchListItem = Template.bind({});
33 |
34 |
35 |
--------------------------------------------------------------------------------
/stories/shared/Address.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import React, { ComponentProps } from 'react';
3 | import AddressComponent from 'src/components/common/address/Address';
4 | import { MY_ADDRESS } from '../mocked-data';
5 | import { Box } from '@mui/system';
6 |
7 | export default {
8 | component: AddressComponent,
9 | title: 'Shared/Address',
10 | decorators: [
11 | (Story) => (
12 |
13 |
14 |
15 | ),
16 | ],
17 | argTypes: {
18 | className: {
19 | control: {
20 | disable: true
21 | }
22 | },
23 | size: {
24 | options: [ 'sm', 'lg' ]
25 | }
26 | },
27 | args: {
28 | lengthOfAddress: 12,
29 | isCopy: true,
30 | isIcon: false,
31 | size: 'sm',
32 | isQr: false,
33 | label: MY_ADDRESS,
34 | isShort: true,
35 | }
36 | } as Meta;
37 |
38 | const Template: Story> = (args) => {
39 | return ;
40 | };
41 |
42 | export const Address = Template.bind({});
43 |
--------------------------------------------------------------------------------
/stories/shared/Avatar.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import AvatarElement from 'src/components/common/avatar/AvatarElement';
3 | import { AvatarSizes } from 'src/models/common/avatar';
4 | import { ComponentProps } from 'react';
5 |
6 | export default {
7 | component: AvatarElement,
8 | title: 'Shared/Avatar',
9 | argTypes: {
10 | size: {
11 | options: Object.keys(AvatarSizes).filter(size => !Number(size)),
12 | mapping: AvatarSizes,
13 | control: {
14 | type: 'select',
15 | },
16 | defaultValue: AvatarSizes.MEDIUM
17 | },
18 | },
19 | args: {
20 | size: AvatarSizes.LARGE,
21 | id: '3oGmzsdeGToQxtNbNrNYVP1mYVza5J2k9foBXav6WdbQTZqF',
22 | }
23 | } as Meta;
24 |
25 | const Template: Story> = (args) => {
26 | return ;
27 | };
28 |
29 | export const Avatar = Template.bind({});
30 | Avatar.args = {
31 | src: 'QmPK7VRmMmTdyq1bKRNvYd3CFfp1xAGi3VuBD1kfdUNZx2'
32 | };
33 |
34 | export const AvatarWithoutImage = Template.bind({});
35 | AvatarWithoutImage.argTypes = {
36 | src: {
37 | control: {
38 | disable: true,
39 | }
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/stories/shared/Balance.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Story, Meta } from '@storybook/react';
2 | import { ComponentProps } from 'react';
3 | import BalanceComponent from 'src/components/common/balance/Balance';
4 |
5 |
6 | export default {
7 | component: BalanceComponent,
8 | title: 'Shared/Tokens',
9 | argTypes: {
10 | className: {
11 | control: {
12 | disable: true
13 | }
14 | },
15 | },
16 | args: {
17 | isIcon: false,
18 | address: '3qis9Gqjw9jiXHDCWfCYApsQ5TNNYkHhAdzhEzLgNXVPjkK9',
19 | }
20 | } as Meta;
21 |
22 | const Template: Story> = (args) => {
23 | return ;
24 | };
25 |
26 | export const Tokens = Template.bind({});
27 |
--------------------------------------------------------------------------------
/stories/shared/Link.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import Link from 'src/components/common/links/link/Link';
3 |
4 | export default {
5 | component: Link,
6 | title: 'Shared/Links',
7 | } as Meta;
8 |
9 | export const StandardLink = () => Link;
10 |
--------------------------------------------------------------------------------
/stories/shared/LinkIcon.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import Link from 'src/components/common/links/link/Link';
3 | import IconLink from 'src/components/common/links/icon-link/IconLink';
4 | import { Box } from '@mui/system';
5 | import React from 'react';
6 |
7 | export default {
8 | component: Link,
9 | title: 'Shared/Links',
10 | decorators: [
11 | (Story) => (
12 |
13 |
14 |
15 | ),
16 | ],
17 | } as Meta;
18 |
19 | const links = [
20 | 'https://test.com/',
21 | 'https://twitter.com/test',
22 | 'https://www.linkedin.com/in/test/',
23 | 'https://t.me/test',
24 | 'https://github.com/test',
25 | 'https://medium.com/test',
26 | 'https://www.youtube.com/channel/test',
27 | 'https://www.reddit.com/user/Test',
28 | 'https://www.facebook.com/user/Test',
29 | ];
30 |
31 | export const IconLinks = () => <>{links.map((link) => )}>;
32 |
--------------------------------------------------------------------------------
/stories/shared/LinkSmall.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import SmallLinkComponent from '../../src/components/common/links/small-link/SmallLink';
3 |
4 | export default {
5 | component: SmallLinkComponent,
6 | title: 'Shared/Links',
7 | } as Meta;
8 |
9 | export const SmallLink = () => (
10 | Link
11 | );
12 |
--------------------------------------------------------------------------------
/stories/shared/Tabs.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import TabsComponent from '../../src/components/common/tabs/Tabs';
3 | import { ComponentProps } from 'react';
4 |
5 | export default {
6 | component: TabsComponent,
7 | title: 'Shared/Tabs',
8 | } as Meta;
9 |
10 | const Template: Story> = (args) => {
11 | return ;
12 | };
13 |
14 | export const Tabs = Template.bind({});
15 | Tabs.args = {
16 | value: 'posts',
17 | tabs: [
18 | { label: 'My feed', tabValue: 'feeds' },
19 | { label: 'Posts', tabValue: 'posts' },
20 | { label: 'Spaces', tabValue: 'spaces' },
21 | ],
22 | setValue: () => {},
23 | };
24 |
--------------------------------------------------------------------------------
/stories/space/SpaceInfo.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import SpaceInfoComponent from 'src/components/space/space-item/SpaceInfo';
3 | import React, { ComponentProps } from 'react';
4 | import { space } from "../mocked-data";
5 | import { SpaceWithSomeDetails } from "@subsocial/api/types/dto";
6 | import { Container } from "@mui/material";
7 |
8 |
9 | export default {
10 | component: SpaceInfoComponent,
11 | title: 'Space/Space Info',
12 | decorators: [
13 | (Story) => (
14 |
15 |
16 |
17 | ),
18 | ],
19 | args: {
20 | spaceData: space as unknown as SpaceWithSomeDetails
21 | }
22 | } as Meta;
23 |
24 | const Template: Story> = (args) => {
25 | return ;
26 | };
27 |
28 | export const SpaceInfo = Template.bind({});
29 |
--------------------------------------------------------------------------------
/stories/space/SpaceItem.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import { Space as SpaceComponent } from 'src/components/space/space-item/Space';
3 | import React from 'react';
4 | import { space } from '../mocked-data';
5 | import { Container } from '@mui/material';
6 |
7 |
8 | export default {
9 | component: SpaceComponent,
10 | title: 'Space/Space Item',
11 | decorators: [
12 | (Story) => (
13 |
14 |
15 |
16 | ),
17 | ],
18 | } as Meta;
19 |
20 | export const SpaceItem = () => ;
21 |
--------------------------------------------------------------------------------
/stories/space/SpaceSelect.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import React, { ComponentProps } from 'react';
3 | import SelectSpaces from "src/components/common/select-spaces/SelectSpaces";
4 | import { Box } from "@mui/system";
5 | import { space, spaces } from "../mocked-data";
6 | import { Container } from "@mui/material";
7 |
8 |
9 | export default {
10 | component: SelectSpaces,
11 | title: 'Space/Space Select',
12 | decorators: [
13 | (Story) => (
14 |
15 |
16 |
17 |
18 |
19 | ),
20 | ],
21 | argTypes: {
22 | className: {
23 | control: {
24 | disable: true
25 | }
26 | }
27 | },
28 | args: {
29 | initialId: space.id,
30 | spaceIds: spaces.map(space => space.id),
31 | },
32 | } as Meta;
33 |
34 | const Template: Story> = (args) => {
35 | return ;
36 | };
37 |
38 | export const SpaceSelect = Template.bind({});
39 |
--------------------------------------------------------------------------------
/stories/text/SeeMore.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/react';
2 | import SeeMoreComponent from 'src/components/common/links/see-more/SeeMore';
3 |
4 | export default {
5 | component: SeeMoreComponent,
6 | title: 'Text/See More',
7 | } as Meta;
8 |
9 | export const SeeMore = () => ;
10 |
--------------------------------------------------------------------------------
/stories/text/Text.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import TextComponent from 'src/components/common/text/Text';
3 | import { TextSizes } from 'src/models/common/typography';
4 | import { ComponentProps } from 'react';
5 |
6 | export default {
7 | component: TextComponent,
8 | title: 'Text/Paragraph',
9 | } as Meta;
10 |
11 | const Template: Story> = ({ children, ...args }) => {
12 | return {children};
13 | };
14 |
15 | export const TextNormal = Template.bind({});
16 | TextNormal.args = {
17 | type: TextSizes.NORMAL,
18 | children: 'Text normal'
19 | };
20 |
21 | export const TextSecondary = Template.bind({});
22 | TextSecondary.args = {
23 | type: TextSizes.SECONDARY,
24 | children: 'Text secondary'
25 | };
26 |
27 |
--------------------------------------------------------------------------------
/stories/text/TextWithSeeMore.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import { ComponentProps } from 'react';
3 | import { AccountDescription as AccountDescriptionComponent} from 'src/components/account/AccountDescription';
4 |
5 | export default {
6 | component: AccountDescriptionComponent,
7 | title: 'Text/Account Description',
8 | args: {
9 | summary: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has b...',
10 | about: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.',
11 | isShowMore: true
12 | }
13 | } as Meta;
14 |
15 | const Template: Story> = ({ children, ...args }) => {
16 | return {children};
17 | };
18 |
19 | export const AccountDescription = Template.bind({});
20 |
--------------------------------------------------------------------------------
/stories/text/Title.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, Story } from '@storybook/react';
2 | import TitleComponent from 'src/components/common/title/Title';
3 | import { TitleSizes } from 'src/models/common/typography';
4 | import { ComponentProps } from 'react';
5 |
6 | export default {
7 | component: TitleComponent,
8 | title: 'Text/Title',
9 | } as Meta;
10 |
11 | const Template: Story> = ({ children, ...args }) => {
12 | return {children};
13 | };
14 |
15 | export const TitleProfile = Template.bind({});
16 | TitleProfile.args = {
17 | type: TitleSizes.PROFILE,
18 | children: 'Title profile'
19 | };
20 |
21 | export const TitlePreview = Template.bind({});
22 | TitlePreview.args = {
23 | type: TitleSizes.PREVIEW,
24 | children: 'Title preview'
25 | };
26 |
27 | export const TitleDetails = Template.bind({});
28 | TitleDetails.args = {
29 | type: TitleSizes.DETAILS,
30 | children: 'Title details'
31 | };
32 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "baseUrl": "."
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------