1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

Merge branch 'feature/8-image-preloader' into develop

This commit is contained in:
Fedor Katurov 2020-11-06 22:17:34 +07:00
commit 2c1adc384e
232 changed files with 342 additions and 278 deletions

View file

@ -56,6 +56,7 @@
"license": "MIT",
"dependencies": {
"@hot-loader/react-dom": "^16.10.2",
"@popperjs/core": "^2.5.4",
"@typescript-eslint/eslint-plugin": "^1.13.0",
"@typescript-eslint/parser": "^1.13.0",
"autosize": "^4.0.2",
@ -95,6 +96,7 @@
"react-dom": "^16.13.0",
"react-hot-loader": "^4.12.15",
"react-packery-component": "^1.0.2",
"react-popper": "^2.2.3",
"react-redux": "^6.0.1",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",

View file

@ -1,7 +1,6 @@
import React, { FC, useCallback, useState, useEffect } from 'react';
import * as styles from './styles.scss';
import React, { FC, useCallback, useEffect, useState } from 'react';
import styles from './styles.module.scss';
import { Icon } from '~/components/input/Icon';
import { Filler } from '~/components/containers/Filler';
import { PLAYER_STATES } from '~/redux/player/constants';
import { connect } from 'react-redux';
import pick from 'ramda/es/pick';

View file

@ -4,7 +4,7 @@ import { Icon } from '~/components/input/Icon';
import * as NODE_ACTIONS from '~/redux/node/actions';
import { DIALOGS } from '~/redux/modal/constants';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { NODE_TYPES } from '~/redux/node/constants';
const mapStateToProps = null;

View file

@ -1,6 +1,6 @@
import React, { FC, memo, useMemo, useEffect } from 'react';
import { ICommentBlockProps } from '~/constants/comment';
import styles from './styles.scss';
import styles from './styles.module.scss';
import { getYoutubeThumb } from '~/utils/dom';
import { selectPlayer } from '~/redux/player/selectors';
import { connect } from 'react-redux';

View file

@ -1,6 +1,6 @@
import React, { FC } from 'react';
import { ICommentBlockProps } from '~/constants/comment';
import styles from './styles.scss';
import styles from './styles.module.scss';
interface IProps extends ICommentBlockProps {}

View file

@ -1,5 +1,5 @@
import React, { AllHTMLAttributes, FC } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import classNames from 'classnames';
type IProps = AllHTMLAttributes<HTMLDivElement> & { is_blurred: boolean };

View file

@ -1,5 +1,5 @@
import React, { FC } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import classNames from 'classnames';

View file

@ -1,5 +1,5 @@
import React, { FC, HTMLAttributes } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import classNames = require('classnames');

View file

@ -1,7 +1,7 @@
import React, { FC, HTMLAttributes } from 'react';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { Card } from '../Card';
import { IUser } from '~/redux/auth/types';
import { getURL } from '~/utils/dom';

View file

@ -1,6 +1,6 @@
import React, { FC, useState, useCallback, useEffect, useRef } from "react";
import { IUser } from "~/redux/auth/types";
import styles from "./styles.scss";
import styles from './styles.module.scss';
import { getURL } from "~/utils/dom";
import { PRESETS } from "~/constants/urls";
import classNames from "classnames";

View file

@ -1,6 +1,6 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
type IProps = React.HTMLAttributes<HTMLDivElement>;

View file

@ -1,6 +1,6 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
type IProps = React.HTMLAttributes<HTMLDivElement> & {
horizontal?: boolean;

View file

@ -1,6 +1,6 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
type IProps = React.HTMLAttributes<HTMLDivElement> & {
horizontal?: boolean;

View file

@ -1,5 +1,5 @@
import React, { FC } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import classNames = require('classnames');

View file

@ -1,5 +1,5 @@
import React, { FC, memo } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { createPortal } from 'react-dom';
import { selectNode } from '~/redux/node/selectors';
import { connect } from 'react-redux';

View file

@ -1,5 +1,5 @@
import React, { FC, HTMLAttributes } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import classNames = require('classnames');

View file

@ -1,7 +1,7 @@
import React, { MouseEventHandler, useEffect, useState } from 'react';
import { Scrollbars } from 'tt-react-custom-scrollbars';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
interface IProps {
children: Element | React.ReactChild;

View file

@ -1,5 +1,5 @@
import React, { FC, ReactComponentElement, DetailsHTMLAttributes, useEffect, useRef } from 'react';
import styles from './styles.scss';
import styles from './styles.module.scss';
import StickySidebar from 'sticky-sidebar';
import classnames from 'classnames';
import ResizeSensor from 'resize-sensor';

View file

@ -1,5 +1,5 @@
import React, { FC, HTMLAttributes } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
type IProps = HTMLAttributes<HTMLDivElement> & {}

View file

@ -1,5 +1,5 @@
import React, { FC, ReactNode } from 'react';
import styles from './styles.scss';
import styles from './styles.module.scss';
interface IProps {
children: ReactNode;

View file

@ -7,7 +7,7 @@ import { AudioGrid } from '../AudioGrid';
import { selectUploads } from '~/redux/uploads/selectors';
import * as UPLOAD_ACTIONS from '~/redux/uploads/actions';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
const mapStateToProps = selectUploads;
const mapDispatchToProps = {

View file

@ -5,7 +5,7 @@ import { IUploadStatus } from '~/redux/uploads/reducer';
import { moveArrItem } from '~/utils/fn';
import { SortableAudioGrid } from '~/components/editors/SortableAudioGrid';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
interface IProps {
files: IFile[];

View file

@ -1,5 +1,5 @@
import React, { FC, createElement } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { INode } from '~/redux/types';
import { NODE_PANEL_COMPONENTS } from '~/redux/node/constants';

View file

@ -1,5 +1,5 @@
import React, { FC, useCallback, useEffect } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { Icon } from '~/components/input/Icon';
import { IFileWithUUID, INode, IFile } from '~/redux/types';
import uuid from 'uuid4';

View file

@ -1,7 +1,7 @@
import React, { FC, useCallback, useEffect, useState } from 'react';
import { IFileWithUUID } from '~/redux/types';
import uuid from 'uuid4';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { UPLOAD_SUBJECTS, UPLOAD_TARGETS, UPLOAD_TYPES } from '~/redux/uploads/constants';
import path from 'ramda/es/path';
import { connect } from 'react-redux';

View file

@ -4,7 +4,7 @@ import { INode, IFile } from '~/redux/types';
import * as UPLOAD_ACTIONS from '~/redux/uploads/actions';
import { selectUploads } from '~/redux/uploads/selectors';
import { ImageGrid } from '~/components/editors/ImageGrid';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
const mapStateToProps = selectUploads;
const mapDispatchToProps = {

View file

@ -1,6 +1,6 @@
import React, { FC, useCallback } from 'react';
import { SortEnd } from 'react-sortable-hoc';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { IFile } from '~/redux/types';
import { IUploadStatus } from '~/redux/uploads/reducer';
import { moveArrItem } from '~/utils/fn';

View file

@ -1,7 +1,7 @@
import React from 'react';
import { SortableContainer } from 'react-sortable-hoc';
import { AudioUpload } from '~/components/upload/AudioUpload';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { SortableAudioGridItem } from '~/components/editors/SortableAudioGridItem';
import { IFile } from '~/redux/types';
import { IUploadStatus } from '~/redux/uploads/reducer';

View file

@ -1,7 +1,7 @@
import React from 'react';
import { SortableElement } from 'react-sortable-hoc';
import styles from './styles.scss';
import styles from './styles.module.scss';
const SortableAudioGridItem = SortableElement(({ children }) => (
<div className={styles.item}>{children}</div>

View file

@ -1,7 +1,7 @@
import React from 'react';
import { SortableContainer } from 'react-sortable-hoc';
import { ImageUpload } from '~/components/upload/ImageUpload';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { SortableImageGridItem } from '~/components/editors/SortableImageGridItem';
import { IFile } from '~/redux/types';
import { IUploadStatus } from '~/redux/uploads/reducer';

View file

@ -1,7 +1,7 @@
import React from 'react';
import { SortableElement } from 'react-sortable-hoc';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
const SortableImageGridItem = SortableElement(({ children }) => (
<div className={styles.item}>{children}</div>

View file

@ -1,6 +1,6 @@
import React, { FC, useCallback } from 'react';
import { INode } from '~/redux/types';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { Textarea } from '~/components/input/Textarea';
import path from 'ramda/es/path';

View file

@ -1,6 +1,6 @@
import React, { FC, useCallback, useMemo } from 'react';
import { INode } from '~/redux/types';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import path from 'ramda/es/path';
import { InputText } from '~/components/input/InputText';
import classnames from 'classnames';

View file

@ -3,7 +3,7 @@ import { INode } from '~/redux/types';
import { formatCellText, getURL } from '~/utils/dom';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { Icon } from '~/components/input/Icon';
import { flowSetCellView } from '~/redux/flow/actions';
import { PRESETS } from '~/constants/urls';

View file

@ -2,116 +2,94 @@ import React, { FC, useState, useCallback, useEffect, useRef, useMemo } from 're
import { IFlowState } from '~/redux/flow/reducer';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { getURL } from '~/utils/dom';
import { withRouter, RouteComponentProps } from 'react-router';
import { withRouter, RouteComponentProps, useHistory } from 'react-router';
import { URLS, PRESETS } from '~/constants/urls';
import { Icon } from '~/components/input/Icon';
import { INode } from "~/redux/types";
type IProps = RouteComponentProps & {
heroes: IFlowState['heroes'];
};
const FlowHeroUnconnected: FC<IProps> = ({ heroes, history }) => {
const [limit, setLimit] = useState(Math.min(heroes.length, 6));
const FlowHeroUnconnected: FC<IProps> = ({ heroes }) => {
const preset = useMemo(() => (window.innerWidth <= 768 ? PRESETS.cover : PRESETS.small_hero), []);
const [limit, setLimit] = useState(6);
const [current, setCurrent] = useState(0);
const [loaded, setLoaded] = useState([]);
const [loaded, setLoaded] = useState<Partial<INode>[]>([]);
const timer = useRef(null)
const history = useHistory();
const timer = useRef(null);
const onLoad = useCallback(id => () => setLoaded([...loaded, id]), [setLoaded, loaded]);
const onNext = useCallback(() => {
clearTimeout(timer.current);
const onLoad = useCallback((i: number) => {
setLoaded([...loaded, heroes[i]])
}, [heroes, loaded, setLoaded])
if (loaded.length <= 1) return;
const index = loaded.findIndex(el => el === current);
setCurrent(index > loaded.length - 2 ? loaded[0] : loaded[index + 1]);
}, [loaded, current, setCurrent, timer]);
const onNextPress = useCallback(() => {
setLimit(Math.min(heroes.length, limit + 1));
onNext();
}, [onNext, heroes, limit, setLimit]);
const onPrevious = useCallback(() => {
clearTimeout(timer.current);
if (loaded.length <= 1) return;
const index = loaded.findIndex(el => el === current);
setCurrent(index > 0 ? loaded[index - 1] : loaded[loaded.length - 1]);
}, [loaded, current, setCurrent, timer]);
useEffect(() => {
timer.current = setTimeout(onNext, 5000);
}, [current, onNext]);
useEffect(() => {
if (current === 0 && loaded.length > 0) setCurrent(loaded[0]);
}, [loaded]);
useEffect(() => {
setLimit(limit > 0 ? Math.min(heroes.length, limit) : heroes.length);
}, [heroes, limit]);
const stopSliding = useCallback(() => {
clearTimeout(timer.current);
timer.current = setTimeout(onNext, 5000);
}, [timer, onNext]);
const onClick = useCallback(() => {
if (!current) return;
history.push(URLS.NODE_URL(current));
}, [current]);
const items = Math.min(heroes.length, limit)
const title = useMemo(() => {
if (loaded.length === 0) return null;
const item = heroes.find(hero => hero.id === current);
if (!item || !item.title) return null;
return item.title;
return loaded[current]?.title || '';
}, [loaded, current, heroes]);
const preset = useMemo(() => (window.innerWidth <= 768 ? PRESETS.cover : PRESETS.small_hero), []);
const onNext = useCallback(() => {
if (heroes.length > limit) setLimit(limit + 1)
setCurrent(current < items - 1 ? current + 1 : 0)
}, [current, items, limit, heroes.length])
const onPrev = useCallback(() => setCurrent(current > 0 ? current - 1 : items - 1), [current, items])
const goToNode = useCallback(() => {
history.push(URLS.NODE_URL(loaded[current].id))
}, [current, loaded]);
useEffect(() => {
timer.current = setTimeout(onNext, 5000)
return () => clearTimeout(timer.current)
}, [current, timer.current])
useEffect(() => {
if (loaded.length === 1) onNext()
}, [loaded])
return (
<div className={styles.wrap} onMouseOver={stopSliding} onFocus={stopSliding}>
{loaded && loaded.length > 0 && (
<div className={styles.wrap}>
<div className={styles.loaders}>
{
heroes.slice(0, items).map((hero, i) => (
<img src={getURL({ url: hero.thumbnail }, preset)} key={hero.id} onLoad={() => onLoad(i)} />
))
}
</div>
{loaded.length > 0 && (
<div className={styles.info}>
<div className={styles.title_wrap}>{title}</div>
<div className={styles.buttons}>
<div className={styles.button} onClick={onPrevious}>
<div className={styles.button} onClick={onPrev}>
<Icon icon="left" />
</div>
<div className={styles.button} onClick={onNextPress}>
<div className={styles.button} onClick={onNext}>
<Icon icon="right" />
</div>
</div>
</div>
)}
{heroes.slice(0, limit).map(hero => (
{loaded.slice(0, limit).map((hero, index) => (
<div
className={classNames(styles.hero, {
[styles.is_visible]: loaded.includes(hero.id),
[styles.is_active]: current === hero.id,
[styles.is_visible]: true,
[styles.is_active]: current === index,
})}
style={{
backgroundImage: `url("${getURL({ url: hero.thumbnail }, preset)}")`,
}}
key={hero.id}
onClick={onClick}
onClick={goToNode}
>
<img
src={getURL({ url: hero.thumbnail }, preset)}
alt={hero.thumbnail}
onLoad={onLoad(hero.id)}
/>
</div>
))}

View file

@ -109,25 +109,11 @@
}
}
// .title {
// flex: 0;
// height: 48px;
// display: flex;
// align-items: center;
// justify-content: center;
// padding: 0 $gap 0 0;
// background: red;
// border-radius: $radius;
// font: $font_hero_title;
// text-transform: uppercase;
// }
.buttons {
display: flex;
align-items: center;
justify-content: center;
height: 48px;
// background: rgba(0, 0, 0, 0.7);
flex-direction: row;
width: 96px;
border-radius: $radius;
@ -145,3 +131,18 @@
}
}
}
.loaders {
position: absolute;
top: 0;
left: 0;
opacity: 0;
pointer-events: none;
touch-action: none;
img {
position: absolute;
left: 0;
top: 0;
}
}

View file

@ -1,6 +1,6 @@
import React, { FC } from 'react';
import { INode } from '~/redux/types';
import styles from './styles.scss';
import styles from './styles.module.scss';
import { URLS } from '~/constants/urls';
import { NodeRelatedItem } from '~/components/node/NodeRelatedItem';
import { getPrettyDate } from '~/utils/dom';

View file

@ -1,5 +1,5 @@
import React, { FC, useCallback } from 'react';
import styles from './styles.scss';
import styles from './styles.module.scss';
import { IFlowState } from '~/redux/flow/reducer';
import { LoaderCircle } from '~/components/input/LoaderCircle';
import { FlowRecentItem } from '../FlowRecentItem';

View file

@ -4,7 +4,7 @@ import { InputText } from '~/components/input/InputText';
import { FlowRecent } from '../FlowRecent';
import classnames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import * as FLOW_ACTIONS from '~/redux/flow/actions';
import { FlowSearchResults } from '../FlowSearchResults';
import { Icon } from '~/components/input/Icon';

View file

@ -1,5 +1,5 @@
import React, { FC } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { describeArc } from '~/utils/dom';
interface IProps {

View file

@ -1,6 +1,6 @@
import classnames from 'classnames';
import React, { ButtonHTMLAttributes, DetailedHTMLProps, FC, createElement, memo } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { Icon } from '~/components/input/Icon';
import { IIcon } from '~/redux/types';

View file

@ -1,5 +1,5 @@
import React, { HTMLAttributes } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
type IProps = HTMLAttributes<HTMLDivElement> & {};

View file

@ -1,6 +1,6 @@
import React, { FC, ChangeEvent, useCallback, useState, useEffect, LegacyRef } from 'react';
import classNames from 'classnames';
import * as styles from '~/styles/inputs.scss';
import * as styles from '~/styles/common/inputs.module.scss';
import { Icon } from '~/components/input/Icon';
import { IInputTextProps } from '~/redux/types';
import { LoaderCircle } from '~/components/input/LoaderCircle';

View file

@ -1,5 +1,5 @@
import React, { FC } from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { describeArc } from '~/utils/dom';
import classNames from 'classnames';

View file

@ -1,5 +1,5 @@
import * as React from 'react';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
interface ITextInputProps {
type?: 'text' | 'password';

View file

@ -11,7 +11,7 @@ import React, {
import classNames from 'classnames';
import autosize from 'autosize';
import * as styles from '~/styles/inputs.scss';
import * as styles from '~/styles/common/inputs.module.scss';
import { Icon } from '../Icon';
type IProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {

View file

@ -1,5 +1,5 @@
import React, { FC, memo } from 'react';
import styles from './styles.scss';
import styles from './styles.module.scss';
interface IProps {}

View file

@ -1,6 +1,6 @@
.wrap {
height: $header_height;
z-index: 5;
z-index: 25;
position: fixed;
top: 0;
left: 0;

View file

@ -1,6 +1,6 @@
import React, { FC, useMemo, useState, useCallback, useEffect } from 'react';
import { Icon } from '~/components/input/Icon';
import styles from './styles.scss';
import styles from './styles.module.scss';
import { connect } from 'react-redux';
import { selectAuthUpdates, selectAuthUser } from '~/redux/auth/selectors';
import pick from 'ramda/es/pick';

View file

@ -2,7 +2,7 @@ import React, {
FC, LegacyRef, ReactChild, useCallback, useEffect, useState
} from 'react';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { Group } from '~/components/containers/Group';
interface IProps {

View file

@ -1,6 +1,6 @@
import React, { FC, useCallback } from 'react';
import { Group } from '~/components/containers/Group';
import styles from './styles.scss';
import styles from './styles.module.scss';
import { getURL } from '~/utils/dom';
import { Icon } from '~/components/input/Icon';
import { IUser } from '~/redux/auth/types';

View file

@ -6,7 +6,7 @@ import { IFile } from '~/redux/types';
import { PLAYER_STATES } from '~/redux/player/constants';
import { Player, IPlayerProgress } from '~/utils/player';
import classNames from 'classnames';
import * as styles from './styles.scss';
import styles from './styles.module.scss';
import { Icon } from '~/components/input/Icon';
import { InputText } from '~/components/input/InputText';

Some files were not shown because too many files have changed in this diff Show more