From d5ed6c75d74abcecebf066bdefde73bcede5d50e Mon Sep 17 00:00:00 2001 From: muerwre <33246675+muerwre@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:54:52 +0700 Subject: [PATCH 01/20] Using custom registry (#75) --- .drone.yml | 109 ++++++++++++++++-------------------------- .env.development | 2 + .env_example | 3 -- .gitignore | 1 - docker/www/Dockerfile | 6 ++- 5 files changed, 48 insertions(+), 73 deletions(-) create mode 100644 .env.development delete mode 100644 .env_example diff --git a/.drone.yml b/.drone.yml index 0ddbd29c..c84cd569 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,78 +7,53 @@ platform: arch: amd64 steps: - - name: compress - image: alpine - commands: - - rm -rf ./app.tar.bz2 - - tar -cjf ./app.tar.bz2 -C ./ . - - name: upload - image: drillster/drone-rsync + - name: build-master + image: plugins/docker when: branch: - master - - develop environment: - RSYNC_KEY: - from_secret: rsync_key - RSYNC_USER: - from_secret: rsync_user - PLUGIN_ARGS: -zz -O --no-perms + REACT_APP_API_HOST: https://pig.vault48.org/ + REACT_APP_REMOTE_CURRENT: https://pig.vault48.org/static/ settings: - port: 22522 - hosts: - - vault48.org - source: ./ - user: ${rsync_user} - key: ${rsync_key} - target: /tmp/vault-frontend-${DRONE_BRANCH} - include: - - "app.tar.bz2" - exclude: - - "*" - - name: build - image: appleboy/drone-ssh - when: - branch: - - master - - develop - environment: - BUILD_PATH: - from_secret: build_path - ENV_PATH: - from_secret: env_path - settings: - host: vault48.org + dockerfile: docker/www/Dockerfile + build_args_from_env: + - REACT_APP_API_HOST + - REACT_APP_REMOTE_CURRENT + tag: + - ${DRONE_BRANCH} + custom_labels: + - "commit=${DRONE_COMMIT_SHA}" username: - from_secret: rsync_user - key: - from_secret: rsync_key - envs: [build_path, env_path] - port: 22522 - script_stop: true - script: - - mkdir -p $${BUILD_PATH}/${DRONE_BRANCH} - - rm -rf $${BUILD_PATH}/${DRONE_BRANCH}/* - - cd $${BUILD_PATH}/${DRONE_BRANCH} - - tar -xjf /tmp/vault-frontend-${DRONE_BRANCH}/app.tar.bz2 -C ./ - - cp -a $${ENV_PATH}/${DRONE_BRANCH}/. $${BUILD_PATH}/${DRONE_BRANCH} - - docker-compose build - - docker-compose up -d - - name: telgram_notify - image: appleboy/drone-telegram + from_secret: global_docker_login + password: + from_secret: global_docker_password + registry: + from_secret: global_docker_registry + repo: + from_secret: docker_repo + - name: build-develop + image: plugins/docker when: - status: - - success - - failure + branch: + - develop + environment: + REACT_APP_API_HOST: https://pig.staging.vault48.org/ + REACT_APP_REMOTE_CURRENT: https://pig.staging.vault48.org/static/ settings: - token: - from_secret: telegram_token - to: - from_secret: telegram_chat_id - format: markdown - message: > - {{#success build.status}}🤓{{else}}😨{{/success}} - [{{repo.name}} / {{commit.branch}}]({{ build.link }}) - ``` - {{ commit.message }} - ``` + dockerfile: docker/www/Dockerfile + build_args_from_env: + - REACT_APP_API_HOST + - REACT_APP_REMOTE_CURRENT + tag: + - ${DRONE_BRANCH} + custom_labels: + - "commit=${DRONE_COMMIT_SHA}" + username: + from_secret: global_docker_login + password: + from_secret: global_docker_password + registry: + from_secret: global_docker_registry + repo: + from_secret: docker_repo diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..f0b95934 --- /dev/null +++ b/.env.development @@ -0,0 +1,2 @@ +REACT_APP_API_HOST: https://pig.staging.vault48.org/ +REACT_APP_REMOTE_CURRENT: https://pig.staging.vault48.org/static/ diff --git a/.env_example b/.env_example deleted file mode 100644 index 06034750..00000000 --- a/.env_example +++ /dev/null @@ -1,3 +0,0 @@ -REACT_APP_API_HOST = https://pig.staging.vault48.org/ -REACT_APP_REMOTE_CURRENT = https://pig.staging.vault48.org/static/ -EXPOSE = 4000 diff --git a/.gitignore b/.gitignore index 1e572c99..a0d7b1b9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ /npm-debug.log /.idea /dist -/.env diff --git a/docker/www/Dockerfile b/docker/www/Dockerfile index 46e2cf2a..aaaf2188 100644 --- a/docker/www/Dockerfile +++ b/docker/www/Dockerfile @@ -1,13 +1,15 @@ -# stage1 as builder FROM node:dubnium-alpine as builder COPY package.json yarn.lock ./ RUN yarn COPY . . +ARG REACT_APP_API_HOST +ARG REACT_APP_REMOTE_CURRENT +ENV REACT_APP_API_HOST $REACT_APP_API_HOST +ENV REACT_APP_REMOTE_CURRENT $REACT_APP_REMOTE_CURRENT RUN yarn build FROM nginx:alpine COPY docker/www/nginx.conf /etc/nginx/nginx.conf RUN rm -rf /usr/share/nginx/html/* COPY --from=builder /build /usr/share/nginx/html -EXPOSE ${EXPOSE} 80 ENTRYPOINT ["nginx", "-g", "daemon off;"] From b43fb35b479a6909fd7b43203252d1990e29e78b Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Thu, 30 Sep 2021 16:59:32 +0700 Subject: [PATCH 02/20] added github link --- .env.development | 4 ++-- src/components/boris/BorisContacts/index.tsx | 7 +++++++ src/sprites/Sprites.tsx | 9 +++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.env.development b/.env.development index f0b95934..43827806 100644 --- a/.env.development +++ b/.env.development @@ -1,2 +1,2 @@ -REACT_APP_API_HOST: https://pig.staging.vault48.org/ -REACT_APP_REMOTE_CURRENT: https://pig.staging.vault48.org/static/ +REACT_APP_API_HOST=https://pig.staging.vault48.org/ +REACT_APP_REMOTE_CURRENT=https://pig.staging.vault48.org/static/ diff --git a/src/components/boris/BorisContacts/index.tsx b/src/components/boris/BorisContacts/index.tsx index ef4d1fbb..b16a0b3c 100644 --- a/src/components/boris/BorisContacts/index.tsx +++ b/src/components/boris/BorisContacts/index.tsx @@ -19,6 +19,13 @@ const BorisContacts: FC = () => ( link="https://t.me/boris48bot" subtitle="телеграм-бот" /> + + ); diff --git a/src/sprites/Sprites.tsx b/src/sprites/Sprites.tsx index 45477b49..e48cde68 100644 --- a/src/sprites/Sprites.tsx +++ b/src/sprites/Sprites.tsx @@ -325,6 +325,15 @@ const Sprites: FC = () => ( d="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10c5.52,0,10-4.48,10-10C22,6.48,17.52,2,12,2z M16.64,8.8 c-0.15,1.58-0.8,5.42-1.13,7.19c-0.14,0.75-0.42,1-0.68,1.03c-0.58,0.05-1.02-0.38-1.58-0.75c-0.88-0.58-1.38-0.94-2.23-1.5 c-0.99-0.65-0.35-1.01,0.22-1.59c0.15-0.15,2.71-2.48,2.76-2.69c0.01-0.03,0.01-0.12-0.05-0.18c-0.06-0.05-0.14-0.03-0.21-0.02 c-0.09,0.02-1.49,0.95-4.22,2.79c-0.4,0.27-0.76,0.41-1.08,0.4c-0.36-0.01-1.04-0.2-1.55-0.37c-0.63-0.2-1.12-0.31-1.08-0.66 c0.02-0.18,0.27-0.36,0.74-0.55c2.92-1.27,4.86-2.11,5.83-2.51c2.78-1.16,3.35-1.36,3.73-1.36c0.08,0,0.27,0.02,0.39,0.12 c0.1,0.08,0.13,0.19,0.14,0.27C16.63,8.48,16.65,8.66,16.64,8.8z" /> + + + + + ); From a808045a7d5c891be3ff98779ac18c633bc032ba Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 1 Oct 2021 12:01:11 +0700 Subject: [PATCH 03/20] made sample profile page --- src/components/comment/CommentForm/index.tsx | 2 +- .../comment/CommentFormAttaches/index.tsx | 2 +- src/components/common/Avatar/index.tsx | 13 +++- src/components/editors/AudioEditor/index.tsx | 2 +- .../editors/EditorUploadButton/index.tsx | 2 +- .../editors/EditorUploadCoverButton/index.tsx | 2 +- src/components/editors/ImageEditor/index.tsx | 2 +- src/containers/dialogs/EditorDialog/index.tsx | 2 +- .../profile/ProfilePageLeft/index.tsx | 56 +++++--------- .../ProfilePageLeft/styles.module.scss | 77 +++++-------------- src/layouts/FlowLayout/styles.module.scss | 62 +-------------- src/layouts/ProfileLayout/index.tsx | 19 ++++- src/layouts/ProfileLayout/styles.module.scss | 16 ++-- src/styles/variables.scss | 36 +++++++++ src/utils/hooks/useCommentFormFormik.ts | 2 +- .../{fileUploader.tsx => useFileUploader.tsx} | 0 src/utils/hooks/useNodeFormFormik.ts | 2 +- 17 files changed, 118 insertions(+), 179 deletions(-) rename src/utils/hooks/{fileUploader.tsx => useFileUploader.tsx} (100%) diff --git a/src/components/comment/CommentForm/index.tsx b/src/components/comment/CommentForm/index.tsx index d1a9337e..7fbddc87 100644 --- a/src/components/comment/CommentForm/index.tsx +++ b/src/components/comment/CommentForm/index.tsx @@ -3,7 +3,7 @@ import { useCommentFormFormik } from '~/utils/hooks/useCommentFormFormik'; import { FormikProvider } from 'formik'; import { LocalCommentFormTextarea } from '~/components/comment/LocalCommentFormTextarea'; import { Button } from '~/components/input/Button'; -import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/fileUploader'; +import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/useFileUploader'; import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants'; import { CommentFormAttachButtons } from '~/components/comment/CommentFormAttachButtons'; import { CommentFormFormatButtons } from '~/components/comment/CommentFormFormatButtons'; diff --git a/src/components/comment/CommentFormAttaches/index.tsx b/src/components/comment/CommentFormAttaches/index.tsx index 7422c134..c0d2b0af 100644 --- a/src/components/comment/CommentFormAttaches/index.tsx +++ b/src/components/comment/CommentFormAttaches/index.tsx @@ -7,7 +7,7 @@ import { SortEnd } from 'react-sortable-hoc'; import { moveArrItem } from '~/utils/fn'; import { useDropZone } from '~/utils/hooks'; import { COMMENT_FILE_TYPES, UPLOAD_TYPES } from '~/redux/uploads/constants'; -import { useFileUploaderContext } from '~/utils/hooks/fileUploader'; +import { useFileUploaderContext } from '~/utils/hooks/useFileUploader'; const CommentFormAttaches: FC = () => { const uploader = useFileUploaderContext(); diff --git a/src/components/common/Avatar/index.tsx b/src/components/common/Avatar/index.tsx index 538ccc3a..e6955762 100644 --- a/src/components/common/Avatar/index.tsx +++ b/src/components/common/Avatar/index.tsx @@ -10,11 +10,20 @@ interface Props extends DivProps { url?: string; username?: string; size?: number; + preset?: typeof PRESETS[keyof typeof PRESETS]; innerRef?: React.Ref; } -const Avatar: FC = ({ url, username, size, className, innerRef, ...rest }) => { - const backgroundImage = !!url ? `url('${getURLFromString(url, PRESETS.avatar)}')` : undefined; +const Avatar: FC = ({ + url, + username, + size, + className, + innerRef, + preset = PRESETS.avatar, + ...rest +}) => { + const backgroundImage = !!url ? `url('${getURLFromString(url, preset)}')` : undefined; const onOpenProfile = useCallback(() => openUserProfile(username), [username]); return ( diff --git a/src/components/editors/AudioEditor/index.tsx b/src/components/editors/AudioEditor/index.tsx index 4eab9548..c817a16a 100644 --- a/src/components/editors/AudioEditor/index.tsx +++ b/src/components/editors/AudioEditor/index.tsx @@ -11,7 +11,7 @@ import { NodeEditorProps } from '~/redux/node/types'; import { useNodeImages } from '~/utils/hooks/node/useNodeImages'; import { useNodeAudios } from '~/utils/hooks/node/useNodeAudios'; import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik'; -import { useFileUploaderContext } from '~/utils/hooks/fileUploader'; +import { useFileUploaderContext } from '~/utils/hooks/useFileUploader'; type IProps = NodeEditorProps; diff --git a/src/components/editors/EditorUploadButton/index.tsx b/src/components/editors/EditorUploadButton/index.tsx index c2cb1dc3..8553c120 100644 --- a/src/components/editors/EditorUploadButton/index.tsx +++ b/src/components/editors/EditorUploadButton/index.tsx @@ -3,7 +3,7 @@ import styles from './styles.module.scss'; import { Icon } from '~/components/input/Icon'; import { UPLOAD_TYPES } from '~/redux/uploads/constants'; import { IEditorComponentProps } from '~/redux/node/types'; -import { useFileUploaderContext } from '~/utils/hooks/fileUploader'; +import { useFileUploaderContext } from '~/utils/hooks/useFileUploader'; import { getFileType } from '~/utils/uploader'; import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik'; import { Button } from '~/components/input/Button'; diff --git a/src/components/editors/EditorUploadCoverButton/index.tsx b/src/components/editors/EditorUploadCoverButton/index.tsx index dcf46f32..56cd55cd 100644 --- a/src/components/editors/EditorUploadCoverButton/index.tsx +++ b/src/components/editors/EditorUploadCoverButton/index.tsx @@ -11,7 +11,7 @@ import { getURL } from '~/utils/dom'; import { Icon } from '~/components/input/Icon'; import { PRESETS } from '~/constants/urls'; import { IEditorComponentProps } from '~/redux/node/types'; -import { useFileUploader, useFileUploaderContext } from '~/utils/hooks/fileUploader'; +import { useFileUploader, useFileUploaderContext } from '~/utils/hooks/useFileUploader'; import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik'; import { getFileType } from '~/utils/uploader'; diff --git a/src/components/editors/ImageEditor/index.tsx b/src/components/editors/ImageEditor/index.tsx index 43ae7502..d401bbb6 100644 --- a/src/components/editors/ImageEditor/index.tsx +++ b/src/components/editors/ImageEditor/index.tsx @@ -6,7 +6,7 @@ import { selectUploads } from '~/redux/uploads/selectors'; import { ImageGrid } from '~/components/editors/ImageGrid'; import styles from './styles.module.scss'; import { NodeEditorProps } from '~/redux/node/types'; -import { useFileUploaderContext } from '~/utils/hooks/fileUploader'; +import { useFileUploaderContext } from '~/utils/hooks/useFileUploader'; type IProps = NodeEditorProps; diff --git a/src/containers/dialogs/EditorDialog/index.tsx b/src/containers/dialogs/EditorDialog/index.tsx index 0fa54f5e..988bf793 100644 --- a/src/containers/dialogs/EditorDialog/index.tsx +++ b/src/containers/dialogs/EditorDialog/index.tsx @@ -7,7 +7,7 @@ import { CoverBackdrop } from '~/components/containers/CoverBackdrop'; import { prop } from 'ramda'; import { useNodeFormFormik } from '~/utils/hooks/useNodeFormFormik'; import { EditorButtons } from '~/components/editors/EditorButtons'; -import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/fileUploader'; +import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/useFileUploader'; import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants'; import { FormikProvider } from 'formik'; import { INode } from '~/redux/types'; diff --git a/src/containers/profile/ProfilePageLeft/index.tsx b/src/containers/profile/ProfilePageLeft/index.tsx index fb2af296..6559c065 100644 --- a/src/containers/profile/ProfilePageLeft/index.tsx +++ b/src/containers/profile/ProfilePageLeft/index.tsx @@ -9,6 +9,9 @@ import classNames from 'classnames'; import styles from './styles.module.scss'; import markdown from '~/styles/common/markdown.module.scss'; +import { ProfileAvatar } from '~/containers/profile/ProfileAvatar'; +import { Avatar } from '~/components/common/Avatar'; +import { Markdown } from '~/components/containers/Markdown'; interface IProps { profile: IAuthState['profile']; @@ -16,49 +19,30 @@ interface IProps { } const ProfilePageLeft: FC = ({ username, profile }) => { - const thumb = useMemo(() => { - if (!profile || !profile.user || !profile.user.photo) return ''; - - return getURL(profile.user.photo, PRESETS.small_hero); - }, [profile]); - return (
-
+ -
-
-
- {profile.is_loading ? : profile?.user?.fullname} -
+
+
+ {profile.is_loading ? : profile?.user?.fullname} +
-
- {profile.is_loading ? : `~${profile?.user?.username}`} -
- -
- - - Профиль - - - - - Настройки - - - - - Сообщения - -
+
+ {profile.is_loading ? : `~${profile?.user?.username}`}
- {profile && profile.user && profile.user.description && false && ( -
- {formatText(profile?.user?.description || '')} -
+ {profile && profile.user && profile.user.description && ( + )}
); diff --git a/src/containers/profile/ProfilePageLeft/styles.module.scss b/src/containers/profile/ProfilePageLeft/styles.module.scss index 94b59f8a..0de94bc0 100644 --- a/src/containers/profile/ProfilePageLeft/styles.module.scss +++ b/src/containers/profile/ProfilePageLeft/styles.module.scss @@ -1,92 +1,51 @@ @import "src/styles/variables"; .wrap { + @include outer_shadow; + + padding: $gap $gap $gap * 2; + box-sizing: border-box; display: flex; align-items: stretch; justify-content: stretch; flex-direction: column; + background: $comment_bg; + height: 100%; + border-radius: $radius; } .avatar { width: 100%; - padding-bottom: 75%; - border-radius: 0 $radius 0 0; - background: 50% 50% no-repeat; - background-size: cover; -} - -.region_wrap { - width: 100%; - // padding: 0 10px; - position: relative; - margin-top: -$radius; - box-sizing: border-box; + height: 0; + padding-bottom: 100%; + margin-bottom: $gap * 2; } .region { - // background: $content_bg; - background: darken($content_bg, 2%); width: 100%; - border-radius: 0 $radius $radius 0; + text-align: center; } .name { font: $font_24_semibold; color: white; - padding: $gap $gap 0 $gap; text-transform: uppercase; width: 100%; box-sizing: border-box; + margin-bottom: 4px; } .username { - font: $font_14_semibold; - padding: 0 $gap $gap $gap; + font: $font_14_regular; box-sizing: border-box; width: 100%; color: transparentize(white, 0.5); - margin-top: $gap / 2; -} - -.menu { - padding: $gap 0 $gap 0; - display: flex; - align-items: stretch; - width: 100%; - flex-direction: column; - box-sizing: border-box; - display: none; - - a { - width: 100%; - color: inherit; - text-decoration: none; - text-transform: uppercase; - font: $font_18_semibold; - padding: $gap $gap; - display: flex; - align-items: center; - justify-content: flex-start; - opacity: 0.5; - box-sizing: border-box; - transition: opacity 0.25s; - - &:hover { - opacity: 1; - } - } - - svg { - margin-right: $gap; - fill: currentColor; - } } .description { - padding: $gap; - box-sizing: border-box; - // background: darken($content_bg, 2%); - background: darken($content_bg, 4%); - // margin: 0 $gap; - border-radius: 0 0 $radius $radius; + @include clamp(3, 21px * 3); + line-height: 21px; + font: $font_14_regular; + margin-top: $gap * 3; + display: none; } diff --git a/src/layouts/FlowLayout/styles.module.scss b/src/layouts/FlowLayout/styles.module.scss index e9f96bdd..4c919f4c 100644 --- a/src/layouts/FlowLayout/styles.module.scss +++ b/src/layouts/FlowLayout/styles.module.scss @@ -9,66 +9,15 @@ $cols: $content_width / $cell; -@mixin fluid { - @media(min-width: $content_width) { - .fluid & { - @content - } - } -} - .container { max-width: $content_width; width: 100%; - - &.fluid { - padding: 0 $gap; - box-sizing: border-box; - max-width: none; - } } .grid { - width: 100%; - box-sizing: border-box; - display: grid; - - grid-template-columns: repeat(auto-fit, minmax($cell - 5, 1fr)); grid-template-rows: 50vh $cell; - grid-auto-rows: $cell; - grid-auto-flow: row dense; - grid-column-gap: $gap; - grid-row-gap: $gap; - - @include fluid { - grid-template-columns: repeat(auto-fit, minmax($fluid_cell - 5, 1fr)); - grid-template-rows: $fluid_cell; - grid-auto-rows: $fluid_cell; - } - - @media (max-width: ($cell + 10) * 3) { - grid-template-columns: repeat(auto-fill, minmax($fluid_cell - 20, 1fr)); - grid-auto-rows: $fluid_cell; - grid-template-rows: calc(50vw - 10px) $fluid_cell; - } - - @media (max-width: $cell_tablet) { - grid-template-rows: calc(66vw - 10px) auto $fluid_cell; - } - - @media (max-width: $cell_mobile) { - // rework stamp, so it will be shown as smaller one on mobiles - grid-template-columns: repeat(auto-fill, minmax(calc(50vw - 20px), 1fr)); - grid-template-rows: calc(80vw - 10px) auto 50vw; - grid-auto-rows: 50vw; - } - - @media (max-width: ($fluid_cell + 5) * 1.5 + 20) { - grid-template-columns: repeat(auto-fill, minmax(calc(50vw - 20px), 1fr)); - grid-template-rows: calc(80vw - 10px) auto 50vw; - grid-auto-rows: 50vw; - } + @include flow_grid; } .pad_last { @@ -86,15 +35,6 @@ $cols: $content_width / $cell; align-items: center; justify-content: center; font: $font_24_semibold; - - @include fluid { - grid-row-end: span 2; - grid-column-end: span 4; - - @media(max-width: $content_width) { - grid-column-end: -1; - } - } } .stamp { diff --git a/src/layouts/ProfileLayout/index.tsx b/src/layouts/ProfileLayout/index.tsx index 950f4ca0..24b6dca9 100644 --- a/src/layouts/ProfileLayout/index.tsx +++ b/src/layouts/ProfileLayout/index.tsx @@ -4,9 +4,13 @@ import { Route, RouteComponentProps, Switch } from 'react-router'; import { useDispatch } from 'react-redux'; import { authLoadProfile } from '~/redux/auth/actions'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; -import { selectAuthProfile } from '~/redux/auth/selectors'; +import { selectAuthProfile, selectUser } from '~/redux/auth/selectors'; import { ProfilePageLeft } from '~/containers/profile/ProfilePageLeft'; import { Container } from '~/containers/main/Container'; +import { FlowGrid } from '~/components/flow/FlowGrid'; +import { FlowLayout } from '~/layouts/FlowLayout'; +import { Sticky } from '~/components/containers/Sticky'; +import { selectFlow } from '~/redux/flow/selectors'; type Props = RouteComponentProps<{ username: string }> & {}; @@ -15,6 +19,9 @@ const ProfileLayout: FC = ({ params: { username }, }, }) => { + const { nodes } = useShallowSelect(selectFlow); + const user = useShallowSelect(selectUser); + const dispatch = useDispatch(); useEffect(() => { @@ -25,7 +32,15 @@ const ProfileLayout: FC = ({ return ( - +
+ + + +
+ +
+ +
); }; diff --git a/src/layouts/ProfileLayout/styles.module.scss b/src/layouts/ProfileLayout/styles.module.scss index 104bc693..aa05c21f 100644 --- a/src/layouts/ProfileLayout/styles.module.scss +++ b/src/layouts/ProfileLayout/styles.module.scss @@ -1,20 +1,16 @@ @import "src/styles/variables"; .wrap { - flex: 1; - display: flex; - align-items: stretch; - justify-content: stretch; - border-radius: $radius; + display: grid; + grid-template-columns: $cell auto; + grid-column-gap: $gap; } +.grid { + @include flow_grid; +} .left { - flex: 1; - background: darken($content_bg, 2%); - border-radius: 0 $radius $radius 0; - box-sizing: border-box; } .right { - flex: 4; } diff --git a/src/styles/variables.scss b/src/styles/variables.scss index e7f42e0c..baf86e5e 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -268,3 +268,39 @@ $sidebar_border: transparentize(white, 0.95); $color: mix($wisegreen, $content_bg, 30%); background: linear-gradient(170deg, lighten($color, 10%), $color); } + +@mixin flow_grid { + width: 100%; + box-sizing: border-box; + display: grid; + + grid-template-columns: repeat(auto-fit, minmax($cell - 5, 1fr)); + grid-auto-rows: $cell; + + grid-auto-flow: row dense; + grid-column-gap: $gap; + grid-row-gap: $gap; + + @media (max-width: ($cell + 10) * 3) { + grid-template-columns: repeat(auto-fill, minmax($fluid_cell - 20, 1fr)); + grid-auto-rows: $fluid_cell; + grid-template-rows: calc(50vw - 10px) $fluid_cell; + } + + @media (max-width: $cell_tablet) { + grid-template-rows: calc(66vw - 10px) auto $fluid_cell; + } + + @media (max-width: $cell_mobile) { + // rework stamp, so it will be shown as smaller one on mobiles + grid-template-columns: repeat(auto-fill, minmax(calc(50vw - 20px), 1fr)); + grid-template-rows: calc(80vw - 10px) auto 50vw; + grid-auto-rows: 50vw; + } + + @media (max-width: ($fluid_cell + 5) * 1.5 + 20) { + grid-template-columns: repeat(auto-fill, minmax(calc(50vw - 20px), 1fr)); + grid-template-rows: calc(80vw - 10px) auto 50vw; + grid-auto-rows: 50vw; + } +} diff --git a/src/utils/hooks/useCommentFormFormik.ts b/src/utils/hooks/useCommentFormFormik.ts index cd222db5..24592101 100644 --- a/src/utils/hooks/useCommentFormFormik.ts +++ b/src/utils/hooks/useCommentFormFormik.ts @@ -2,7 +2,7 @@ import { IComment, INode } from '~/redux/types'; import { useCallback, useEffect, useRef } from 'react'; import { FormikHelpers, useFormik, useFormikContext } from 'formik'; import { array, object, string } from 'yup'; -import { FileUploader } from '~/utils/hooks/fileUploader'; +import { FileUploader } from '~/utils/hooks/useFileUploader'; import { useDispatch } from 'react-redux'; import { nodePostLocalComment } from '~/redux/node/actions'; diff --git a/src/utils/hooks/fileUploader.tsx b/src/utils/hooks/useFileUploader.tsx similarity index 100% rename from src/utils/hooks/fileUploader.tsx rename to src/utils/hooks/useFileUploader.tsx diff --git a/src/utils/hooks/useNodeFormFormik.ts b/src/utils/hooks/useNodeFormFormik.ts index d04e9200..f570811e 100644 --- a/src/utils/hooks/useNodeFormFormik.ts +++ b/src/utils/hooks/useNodeFormFormik.ts @@ -1,5 +1,5 @@ import { IComment, INode } from '~/redux/types'; -import { FileUploader } from '~/utils/hooks/fileUploader'; +import { FileUploader } from '~/utils/hooks/useFileUploader'; import { useCallback, useEffect, useRef } from 'react'; import { FormikHelpers, useFormik, useFormikContext } from 'formik'; import { object, string } from 'yup'; From 3cdc3824579c58f520b49a17087940eb4991b0d2 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 1 Oct 2021 16:12:27 +0700 Subject: [PATCH 04/20] added fake profile stats --- .../profile/ProfilePageStats/index.tsx | 21 +++++++++ .../ProfilePageStats/styles.module.scss | 43 +++++++++++++++++++ src/layouts/ProfileLayout/index.tsx | 8 +++- src/layouts/ProfileLayout/styles.module.scss | 4 ++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/containers/profile/ProfilePageStats/index.tsx create mode 100644 src/containers/profile/ProfilePageStats/styles.module.scss diff --git a/src/containers/profile/ProfilePageStats/index.tsx b/src/containers/profile/ProfilePageStats/index.tsx new file mode 100644 index 00000000..53ae4674 --- /dev/null +++ b/src/containers/profile/ProfilePageStats/index.tsx @@ -0,0 +1,21 @@ +import React, { FC } from 'react'; +import styles from './styles.module.scss'; + +interface Props {} + +const Row: FC<{ count: number; title: string }> = ({ count, title }) => ( +
+
{count > 999 ? '999+' : count}
+
{title}
+
+); + +const ProfilePageStats: FC = () => ( +
+ + + +
+); + +export { ProfilePageStats }; diff --git a/src/containers/profile/ProfilePageStats/styles.module.scss b/src/containers/profile/ProfilePageStats/styles.module.scss new file mode 100644 index 00000000..a943ab8a --- /dev/null +++ b/src/containers/profile/ProfilePageStats/styles.module.scss @@ -0,0 +1,43 @@ +@import "~/styles/variables"; + +.wrap { + @include outer_shadow; + + padding: $gap; + background: $content_bg; + border-radius: $radius; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + grid-row-gap: $gap; + grid-column-gap: $gap; + grid-auto-flow: row; + + & > .row:not(:last-child) { + margin-bottom: $gap; + } +} + +.row { + width: 100%; + background-color: lighten($content_bg, 4%); + border-radius: $radius; + padding: $gap; + box-sizing: border-box; + height: 100%; +} + +.counter { + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font: $font_32_bold; + height: 60px; +} + +.title { + font: $font_12_semibold; + text-align: center; + word-wrap: break-word; + opacity: 0.5; +} diff --git a/src/layouts/ProfileLayout/index.tsx b/src/layouts/ProfileLayout/index.tsx index 24b6dca9..27f1df73 100644 --- a/src/layouts/ProfileLayout/index.tsx +++ b/src/layouts/ProfileLayout/index.tsx @@ -11,6 +11,7 @@ import { FlowGrid } from '~/components/flow/FlowGrid'; import { FlowLayout } from '~/layouts/FlowLayout'; import { Sticky } from '~/components/containers/Sticky'; import { selectFlow } from '~/redux/flow/selectors'; +import { ProfilePageStats } from '~/containers/profile/ProfilePageStats'; type Props = RouteComponentProps<{ username: string }> & {}; @@ -34,7 +35,12 @@ const ProfileLayout: FC = ({
- +
+ +
+
+ +
diff --git a/src/layouts/ProfileLayout/styles.module.scss b/src/layouts/ProfileLayout/styles.module.scss index aa05c21f..5f1f1c20 100644 --- a/src/layouts/ProfileLayout/styles.module.scss +++ b/src/layouts/ProfileLayout/styles.module.scss @@ -14,3 +14,7 @@ .right { } + +.row { + margin-bottom: $gap; +} From 33c17616e93404255c5bd3e0d463869a7c0b2180 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 1 Oct 2021 16:17:46 +0700 Subject: [PATCH 05/20] added icons for fake profile stats --- src/containers/profile/ProfilePageStats/index.tsx | 11 +++++++++-- .../profile/ProfilePageStats/styles.module.scss | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/containers/profile/ProfilePageStats/index.tsx b/src/containers/profile/ProfilePageStats/index.tsx index 53ae4674..432a3689 100644 --- a/src/containers/profile/ProfilePageStats/index.tsx +++ b/src/containers/profile/ProfilePageStats/index.tsx @@ -1,10 +1,16 @@ import React, { FC } from 'react'; import styles from './styles.module.scss'; +import { Icon } from '~/components/input/Icon'; interface Props {} -const Row: FC<{ count: number; title: string }> = ({ count, title }) => ( +const Row: FC<{ count: number; title: string; icon?: string }> = ({ count, title, icon }) => (
+ {icon && ( +
+ +
+ )}
{count > 999 ? '999+' : count}
{title}
@@ -14,7 +20,8 @@ const ProfilePageStats: FC = () => (
- + +
); diff --git a/src/containers/profile/ProfilePageStats/styles.module.scss b/src/containers/profile/ProfilePageStats/styles.module.scss index a943ab8a..8cd17f7b 100644 --- a/src/containers/profile/ProfilePageStats/styles.module.scss +++ b/src/containers/profile/ProfilePageStats/styles.module.scss @@ -24,6 +24,7 @@ padding: $gap; box-sizing: border-box; height: 100%; + position: relative; } .counter { @@ -41,3 +42,12 @@ word-wrap: break-word; opacity: 0.5; } + +.icon { + position: absolute; + opacity: 0.5; + top: 5px; + left: 5px; + fill: transparentize(black, 0.5); + stroke: none; +} From fb8ad315c06f7de7344e7fd253959953e0decc64 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Mon, 4 Oct 2021 14:34:15 +0700 Subject: [PATCH 06/20] changed profile stats appearance --- .../boris/BorisStatsBackend/index.tsx | 105 +-- .../BorisStatsBackend/styles.module.scss | 28 +- src/components/common/StatsRow/index.tsx | 21 + .../common/StatsRow/styles.module.scss | 23 + src/components/common/SubTitle/index.tsx | 19 + .../common/SubTitle/styles.module.scss | 7 + .../profile/ProfilePageStats/index.tsx | 30 +- .../ProfilePageStats/styles.module.scss | 22 +- src/layouts/ProfileLayout/index.tsx | 11 +- src/layouts/ProfileLayout/styles.module.scss | 9 +- src/sprites/boris_lab.svg | 883 ++++++++++++++++++ 11 files changed, 1033 insertions(+), 125 deletions(-) create mode 100644 src/components/common/StatsRow/index.tsx create mode 100644 src/components/common/StatsRow/styles.module.scss create mode 100644 src/components/common/SubTitle/index.tsx create mode 100644 src/components/common/SubTitle/styles.module.scss create mode 100644 src/sprites/boris_lab.svg diff --git a/src/components/boris/BorisStatsBackend/index.tsx b/src/components/boris/BorisStatsBackend/index.tsx index e3a034e3..2fbb779c 100644 --- a/src/components/boris/BorisStatsBackend/index.tsx +++ b/src/components/boris/BorisStatsBackend/index.tsx @@ -1,102 +1,73 @@ import React, { FC } from 'react'; import { IBorisState } from '~/redux/boris/reducer'; import styles from './styles.module.scss'; -import { Placeholder } from '~/components/placeholders/Placeholder'; import { sizeOf } from '~/utils/dom'; +import { StatsRow } from '~/components/common/StatsRow'; +import { SubTitle } from '~/components/common/SubTitle'; interface IProps { stats: IBorisState['stats']; } -const Row: FC<{ isLoading: boolean }> = ({ isLoading, children }) => ( -
  • - {isLoading ? ( - <> - - - - ) : ( - children - )} -
  • -); - const BorisStatsBackend: FC = ({ stats: { is_loading, backend } }) => { - // const is_loading = true; - if (!backend && !is_loading) { return null; } return (
    -
    - - Юнитс - -
    + + Юнитс +
      - - В сознании - {backend.users.alive} - + + {backend.users.alive} + - - Криокамера - {backend.users.total - backend.users.alive} - + + {backend.users.total - backend.users.alive} +
    -
    - - Контент - -
    + + Контент +
      - - Фотографии - {backend.nodes.images} - + + {backend.nodes.images} + - - Письма - {backend.nodes.texts} - + + {backend.nodes.texts} + - - Видеозаписи - {backend.nodes.videos} - + + {backend.nodes.videos} + - - Аудиозаписи - {backend.nodes.audios} - + + {backend.nodes.audios} + - - Комментарии - {backend.comments.total} - + + {backend.comments.total} +
    -
    - - Сторедж - -
    + + Сторедж +
      - - Файлы - {backend.files.count} - + + {backend.files.count} + - - На диске - {sizeOf(backend.files.size)} - + + {sizeOf(backend.files.size)} +
    ); diff --git a/src/components/boris/BorisStatsBackend/styles.module.scss b/src/components/boris/BorisStatsBackend/styles.module.scss index 16fe4a3c..638e4277 100644 --- a/src/components/boris/BorisStatsBackend/styles.module.scss +++ b/src/components/boris/BorisStatsBackend/styles.module.scss @@ -1,34 +1,10 @@ @import "src/styles/variables"; -.value { - float: right; - color: white; - font: $font_12_semibold; - line-height: 24px; +.title { + margin: $gap * 2 0 $gap; } .wrap { - ul { - font: $font_12_regular; - line-height: 24px; - text-transform: uppercase; - - li { - border-bottom: 1px solid #333333; - color: #aaaaaa; - - &:last-child { - border-bottom: none; - } - } - } -} - -.title { - font: $font_12_semibold; - text-transform: uppercase; - opacity: 0.3; - margin: $gap * 2 0 $gap; } .subtitle { diff --git a/src/components/common/StatsRow/index.tsx b/src/components/common/StatsRow/index.tsx new file mode 100644 index 00000000..d8931c85 --- /dev/null +++ b/src/components/common/StatsRow/index.tsx @@ -0,0 +1,21 @@ +import React, { FC } from 'react'; +import { Placeholder } from '~/components/placeholders/Placeholder'; +import styles from './styles.module.scss'; + +const StatsRow: FC<{ isLoading: boolean; label: string }> = ({ isLoading, label, children }) => ( +
  • + {isLoading ? ( + <> + + + + ) : ( + <> +
    {label}
    +
    {children}
    + + )} +
  • +); + +export { StatsRow }; diff --git a/src/components/common/StatsRow/styles.module.scss b/src/components/common/StatsRow/styles.module.scss new file mode 100644 index 00000000..fd970b35 --- /dev/null +++ b/src/components/common/StatsRow/styles.module.scss @@ -0,0 +1,23 @@ +@import '~/styles/variables.scss'; + +.row { + @include row_shadow; + color: #aaaaaa; + display: flex; + flex-direction: row; + font: $font_12_regular; + line-height: 24px; + text-transform: uppercase; +} + +.label { + flex: 1; + margin-right: $gap; +} + +.value { + float: right; + color: white; + font: $font_12_semibold; + line-height: 24px; +} diff --git a/src/components/common/SubTitle/index.tsx b/src/components/common/SubTitle/index.tsx new file mode 100644 index 00000000..49da1ddf --- /dev/null +++ b/src/components/common/SubTitle/index.tsx @@ -0,0 +1,19 @@ +import React, { FC } from 'react'; +import { Placeholder } from '~/components/placeholders/Placeholder'; +import { DivProps } from '~/utils/types'; +import classNames from 'classnames'; +import styles from './styles.module.scss'; + +interface Props extends DivProps { + isLoading?: boolean; +} + +const SubTitle: FC = ({ isLoading, children, ...rest }) => ( +
    + + {children} + +
    +); + +export { SubTitle }; diff --git a/src/components/common/SubTitle/styles.module.scss b/src/components/common/SubTitle/styles.module.scss new file mode 100644 index 00000000..b53a4451 --- /dev/null +++ b/src/components/common/SubTitle/styles.module.scss @@ -0,0 +1,7 @@ +@import "~/styles/variables.scss"; + +.title { + font: $font_12_semibold; + text-transform: uppercase; + opacity: 0.3; +} diff --git a/src/containers/profile/ProfilePageStats/index.tsx b/src/containers/profile/ProfilePageStats/index.tsx index 432a3689..bd2aba85 100644 --- a/src/containers/profile/ProfilePageStats/index.tsx +++ b/src/containers/profile/ProfilePageStats/index.tsx @@ -1,27 +1,35 @@ import React, { FC } from 'react'; import styles from './styles.module.scss'; -import { Icon } from '~/components/input/Icon'; +import { StatsRow } from '~/components/common/StatsRow'; +import { SubTitle } from '~/components/common/SubTitle'; interface Props {} const Row: FC<{ count: number; title: string; icon?: string }> = ({ count, title, icon }) => (
    - {icon && ( -
    - -
    - )} -
    {count > 999 ? '999+' : count}
    {title}
    +
    {count > 999 ? '999+' : count}
    ); const ProfilePageStats: FC = () => (
    - - - - + Ачивментс + +
      + + 9 + + + 99 + + + 999+ + + + 99 + +
    ); diff --git a/src/containers/profile/ProfilePageStats/styles.module.scss b/src/containers/profile/ProfilePageStats/styles.module.scss index 8cd17f7b..51d24c8e 100644 --- a/src/containers/profile/ProfilePageStats/styles.module.scss +++ b/src/containers/profile/ProfilePageStats/styles.module.scss @@ -7,10 +7,9 @@ background: $content_bg; border-radius: $radius; display: grid; - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); - grid-row-gap: $gap; grid-column-gap: $gap; grid-auto-flow: row; + grid-row-gap: $gap; & > .row:not(:last-child) { margin-bottom: $gap; @@ -19,12 +18,12 @@ .row { width: 100%; - background-color: lighten($content_bg, 4%); border-radius: $radius; - padding: $gap; box-sizing: border-box; height: 100%; position: relative; + display: flex; + flex-direction: row; } .counter { @@ -32,22 +31,13 @@ align-items: center; justify-content: center; text-align: center; - font: $font_32_bold; - height: 60px; + font: $font_16_semibold; } .title { font: $font_12_semibold; - text-align: center; + text-align: left; word-wrap: break-word; opacity: 0.5; -} - -.icon { - position: absolute; - opacity: 0.5; - top: 5px; - left: 5px; - fill: transparentize(black, 0.5); - stroke: none; + flex: 1; } diff --git a/src/layouts/ProfileLayout/index.tsx b/src/layouts/ProfileLayout/index.tsx index 27f1df73..4412a23b 100644 --- a/src/layouts/ProfileLayout/index.tsx +++ b/src/layouts/ProfileLayout/index.tsx @@ -1,6 +1,6 @@ import React, { FC, useEffect } from 'react'; import styles from './styles.module.scss'; -import { Route, RouteComponentProps, Switch } from 'react-router'; +import { RouteComponentProps } from 'react-router'; import { useDispatch } from 'react-redux'; import { authLoadProfile } from '~/redux/auth/actions'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; @@ -8,10 +8,10 @@ import { selectAuthProfile, selectUser } from '~/redux/auth/selectors'; import { ProfilePageLeft } from '~/containers/profile/ProfilePageLeft'; import { Container } from '~/containers/main/Container'; import { FlowGrid } from '~/components/flow/FlowGrid'; -import { FlowLayout } from '~/layouts/FlowLayout'; import { Sticky } from '~/components/containers/Sticky'; import { selectFlow } from '~/redux/flow/selectors'; import { ProfilePageStats } from '~/containers/profile/ProfilePageStats'; +import { Card } from '~/components/containers/Card'; type Props = RouteComponentProps<{ username: string }> & {}; @@ -38,6 +38,13 @@ const ProfileLayout: FC = ({
    + + {!!profile.user?.description && ( +
    + {profile.user.description} +
    + )} +
    diff --git a/src/layouts/ProfileLayout/styles.module.scss b/src/layouts/ProfileLayout/styles.module.scss index 5f1f1c20..9182e8df 100644 --- a/src/layouts/ProfileLayout/styles.module.scss +++ b/src/layouts/ProfileLayout/styles.module.scss @@ -12,9 +12,12 @@ .left { } -.right { -} - .row { margin-bottom: $gap; } + +.description { + font: $font_14_semibold; + text-align: center; + padding: $gap * 2 $gap; +} diff --git a/src/sprites/boris_lab.svg b/src/sprites/boris_lab.svg new file mode 100644 index 00000000..446867d8 --- /dev/null +++ b/src/sprites/boris_lab.svg @@ -0,0 +1,883 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f10a1fa2d8adfb9af5e541669a1d98b7d060a102 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 11:31:21 +0700 Subject: [PATCH 07/20] added dropzones for comments and node editors --- package.json | 2 + src/components/comment/CommentForm/index.tsx | 6 +-- .../comment/CommentFormAttaches/index.tsx | 4 +- .../comment/CommentFormDropzone/index.tsx | 14 ------ src/components/editors/AudioEditor/index.tsx | 13 +++-- .../EditorActionsPanel/styles.module.scss | 4 ++ src/components/editors/EditorFiller/index.tsx | 3 +- .../editors/EditorFiller/styles.module.scss | 4 ++ src/components/editors/ImageEditor/index.tsx | 17 +++---- .../editors/ImageEditor/styles.module.scss | 5 +- src/components/input/DropHereIcon/index.tsx | 18 +++++++ .../input/DropHereIcon/styles.module.scss | 8 +++ .../upload/UploadDropzone/index.tsx | 48 ++++++++++++++++++ .../upload/UploadDropzone/styles.module.scss | 34 +++++++++++++ src/containers/App.tsx | 6 +-- src/containers/dialogs/EditorDialog/index.tsx | 1 + src/redux/uploads/constants.ts | 2 + src/sprites/Sprites.tsx | 8 +++ src/sprites/boris_lab.svg | 4 +- src/utils/hooks/index.ts | 4 +- src/utils/hooks/useDragDetector.tsx | 50 +++++++++++++++++++ src/utils/types.ts | 2 + yarn.lock | 31 ++++++++++++ 23 files changed, 247 insertions(+), 41 deletions(-) delete mode 100644 src/components/comment/CommentFormDropzone/index.tsx create mode 100644 src/components/editors/EditorFiller/styles.module.scss create mode 100644 src/components/input/DropHereIcon/index.tsx create mode 100644 src/components/input/DropHereIcon/styles.module.scss create mode 100644 src/components/upload/UploadDropzone/index.tsx create mode 100644 src/components/upload/UploadDropzone/styles.module.scss create mode 100644 src/utils/hooks/useDragDetector.tsx diff --git a/package.json b/package.json index a7698031..0fe42007 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "ramda": "^0.26.1", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-dropzone": "^11.4.2", "react-masonry-css": "^1.0.16", "react-popper": "^2.2.3", "react-redux": "^7.2.2", @@ -69,6 +70,7 @@ ] }, "devDependencies": { + "@types/throttle-debounce": "^2.1.0", "@craco/craco": "5.8.0", "@types/classnames": "^2.2.7", "@types/marked": "^1.2.2", diff --git a/src/components/comment/CommentForm/index.tsx b/src/components/comment/CommentForm/index.tsx index 7fbddc87..119ad897 100644 --- a/src/components/comment/CommentForm/index.tsx +++ b/src/components/comment/CommentForm/index.tsx @@ -11,7 +11,7 @@ import { CommentFormAttaches } from '~/components/comment/CommentFormAttaches'; import { LoaderCircle } from '~/components/input/LoaderCircle'; import { IComment, INode } from '~/redux/types'; import { EMPTY_COMMENT } from '~/redux/node/constants'; -import { CommentFormDropzone } from '~/components/comment/CommentFormDropzone'; +import { UploadDropzone } from '~/components/upload/UploadDropzone'; import styles from './styles.module.scss'; import { ERROR_LITERAL } from '~/constants/errors'; import { useInputPasteUpload } from '~/utils/hooks/useInputPasteUpload'; @@ -50,7 +50,7 @@ const CommentForm: FC = ({ comment, nodeId, onCancelEdit }) => { useInputPasteUpload(textarea, uploader.uploadFiles); return ( - +
    @@ -103,7 +103,7 @@ const CommentForm: FC = ({ comment, nodeId, onCancelEdit }) => {
    -
    + ); }; diff --git a/src/components/comment/CommentFormAttaches/index.tsx b/src/components/comment/CommentFormAttaches/index.tsx index c0d2b0af..1523a578 100644 --- a/src/components/comment/CommentFormAttaches/index.tsx +++ b/src/components/comment/CommentFormAttaches/index.tsx @@ -5,7 +5,7 @@ import { SortableAudioGrid } from '~/components/editors/SortableAudioGrid'; import { IFile } from '~/redux/types'; import { SortEnd } from 'react-sortable-hoc'; import { moveArrItem } from '~/utils/fn'; -import { useDropZone } from '~/utils/hooks'; +import { useFileDropZone } from '~/utils/hooks'; import { COMMENT_FILE_TYPES, UPLOAD_TYPES } from '~/redux/uploads/constants'; import { useFileUploaderContext } from '~/utils/hooks/useFileUploader'; @@ -29,7 +29,7 @@ const CommentFormAttaches: FC = () => { pending, ]); - const onDrop = useDropZone(uploadFiles, COMMENT_FILE_TYPES); + const onDrop = useFileDropZone(uploadFiles, COMMENT_FILE_TYPES); const hasImageAttaches = images.length > 0 || pendingImages.length > 0; const hasAudioAttaches = audios.length > 0 || pendingAudios.length > 0; diff --git a/src/components/comment/CommentFormDropzone/index.tsx b/src/components/comment/CommentFormDropzone/index.tsx deleted file mode 100644 index 5c06fb67..00000000 --- a/src/components/comment/CommentFormDropzone/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React, { FC } from 'react'; -import { COMMENT_FILE_TYPES } from '~/redux/uploads/constants'; -import { useDropZone } from '~/utils/hooks'; - -interface IProps { - onUpload: (files: File[]) => void; -} - -const CommentFormDropzone: FC = ({ children, onUpload }) => { - const onDrop = useDropZone(onUpload, COMMENT_FILE_TYPES); - return
    {children}
    ; -}; - -export { CommentFormDropzone }; diff --git a/src/components/editors/AudioEditor/index.tsx b/src/components/editors/AudioEditor/index.tsx index c817a16a..81c92931 100644 --- a/src/components/editors/AudioEditor/index.tsx +++ b/src/components/editors/AudioEditor/index.tsx @@ -12,12 +12,13 @@ import { useNodeImages } from '~/utils/hooks/node/useNodeImages'; import { useNodeAudios } from '~/utils/hooks/node/useNodeAudios'; import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik'; import { useFileUploaderContext } from '~/utils/hooks/useFileUploader'; +import { UploadDropzone } from '~/components/upload/UploadDropzone'; type IProps = NodeEditorProps; const AudioEditor: FC = () => { const { values } = useNodeFormContext(); - const { pending, setFiles } = useFileUploaderContext()!; + const { pending, setFiles, uploadFiles } = useFileUploaderContext()!; const images = useNodeImages(values); const audios = useNodeAudios(values); @@ -35,10 +36,12 @@ const AudioEditor: FC = () => { const setAudios = useCallback(values => setFiles([...values, ...images]), [setFiles, images]); return ( -
    - - -
    + +
    + + +
    +
    ); }; diff --git a/src/components/editors/EditorActionsPanel/styles.module.scss b/src/components/editors/EditorActionsPanel/styles.module.scss index 5bda9666..e272b93a 100644 --- a/src/components/editors/EditorActionsPanel/styles.module.scss +++ b/src/components/editors/EditorActionsPanel/styles.module.scss @@ -11,9 +11,13 @@ z-index: 10; display: flex; flex-direction: row; + pointer-events: none; + touch-action: none; & > * { margin: 0 $gap / 2; + pointer-events: all; + touch-action: auto; &:first-child { margin-left: 0; diff --git a/src/components/editors/EditorFiller/index.tsx b/src/components/editors/EditorFiller/index.tsx index d2de7b7b..d49d750b 100644 --- a/src/components/editors/EditorFiller/index.tsx +++ b/src/components/editors/EditorFiller/index.tsx @@ -1,9 +1,10 @@ import React, { FC } from 'react'; import { Filler } from '~/components/containers/Filler'; import { IEditorComponentProps } from '~/redux/node/types'; +import styles from './styles.module.scss'; type IProps = IEditorComponentProps & {}; -const EditorFiller: FC = () => ; +const EditorFiller: FC = () => ; export { EditorFiller }; diff --git a/src/components/editors/EditorFiller/styles.module.scss b/src/components/editors/EditorFiller/styles.module.scss new file mode 100644 index 00000000..6f0ce1d0 --- /dev/null +++ b/src/components/editors/EditorFiller/styles.module.scss @@ -0,0 +1,4 @@ +.filler { + touch-action: none; + pointer-events: none; +} diff --git a/src/components/editors/ImageEditor/index.tsx b/src/components/editors/ImageEditor/index.tsx index d401bbb6..8dc2a0f0 100644 --- a/src/components/editors/ImageEditor/index.tsx +++ b/src/components/editors/ImageEditor/index.tsx @@ -1,22 +1,21 @@ -import React, { FC, useMemo, useCallback } from 'react'; -import { connect } from 'react-redux'; -import { INode, IFile } from '~/redux/types'; -import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; -import { selectUploads } from '~/redux/uploads/selectors'; +import React, { FC } from 'react'; import { ImageGrid } from '~/components/editors/ImageGrid'; import styles from './styles.module.scss'; import { NodeEditorProps } from '~/redux/node/types'; import { useFileUploaderContext } from '~/utils/hooks/useFileUploader'; +import { UploadDropzone } from '~/components/upload/UploadDropzone'; type IProps = NodeEditorProps; const ImageEditor: FC = () => { - const { pending, files, setFiles } = useFileUploaderContext()!; + const { pending, files, setFiles, uploadFiles } = useFileUploaderContext()!; return ( -
    - -
    + +
    + +
    +
    ); }; diff --git a/src/components/editors/ImageEditor/styles.module.scss b/src/components/editors/ImageEditor/styles.module.scss index 10286e1a..c8fc297b 100644 --- a/src/components/editors/ImageEditor/styles.module.scss +++ b/src/components/editors/ImageEditor/styles.module.scss @@ -1,6 +1,9 @@ -@import "src/styles/variables"; +@import 'src/styles/variables'; .wrap { min-height: 200px; padding-bottom: $upload_button_height + $gap; } + +div.dropzone { +} diff --git a/src/components/input/DropHereIcon/index.tsx b/src/components/input/DropHereIcon/index.tsx new file mode 100644 index 00000000..8711d3ca --- /dev/null +++ b/src/components/input/DropHereIcon/index.tsx @@ -0,0 +1,18 @@ +import React, { FC } from 'react'; +import styles from './styles.module.scss'; +import { SVGProps } from '~/utils/types'; + +interface Props extends SVGProps {} + +const DropHereIcon: FC = ({ ...rest }) => ( + + + + + +); + +export { DropHereIcon }; diff --git a/src/components/input/DropHereIcon/styles.module.scss b/src/components/input/DropHereIcon/styles.module.scss new file mode 100644 index 00000000..6af4e092 --- /dev/null +++ b/src/components/input/DropHereIcon/styles.module.scss @@ -0,0 +1,8 @@ +@keyframes bounce { + 0% { transform: translate(0, -5%); } + 100% { transform: translate(0, 5%); } +} + +.arrow { + animation: bounce alternate infinite 0.25s; +} diff --git a/src/components/upload/UploadDropzone/index.tsx b/src/components/upload/UploadDropzone/index.tsx new file mode 100644 index 00000000..508f9062 --- /dev/null +++ b/src/components/upload/UploadDropzone/index.tsx @@ -0,0 +1,48 @@ +import React, { FC, useCallback } from 'react'; +import Dropzone from 'react-dropzone'; +import classnames from 'classnames'; +import classNames from 'classnames'; +import styles from './styles.module.scss'; +import { DivProps } from '~/utils/types'; +import { DropHereIcon } from '~/components/input/DropHereIcon'; +import { useDragDetector } from '~/utils/hooks/useDragDetector'; + +interface IProps extends DivProps { + onUpload: (files: File[]) => void; + helperClassName?: string; +} + +const UploadDropzone: FC = ({ children, onUpload, helperClassName, ...rest }) => { + const { isDragging: isDraggingOnBody, onStopDragging } = useDragDetector(); + const onDrop = useCallback( + (files: File[]) => { + onStopDragging(); + onUpload(files); + }, + [onUpload] + ); + + return ( + + {({ getRootProps, isDragActive }) => ( +
    + {children} +
    + +
    +
    + )} +
    + ); +}; + +export { UploadDropzone }; diff --git a/src/components/upload/UploadDropzone/styles.module.scss b/src/components/upload/UploadDropzone/styles.module.scss new file mode 100644 index 00000000..8bda07d1 --- /dev/null +++ b/src/components/upload/UploadDropzone/styles.module.scss @@ -0,0 +1,34 @@ +@import '~/styles/variables'; + +.zone { + position: relative; + z-index: 1; + outline: none; +} + +.helper { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: transparentize($wisegreen, 0.7); + border-radius: $radius; + z-index: 10; + box-shadow: inset $wisegreen 0 0 0 2px; + display: none; + align-items: center; + justify-content: center; + pointer-events: none; + touch-action: none; + + &.active, :global(.dragging) & { + display: flex; + } +} + +svg.icon { + width: auto; + height: 72px; + fill: $wisegreen; +} diff --git a/src/containers/App.tsx b/src/containers/App.tsx index af312b6e..d0393e26 100644 --- a/src/containers/App.tsx +++ b/src/containers/App.tsx @@ -10,6 +10,7 @@ import { BlurWrapper } from '~/components/containers/BlurWrapper'; import { PageCover } from '~/components/containers/PageCover'; import { BottomContainer } from '~/containers/main/BottomContainer'; import { MainRouter } from '~/containers/main/MainRouter'; +import { DragDetectorProvider } from '~/utils/hooks/useDragDetector'; const mapStateToProps = state => ({ modal: selectModal(state), @@ -21,7 +22,7 @@ type IProps = typeof mapDispatchToProps & ReturnType & { const Component: FC = ({ modal: { is_shown } }) => { return ( -
    + @@ -32,9 +33,8 @@ const Component: FC = ({ modal: { is_shown } }) => { - -
    +
    ); }; diff --git a/src/containers/dialogs/EditorDialog/index.tsx b/src/containers/dialogs/EditorDialog/index.tsx index 988bf793..3ad3d045 100644 --- a/src/containers/dialogs/EditorDialog/index.tsx +++ b/src/containers/dialogs/EditorDialog/index.tsx @@ -15,6 +15,7 @@ import { ModalWrapper } from '~/components/dialogs/ModalWrapper'; import { useTranslatedError } from '~/utils/hooks/useTranslatedError'; import { useCloseOnEscape } from '~/utils/hooks'; import { EditorConfirmClose } from '~/components/editors/EditorConfirmClose'; +import { UploadDropzone } from '~/components/upload/UploadDropzone'; interface Props extends IDialogProps { node: INode; diff --git a/src/redux/uploads/constants.ts b/src/redux/uploads/constants.ts index 8d8cf134..f5280bd4 100644 --- a/src/redux/uploads/constants.ts +++ b/src/redux/uploads/constants.ts @@ -74,3 +74,5 @@ export const COMMENT_FILE_TYPES = [ ...FILE_MIMES[UPLOAD_TYPES.IMAGE], ...FILE_MIMES[UPLOAD_TYPES.AUDIO], ]; + +export const IMAGE_FILE_TYPES = [...FILE_MIMES[UPLOAD_TYPES.IMAGE]]; diff --git a/src/sprites/Sprites.tsx b/src/sprites/Sprites.tsx index e48cde68..ba0e79d6 100644 --- a/src/sprites/Sprites.tsx +++ b/src/sprites/Sprites.tsx @@ -334,6 +334,14 @@ const Sprites: FC = () => ( d="M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365 c-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63 c0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996 c2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136 c-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559 c-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559 c-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997 c-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851 c5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136 c6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41 c-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126 c-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817 c-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994 c5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849 c7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24 c15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979 c-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146 c9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995 c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906 C438.536,184.851,428.728,148.168,409.132,114.573z" /> + + + + + ); diff --git a/src/sprites/boris_lab.svg b/src/sprites/boris_lab.svg index 446867d8..b47a54e8 100644 --- a/src/sprites/boris_lab.svg +++ b/src/sprites/boris_lab.svg @@ -16,7 +16,7 @@ xmlns:svg="http://www.w3.org/2000/svg"> void, delay: number * @param onUpload -- upload callback * @param allowedTypes -- list of allowed types */ -export const useDropZone = (onUpload: (file: File[]) => void, allowedTypes?: string[]) => { +export const useFileDropZone = (onUpload: (file: File[]) => void, allowedTypes?: string[]) => { return useCallback( event => { event.preventDefault(); + event.stopPropagation(); + const files: File[] = Array.from((event.dataTransfer?.files as File[]) || []).filter( (file: File) => file?.type && (!allowedTypes || allowedTypes.includes(file.type)) ); diff --git a/src/utils/hooks/useDragDetector.tsx b/src/utils/hooks/useDragDetector.tsx new file mode 100644 index 00000000..80760d68 --- /dev/null +++ b/src/utils/hooks/useDragDetector.tsx @@ -0,0 +1,50 @@ +import React, { FC, useContext } from 'react'; +import { createContext, useCallback, useEffect, useState } from 'react'; + +const DragContext = createContext({ + isDragging: false, + setIsDragging: (val: boolean) => {}, +}); + +export const DragDetectorProvider: FC = ({ children }) => { + const [isDragging, setIsDragging] = useState(false); + + return ( + {children} + ); +}; + +export const useDragDetector = () => { + const { isDragging, setIsDragging } = useContext(DragContext); + + const onStopDragging = useCallback(() => setIsDragging(false), [setIsDragging]); + + useEffect(() => { + const addClass = () => setIsDragging(true); + + const removeClass = event => { + // Small hack to ignore intersection with child elements + if (event.pageX !== 0 && event.pageY !== 0) { + return; + } + + setIsDragging(false); + }; + + document.addEventListener('dragenter', addClass); + document.addEventListener('dragover', addClass); + document.addEventListener('dragleave', removeClass); + document.addEventListener('blur', removeClass); + document.addEventListener('drop', onStopDragging); + + return () => { + document.removeEventListener('dragenter', addClass); + document.removeEventListener('dragover', addClass); + document.removeEventListener('dragleave', removeClass); + document.removeEventListener('blur', removeClass); + document.removeEventListener('drop', onStopDragging); + }; + }, [setIsDragging]); + + return { isDragging, onStopDragging }; +}; diff --git a/src/utils/types.ts b/src/utils/types.ts index 7f43acfb..a7d786e1 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -4,3 +4,5 @@ export type DivProps = React.DetailedHTMLProps< React.HTMLAttributes, HTMLDivElement >; + +export type SVGProps = React.SVGProps; diff --git a/yarn.lock b/yarn.lock index e5a6393e..f3b8b5b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1805,6 +1805,11 @@ dependencies: "@types/jest" "*" +"@types/throttle-debounce@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz#1c3df624bfc4b62f992d3012b84c56d41eab3776" + integrity sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ== + "@types/yargs-parser@*": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" @@ -2410,6 +2415,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +attr-accept@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + autoprefixer@^9.6.1: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" @@ -4885,6 +4895,13 @@ file-loader@4.3.0: loader-utils "^1.2.3" schema-utils "^2.5.0" +file-selector@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.4.tgz#7b98286f9dbb9925f420130ea5ed0a69238d4d80" + integrity sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA== + dependencies: + tslib "^2.0.3" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -9361,6 +9378,15 @@ react-dom@^17.0.1: object-assign "^4.1.1" scheduler "^0.20.1" +react-dropzone@^11.4.2: + version "11.4.2" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.4.2.tgz#1eb99e9def4cc7520f4f58e85c853ce52c483d56" + integrity sha512-ocYzYn7Qgp0tFc1gQtUTOaHHSzVTwhWHxxY+r7cj2jJTPfMTZB5GWSJHdIVoxsl+EQENpjJ/6Zvcw0BqKZQ+Eg== + dependencies: + attr-accept "^2.2.1" + file-selector "^0.2.2" + prop-types "^15.7.2" + react-error-overlay@^6.0.7: version "6.0.8" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.8.tgz#474ed11d04fc6bda3af643447d85e9127ed6b5de" @@ -11137,6 +11163,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" From ae93ff065d79d0390ac1b9c2e61402b39203084b Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 12:26:57 +0700 Subject: [PATCH 08/20] fixed key issue on Paragraph --- src/components/placeholders/Paragraph/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/placeholders/Paragraph/index.tsx b/src/components/placeholders/Paragraph/index.tsx index f7777390..7d9b411a 100644 --- a/src/components/placeholders/Paragraph/index.tsx +++ b/src/components/placeholders/Paragraph/index.tsx @@ -17,12 +17,10 @@ const Paragraph: FC = ({ lines = 3, wordsLimit = 12, ...props }) => { [lines, wordsLimit] ); - console.log({ iters }); - return ( - {iters.map(words => ( -
    + {iters.map((words, i) => ( +
    {words.map(word => ( ))} From c986bc434b8a57a8a13ff01805ac0ca94d3092b6 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 12:44:28 +0700 Subject: [PATCH 09/20] added lab pagination --- src/layouts/FlowLayout/index.tsx | 4 --- src/layouts/LabLayout/index.tsx | 3 +++ src/redux/lab/actions.ts | 4 +++ src/redux/lab/constants.ts | 1 + src/redux/lab/sagas.ts | 31 ++++++++++++++++++++++- src/utils/hooks/flow/useFlowPagination.ts | 19 +++----------- src/utils/hooks/lab/useLabPagination.ts | 21 +++++++++++++++ src/utils/hooks/useInfiniteLoader.ts | 17 +++++++++++++ 8 files changed, 79 insertions(+), 21 deletions(-) create mode 100644 src/utils/hooks/lab/useLabPagination.ts create mode 100644 src/utils/hooks/useInfiniteLoader.ts diff --git a/src/layouts/FlowLayout/index.tsx b/src/layouts/FlowLayout/index.tsx index bede37cf..9d03bc87 100644 --- a/src/layouts/FlowLayout/index.tsx +++ b/src/layouts/FlowLayout/index.tsx @@ -55,10 +55,6 @@ const FlowLayout: FC = () => { labUpdates, ]); - useEffect(() => { - window.scrollTo(0, (window as any).flowScrollPos || 0); - }, []); - return (
    diff --git a/src/layouts/LabLayout/index.tsx b/src/layouts/LabLayout/index.tsx index c1d02176..a083c346 100644 --- a/src/layouts/LabLayout/index.tsx +++ b/src/layouts/LabLayout/index.tsx @@ -15,6 +15,7 @@ import { Superpower } from '~/components/boris/Superpower'; import { Toggle } from '~/components/input/Toggle'; import { usePersistedState } from '~/utils/hooks/usePersistedState'; import classNames from 'classnames'; +import { useLabPagination } from '~/utils/hooks/lab/useLabPagination'; interface IProps {} @@ -22,6 +23,8 @@ const LabLayout: FC = () => { const { is_loading } = useShallowSelect(selectLabList); const dispatch = useDispatch(); + useLabPagination({ isLoading: is_loading }); + useEffect(() => { dispatch(labGetList()); dispatch(labGetStats()); diff --git a/src/redux/lab/actions.ts b/src/redux/lab/actions.ts index 3617262f..00dcc529 100644 --- a/src/redux/lab/actions.ts +++ b/src/redux/lab/actions.ts @@ -34,3 +34,7 @@ export const labSeenNode = (nodeId: INode['id']) => ({ type: LAB_ACTIONS.SEEN_NODE, nodeId, }); + +export const labGetMore = () => ({ + type: LAB_ACTIONS.GET_MORE, +}); diff --git a/src/redux/lab/constants.ts b/src/redux/lab/constants.ts index 16e2d985..bc2e150e 100644 --- a/src/redux/lab/constants.ts +++ b/src/redux/lab/constants.ts @@ -10,4 +10,5 @@ export const LAB_ACTIONS = { SET_UPDATES: `${prefix}SET_UPDATES`, GET_UPDATES: `${prefix}GET_UPDATES`, SEEN_NODE: `${prefix}SET_UPDATE_SEEN`, + GET_MORE: `${prefix}GET_MORE`, }; diff --git a/src/redux/lab/sagas.ts b/src/redux/lab/sagas.ts index 5e3baaaf..d19380bb 100644 --- a/src/redux/lab/sagas.ts +++ b/src/redux/lab/sagas.ts @@ -9,7 +9,7 @@ import { import { LAB_ACTIONS } from '~/redux/lab/constants'; import { Unwrap } from '~/redux/types'; import { getLabNodes, getLabStats, getLabUpdates } from '~/redux/lab/api'; -import { selectLabUpdatesNodes } from '~/redux/lab/selectors'; +import { selectLabList, selectLabUpdatesNodes } from '~/redux/lab/selectors'; function* getList({ after = '' }: ReturnType) { try { @@ -53,10 +53,39 @@ function* seenNode({ nodeId }: ReturnType) { yield put(labSetUpdates({ nodes: newNodes })); } +function* getMore() { + try { + yield put(labSetList({ is_loading: true })); + + const list: ReturnType = yield select(selectLabList); + if (list.nodes.length === list.count) { + return; + } + + const last = list.nodes[list.nodes.length]; + + if (!last) { + return; + } + + const after = last.node.created_at; + const { nodes, count }: Unwrap = yield call(getLabNodes, { after }); + const newNodes = [...list.nodes, ...nodes]; + + yield put(labSetList({ nodes: newNodes, count })); + } catch (error) { + yield put(labSetList({ error: error.message })); + } finally { + yield put(labSetList({ is_loading: false })); + } +} + export default function* labSaga() { yield takeLeading(LAB_ACTIONS.GET_LIST, getList); yield takeLeading(LAB_ACTIONS.GET_STATS, getStats); yield takeLeading(LAB_ACTIONS.GET_UPDATES, getUpdates); yield takeLeading(LAB_ACTIONS.SEEN_NODE, seenNode); + + yield takeLeading(LAB_ACTIONS.GET_MORE, getMore); } diff --git a/src/utils/hooks/flow/useFlowPagination.ts b/src/utils/hooks/flow/useFlowPagination.ts index 255d9f55..bb88461c 100644 --- a/src/utils/hooks/flow/useFlowPagination.ts +++ b/src/utils/hooks/flow/useFlowPagination.ts @@ -1,23 +1,10 @@ import { useCallback, useEffect } from 'react'; import { flowGetMore } from '~/redux/flow/actions'; import { useDispatch } from 'react-redux'; +import { useInfiniteLoader } from '~/utils/hooks/useInfiniteLoader'; export const useFlowPagination = ({ isLoading }) => { const dispatch = useDispatch(); - - const onLoadMore = useCallback(() => { - (window as any).flowScrollPos = window.scrollY; - - const pos = window.scrollY + window.innerHeight - document.body.scrollHeight; - - if (isLoading || pos < -600) return; - - dispatch(flowGetMore()); - }, [dispatch, isLoading]); - - useEffect(() => { - window.addEventListener('scroll', onLoadMore); - - return () => window.removeEventListener('scroll', onLoadMore); - }, [onLoadMore]); + const loadMore = useCallback(() => dispatch(flowGetMore()), []); + useInfiniteLoader(loadMore, isLoading); }; diff --git a/src/utils/hooks/lab/useLabPagination.ts b/src/utils/hooks/lab/useLabPagination.ts new file mode 100644 index 00000000..4fb8d071 --- /dev/null +++ b/src/utils/hooks/lab/useLabPagination.ts @@ -0,0 +1,21 @@ +import { useDispatch } from 'react-redux'; +import { useCallback } from 'react'; +import { useInfiniteLoader } from '~/utils/hooks/useInfiniteLoader'; +import { labGetMore } from '~/redux/lab/actions'; +import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; +import { selectLabList } from '~/redux/lab/selectors'; + +export const useLabPagination = ({ isLoading }) => { + const { nodes, count } = useShallowSelect(selectLabList); + + const dispatch = useDispatch(); + const loadMore = useCallback(() => { + if (nodes.length >= count) { + return; + } + + dispatch(labGetMore()); + }, [nodes, count]); + + useInfiniteLoader(loadMore, isLoading); +}; diff --git a/src/utils/hooks/useInfiniteLoader.ts b/src/utils/hooks/useInfiniteLoader.ts new file mode 100644 index 00000000..e6eb2608 --- /dev/null +++ b/src/utils/hooks/useInfiniteLoader.ts @@ -0,0 +1,17 @@ +import { useCallback, useEffect } from 'react'; + +export const useInfiniteLoader = (loader: () => void, isLoading?: boolean) => { + const onLoadMore = useCallback(() => { + const pos = window.scrollY + window.innerHeight - document.body.scrollHeight; + + if (isLoading || pos < -600) return; + + loader(); + }, [loader, isLoading]); + + useEffect(() => { + window.addEventListener('scroll', onLoadMore); + + return () => window.removeEventListener('scroll', onLoadMore); + }, [onLoadMore]); +}; From d31b94c42ae4a026c3825a3b70df8d1e08ed0285 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 13:44:38 +0700 Subject: [PATCH 10/20] fixed node loading --- src/containers/lab/LabGrid/index.tsx | 15 +++++++-------- src/layouts/LabLayout/index.tsx | 8 +++++--- src/redux/lab/sagas.ts | 3 +-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/containers/lab/LabGrid/index.tsx b/src/containers/lab/LabGrid/index.tsx index f5b5e212..6d7bcaec 100644 --- a/src/containers/lab/LabGrid/index.tsx +++ b/src/containers/lab/LabGrid/index.tsx @@ -1,13 +1,15 @@ import React, { FC } from 'react'; import Masonry from 'react-masonry-css'; -import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; import styles from './styles.module.scss'; import { LabNode } from '~/components/lab/LabNode'; -import { selectLabList, selectLabListNodes } from '~/redux/lab/selectors'; import { EMPTY_NODE, NODE_TYPES } from '~/redux/node/constants'; import { values } from 'ramda'; +import { ILabNode } from '~/redux/lab/types'; -interface IProps {} +interface IProps { + isLoading: boolean; + nodes: ILabNode[]; +} const breakpointCols = { default: 2, @@ -26,11 +28,8 @@ const LoadingNode = () => ( /> ); -const LabGrid: FC = () => { - const nodes = useShallowSelect(selectLabListNodes); - const { is_loading } = useShallowSelect(selectLabList); - - if (is_loading) { +const LabGrid: FC = ({ isLoading, nodes }) => { + if (isLoading) { return ( = () => { - const { is_loading } = useShallowSelect(selectLabList); + const { is_loading, nodes } = useShallowSelect(selectLabList); const dispatch = useDispatch(); useLabPagination({ isLoading: is_loading }); @@ -30,16 +30,18 @@ const LabLayout: FC = () => { dispatch(labGetStats()); }, [dispatch]); + const isInitialLoading = is_loading && !nodes.length; + return (
    - +
    - +
    diff --git a/src/redux/lab/sagas.ts b/src/redux/lab/sagas.ts index d19380bb..6668556a 100644 --- a/src/redux/lab/sagas.ts +++ b/src/redux/lab/sagas.ts @@ -62,12 +62,11 @@ function* getMore() { return; } - const last = list.nodes[list.nodes.length]; + const last = list.nodes[list.nodes.length - 1]; if (!last) { return; } - const after = last.node.created_at; const { nodes, count }: Unwrap = yield call(getLabNodes, { after }); const newNodes = [...list.nodes, ...nodes]; From f19fed1c5a978eb09c34eeb0ebaf0187dc7b92df Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 14:45:23 +0700 Subject: [PATCH 11/20] fixed node loading --- src/redux/lab/sagas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/lab/sagas.ts b/src/redux/lab/sagas.ts index 6668556a..b507d02c 100644 --- a/src/redux/lab/sagas.ts +++ b/src/redux/lab/sagas.ts @@ -67,7 +67,7 @@ function* getMore() { if (!last) { return; } - const after = last.node.created_at; + const after = last.node.commented_at || last.node.created_at; const { nodes, count }: Unwrap = yield call(getLabNodes, { after }); const newNodes = [...list.nodes, ...nodes]; From 07852df0dcdd41857cba36e619259f350e9ca55d Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 15:32:02 +0700 Subject: [PATCH 12/20] added user description for deactivated units --- src/components/node/NodeAuthorBlock/index.tsx | 11 +++++----- src/constants/user.ts | 1 + src/utils/hooks/user/useUserDescription.ts | 21 +++++++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/constants/user.ts create mode 100644 src/utils/hooks/user/useUserDescription.ts diff --git a/src/components/node/NodeAuthorBlock/index.tsx b/src/components/node/NodeAuthorBlock/index.tsx index 9e05d03e..9ea9b3d0 100644 --- a/src/components/node/NodeAuthorBlock/index.tsx +++ b/src/components/node/NodeAuthorBlock/index.tsx @@ -3,24 +3,24 @@ import { INode } from '~/redux/types'; import styles from './styles.module.scss'; import { Avatar } from '~/components/common/Avatar'; import { openUserProfile } from '~/utils/user'; -import { useRandomPhrase } from '~/constants/phrases'; +import { useUserDescription } from '~/utils/hooks/user/useUserDescription'; interface Props { node?: INode; } const NodeAuthorBlock: FC = ({ node }) => { - const randomPhrase = useRandomPhrase('USER_DESCRIPTION'); - const onOpenProfile = useCallback(() => openUserProfile(node?.user?.username), [ node?.user?.username, ]); + const description = useUserDescription(node?.user); + if (!node?.user) { return null; } - const { fullname, username, description, photo } = node.user; + const { fullname, username, photo } = node.user; return (
    @@ -28,8 +28,7 @@ const NodeAuthorBlock: FC = ({ node }) => {
    {fullname || username}
    - -
    {description || randomPhrase}
    +
    {description}
    ); diff --git a/src/constants/user.ts b/src/constants/user.ts new file mode 100644 index 00000000..acb60ee0 --- /dev/null +++ b/src/constants/user.ts @@ -0,0 +1 @@ +export const INACTIVE_ACCOUNT_DAYS = 40; diff --git a/src/utils/hooks/user/useUserDescription.ts b/src/utils/hooks/user/useUserDescription.ts new file mode 100644 index 00000000..6565a99d --- /dev/null +++ b/src/utils/hooks/user/useUserDescription.ts @@ -0,0 +1,21 @@ +import { IUser } from '~/redux/auth/types'; +import { useRandomPhrase } from '~/constants/phrases'; +import { differenceInDays, parseISO } from 'date-fns'; +import { INACTIVE_ACCOUNT_DAYS } from '~/constants/user'; + +const today = new Date(); + +export const useUserDescription = (user?: Partial) => { + const randomPhrase = useRandomPhrase('USER_DESCRIPTION'); + + if (!user) { + return ''; + } + + const lastSeen = user.last_seen ? parseISO(user.last_seen) : undefined; + if (!lastSeen || differenceInDays(today, lastSeen) > INACTIVE_ACCOUNT_DAYS) { + return 'Юнит деактивирован'; + } + + return randomPhrase; +}; From d382553b8a19d255a6efe453f20e66c961de0d09 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 15:47:01 +0700 Subject: [PATCH 13/20] fixed titles on lab and node related --- src/components/node/NodeRelated/index.tsx | 5 ++--- .../node/NodeRelated/styles.module.scss | 2 +- src/containers/lab/LabStats/index.tsx | 17 +++++++++-------- src/containers/lab/LabStats/styles.module.scss | 6 ++---- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/components/node/NodeRelated/index.tsx b/src/components/node/NodeRelated/index.tsx index 8e9888f7..17c78588 100644 --- a/src/components/node/NodeRelated/index.tsx +++ b/src/components/node/NodeRelated/index.tsx @@ -3,6 +3,7 @@ import styles from './styles.module.scss'; import { Group } from '~/components/containers/Group'; import { INode } from '~/redux/types'; import { NodeRelatedItem } from '~/components/node/NodeRelatedItem'; +import { SubTitle } from '~/components/common/SubTitle'; interface IProps { title: ReactElement | string; @@ -12,9 +13,7 @@ interface IProps { const NodeRelated: FC = ({ title, items }) => { return ( -
    -
    {title}
    -
    + {title}
    {items.map(item => ( diff --git a/src/components/node/NodeRelated/styles.module.scss b/src/components/node/NodeRelated/styles.module.scss index ff4cbb95..9685b792 100644 --- a/src/components/node/NodeRelated/styles.module.scss +++ b/src/components/node/NodeRelated/styles.module.scss @@ -21,7 +21,7 @@ } .title { - @include title_with_line(); + padding-left: 5px; a { text-decoration: none; diff --git a/src/containers/lab/LabStats/index.tsx b/src/containers/lab/LabStats/index.tsx index f567884b..5d632a0c 100644 --- a/src/containers/lab/LabStats/index.tsx +++ b/src/containers/lab/LabStats/index.tsx @@ -16,6 +16,7 @@ import { import { LabTags } from '~/components/lab/LabTags'; import { LabHeroes } from '~/components/lab/LabHeroes'; import { FlowRecentItem } from '~/components/flow/FlowRecentItem'; +import { SubTitle } from '~/components/common/SubTitle'; interface IProps {} @@ -31,10 +32,10 @@ const LabStats: FC = () => {
    - {isLoading ? ( - - ) : ( - tags.length &&
    Тэги
    + {(!!tags.length || isLoading) && ( + + Тэги + )}
    @@ -56,10 +57,10 @@ const LabStats: FC = () => { )} - {isLoading ? ( - - ) : ( - heroes.length > 0 &&
    Важные
    + {(!!heroes.length || isLoading) && ( + + Важные + )}
    diff --git a/src/containers/lab/LabStats/styles.module.scss b/src/containers/lab/LabStats/styles.module.scss index de9d0c23..4ec81c2f 100644 --- a/src/containers/lab/LabStats/styles.module.scss +++ b/src/containers/lab/LabStats/styles.module.scss @@ -1,10 +1,7 @@ @import "~/styles/variables.scss"; .title { - font: $font_14_semibold; - color: darken(white, 50%); - text-transform: uppercase; - padding: 0 $gap / 2; + padding-bottom: $gap; } .tags.tags { @@ -26,3 +23,4 @@ .updates { padding: 0 $gap / 4 $gap * 2; } + From a5a394c59008b2446296fcecaf702e0a60cce354 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 15:50:59 +0700 Subject: [PATCH 14/20] fixed panel z-index on node title --- src/layouts/NodeLayout/index.tsx | 2 +- src/layouts/NodeLayout/styles.module.scss | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/layouts/NodeLayout/index.tsx b/src/layouts/NodeLayout/index.tsx index 51140207..48b56896 100644 --- a/src/layouts/NodeLayout/index.tsx +++ b/src/layouts/NodeLayout/index.tsx @@ -50,7 +50,7 @@ const NodeLayout: FC = memo(
    {head} - + {block} diff --git a/src/layouts/NodeLayout/styles.module.scss b/src/layouts/NodeLayout/styles.module.scss index 910fea27..2f37e886 100644 --- a/src/layouts/NodeLayout/styles.module.scss +++ b/src/layouts/NodeLayout/styles.module.scss @@ -9,9 +9,8 @@ } .content { - align-items: stretch !important; - - @include vertical_at_tablet; + position: relative; + z-index: 3; } .comments { From 57d803f4d89eb6887bcfa26d1bbabd8468ec3f07 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 16:13:54 +0700 Subject: [PATCH 15/20] fixed user description issue --- src/utils/hooks/user/useUserDescription.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/hooks/user/useUserDescription.ts b/src/utils/hooks/user/useUserDescription.ts index 6565a99d..ddad43ec 100644 --- a/src/utils/hooks/user/useUserDescription.ts +++ b/src/utils/hooks/user/useUserDescription.ts @@ -17,5 +17,5 @@ export const useUserDescription = (user?: Partial) => { return 'Юнит деактивирован'; } - return randomPhrase; + return user.description || randomPhrase; }; From 59ca0762870101abe7c777915dfe6700851ccb56 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 18:13:01 +0700 Subject: [PATCH 16/20] added flow shade --- src/components/flow/Cell/index.tsx | 4 ++- src/components/flow/Cell/styles.module.scss | 1 - src/components/flow/CellShade/index.tsx | 26 +++++++++++++++++++ .../flow/CellShade/styles.module.scss | 12 +++++++++ src/constants/node.ts | 1 + src/redux/types.ts | 1 + src/utils/color.ts | 13 ++++++++++ 7 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/components/flow/CellShade/index.tsx create mode 100644 src/components/flow/CellShade/styles.module.scss create mode 100644 src/constants/node.ts create mode 100644 src/utils/color.ts diff --git a/src/components/flow/Cell/index.tsx b/src/components/flow/Cell/index.tsx index ee587833..bf4ab35a 100644 --- a/src/components/flow/Cell/index.tsx +++ b/src/components/flow/Cell/index.tsx @@ -9,6 +9,7 @@ import { Icon } from '~/components/input/Icon'; import { PRESETS } from '~/constants/urls'; import { NODE_TYPES } from '~/redux/node/constants'; import { Link } from 'react-router-dom'; +import { CellShade } from '~/components/flow/CellShade'; const THUMBNAIL_SIZES = { horizontal: PRESETS.small_hero, @@ -26,7 +27,6 @@ interface IProps { const Cell: FC = ({ node: { id, title, thumbnail, type, flow, description }, can_edit, - onSelect, onChangeCellView, }) => { const ref = useRef(null); @@ -112,6 +112,8 @@ const Cell: FC = ({ )} + +
    {!text &&
    {title || '...'}
    } diff --git a/src/components/flow/Cell/styles.module.scss b/src/components/flow/Cell/styles.module.scss index e6bb28ac..209b10a7 100644 --- a/src/components/flow/Cell/styles.module.scss +++ b/src/components/flow/Cell/styles.module.scss @@ -195,7 +195,6 @@ left: 0; width: 100%; height: 100%; - background: linear-gradient(5deg, transparentize($content_bg, 0), transparentize($content_bg, 1)); z-index: 2; border-radius: $cell_radius; padding: $gap / 2; diff --git a/src/components/flow/CellShade/index.tsx b/src/components/flow/CellShade/index.tsx new file mode 100644 index 00000000..dbc3921f --- /dev/null +++ b/src/components/flow/CellShade/index.tsx @@ -0,0 +1,26 @@ +import React, { FC, useMemo } from 'react'; +import styles from './styles.module.scss'; +import { DEFAULT_DOMINANT_COLOR } from '~/constants/node'; +import { convertHexToRGBA } from '~/utils/color'; +import { DivProps } from '~/utils/types'; +import classNames from 'classnames'; + +interface Props extends DivProps { + color?: string; +} + +const CellShade: FC = ({ color, ...rest }) => { + const background = useMemo(() => { + if (!color || color === DEFAULT_DOMINANT_COLOR) { + return undefined; + } + + return `linear-gradient(10deg, ${color} 30px, ${convertHexToRGBA(color, 0.2)} 200px)`; + }, [color]); + + return ( +
    + ); +}; + +export { CellShade }; diff --git a/src/components/flow/CellShade/styles.module.scss b/src/components/flow/CellShade/styles.module.scss new file mode 100644 index 00000000..76326268 --- /dev/null +++ b/src/components/flow/CellShade/styles.module.scss @@ -0,0 +1,12 @@ +@import "~/styles/variables"; + +.shade { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(5deg, transparentize($content_bg, 0), transparentize($content_bg, 1)); + pointer-events: none; + touch-action: none; +} diff --git a/src/constants/node.ts b/src/constants/node.ts new file mode 100644 index 00000000..0fdebad8 --- /dev/null +++ b/src/constants/node.ts @@ -0,0 +1 @@ +export const DEFAULT_DOMINANT_COLOR = '#000000'; diff --git a/src/redux/types.ts b/src/redux/types.ts index 1b70b603..589578b2 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -133,6 +133,7 @@ export interface INode { flow: { display: 'single' | 'vertical' | 'horizontal' | 'quadro'; show_description: boolean; + dominant_color?: string; }; tags: ITag[]; diff --git a/src/utils/color.ts b/src/utils/color.ts new file mode 100644 index 00000000..07b136e0 --- /dev/null +++ b/src/utils/color.ts @@ -0,0 +1,13 @@ +export const convertHexToRGBA = (hexCode, opacity) => { + let hex = hexCode.replace('#', ''); + + if (hex.length === 3) { + hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`; + } + + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); + + return `rgba(${r},${g},${b},${opacity})`; +}; From 71b94ce585dcaffb93058ae82a4439b09e0c059e Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 18:25:40 +0700 Subject: [PATCH 17/20] added image preloading color for swiper --- src/components/media/ImagePreloader/index.tsx | 7 +++++-- src/components/node/NodeImageSwiperBlock/index.tsx | 1 + src/redux/types.ts | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/media/ImagePreloader/index.tsx b/src/components/media/ImagePreloader/index.tsx index 4e241194..62986b62 100644 --- a/src/components/media/ImagePreloader/index.tsx +++ b/src/components/media/ImagePreloader/index.tsx @@ -7,9 +7,11 @@ import { IFile } from '~/redux/types'; import { LoaderCircle } from '~/components/input/LoaderCircle'; import { Icon } from '~/components/input/Icon'; import { useResizeHandler } from '~/utils/hooks/useResizeHandler'; +import { DEFAULT_DOMINANT_COLOR } from '~/constants/node'; interface IProps { file: IFile; + color?: string; onLoad?: () => void; onClick?: MouseEventHandler; className?: string; @@ -18,7 +20,7 @@ interface IProps { const DEFAULT_WIDTH = 1920; const DEFAULT_HEIGHT = 1020; -const ImagePreloader: FC = ({ file, onLoad, onClick, className }) => { +const ImagePreloader: FC = ({ file, color, onLoad, onClick, className }) => { const [maxHeight, setMaxHeight] = useState(window.innerHeight - 140); const [loaded, setLoaded] = useState(false); const [hasError, setHasError] = useState(false); @@ -48,6 +50,7 @@ const ImagePreloader: FC = ({ file, onLoad, onClick, className }) => { useResizeHandler(onResize); const estimatedWidth = (width * maxHeight) / height; + const fill = color && color !== DEFAULT_DOMINANT_COLOR ? color : '#222222'; return ( <> @@ -67,7 +70,7 @@ const ImagePreloader: FC = ({ file, onLoad, onClick, className }) => { - + {!hasError && ( = ({ node }) => { onLoad={updateSwiper} onClick={() => onOpenPhotoSwipe(i)} className={styles.image} + color={file?.metadata?.dominant_color} /> ))} diff --git a/src/redux/types.ts b/src/redux/types.ts index 589578b2..cc99265b 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -83,6 +83,7 @@ export interface IFile { duration?: number; width?: number; height?: number; + dominant_color?: string; }; createdAt?: string; From 47172cfaf483bdde8a53cfb898f4176b19b65284 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 18:29:56 +0700 Subject: [PATCH 18/20] fixed shade opacity on mobiles --- src/components/flow/CellShade/index.tsx | 2 +- src/components/flow/CellShade/styles.module.scss | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/flow/CellShade/index.tsx b/src/components/flow/CellShade/index.tsx index dbc3921f..e74cc4f6 100644 --- a/src/components/flow/CellShade/index.tsx +++ b/src/components/flow/CellShade/index.tsx @@ -15,7 +15,7 @@ const CellShade: FC = ({ color, ...rest }) => { return undefined; } - return `linear-gradient(10deg, ${color} 30px, ${convertHexToRGBA(color, 0.2)} 200px)`; + return `linear-gradient(10deg, ${color} 30px, ${convertHexToRGBA(color, 0.3)} 200px)`; }, [color]); return ( diff --git a/src/components/flow/CellShade/styles.module.scss b/src/components/flow/CellShade/styles.module.scss index 76326268..dfc82d72 100644 --- a/src/components/flow/CellShade/styles.module.scss +++ b/src/components/flow/CellShade/styles.module.scss @@ -2,11 +2,16 @@ .shade { position: absolute; - top: 0; + bottom: 0; left: 0; right: 0; - bottom: 0; + top: 0; + max-height: $cell; background: linear-gradient(5deg, transparentize($content_bg, 0), transparentize($content_bg, 1)); pointer-events: none; touch-action: none; + + @include tablet { + opacity: 0.5; + } } From 2858b70a8b4e38629bd0e135f9978cd69daa824d Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 18:32:04 +0700 Subject: [PATCH 19/20] fixed cell size on mobiles --- src/components/flow/CellShade/styles.module.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/flow/CellShade/styles.module.scss b/src/components/flow/CellShade/styles.module.scss index dfc82d72..ee6ddd13 100644 --- a/src/components/flow/CellShade/styles.module.scss +++ b/src/components/flow/CellShade/styles.module.scss @@ -6,12 +6,11 @@ left: 0; right: 0; top: 0; - max-height: $cell; background: linear-gradient(5deg, transparentize($content_bg, 0), transparentize($content_bg, 1)); pointer-events: none; touch-action: none; @include tablet { - opacity: 0.5; + opacity: 0.7; } } From a7e8e19b0649b4cad972cbf84d976663bf0e7fd3 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 5 Oct 2021 18:35:20 +0700 Subject: [PATCH 20/20] fixed default cell shades --- src/components/flow/CellShade/index.tsx | 2 +- src/components/flow/CellShade/styles.module.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/flow/CellShade/index.tsx b/src/components/flow/CellShade/index.tsx index e74cc4f6..7ba39f25 100644 --- a/src/components/flow/CellShade/index.tsx +++ b/src/components/flow/CellShade/index.tsx @@ -15,7 +15,7 @@ const CellShade: FC = ({ color, ...rest }) => { return undefined; } - return `linear-gradient(10deg, ${color} 30px, ${convertHexToRGBA(color, 0.3)} 200px)`; + return `linear-gradient(7deg, ${color} 50px, ${convertHexToRGBA(color, 0.3)} 250px)`; }, [color]); return ( diff --git a/src/components/flow/CellShade/styles.module.scss b/src/components/flow/CellShade/styles.module.scss index ee6ddd13..ce9f167d 100644 --- a/src/components/flow/CellShade/styles.module.scss +++ b/src/components/flow/CellShade/styles.module.scss @@ -6,7 +6,7 @@ left: 0; right: 0; top: 0; - background: linear-gradient(5deg, transparentize($content_bg, 0), transparentize($content_bg, 1)); + background: linear-gradient(7deg, transparentize($content_bg, 0.05) 30px, transparentize($content_bg, 1) 250px); pointer-events: none; touch-action: none;