Compare commits

...

5 Commits

16 changed files with 357 additions and 40 deletions

View File

@ -105,6 +105,11 @@ def users_username(username):
if request.method == 'GET': return util.jsonify(users.get(util.get_user(required=False), username))
if request.method == 'PUT': return util.jsonify(users.update(util.get_user(), username, request.json))
@app.route('/users/<username>/tours/<tour>', methods=['PUT'])
def users_tour(username, tour):
status = request.args.get('status', 'completed')
return util.jsonify(users.finish_tour(util.get_user(), username, tour, status))
@app.route('/users/me/projects', methods=['GET'])
def me_projects_route():
user = util.get_user()

View File

@ -13,7 +13,8 @@ def me(user):
'avatarUrl': user.get('avatar') and uploads.get_presigned_url('users/{0}/{1}'.format(user['_id'], user['avatar'])),
'roles': user.get('roles', []),
'groups': user.get('groups', []),
'subscriptions': user.get('subscriptions')
'subscriptions': user.get('subscriptions'),
'finishedTours': user.get('completedTours', []) + user.get('skippedTours', []),
}
def get(user, username):
@ -53,6 +54,14 @@ def update(user, username, data):
db.users.update({'username': username}, updater)
return get(user, data.get('username', username))
def finish_tour(user, username, tour, status):
db = database.get_db()
if user['username'] != username:
raise util.errors.Forbidden('Not allowed')
key = 'completedTours' if status == 'completed' else 'skippedTours'
db.users.update_one({'_id': user['_id']}, {'$addToSet': {key: tour}})
return {'finishedTour': tour}
def get_projects(user, id):
db = database.get_db()
u = db.users.find_one(id, {'username': 1, 'avatar': 1})

View File

@ -17,6 +17,7 @@
"react-confirm": "^0.1.18",
"react-dom": "^16.13.1",
"react-helmet": "^6.0.0",
"react-joyride": "^2.4.0",
"react-redux": "^7.2.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1",

View File

@ -12,6 +12,9 @@ export const users = {
update(username, data, success, fail) {
api.authenticatedRequest('PUT', `/users/${username}`, data, success, fail);
},
finishTour(username, tour, status, success, fail) {
api.authenticatedRequest('PUT', `/users/${username}/tours/${tour}?status=${status}`, null, success, fail)
},
getMyProjects(success, fail) {
store.dispatch(actions.projects.request());
api.authenticatedRequest('GET', '/users/me/projects', null, d => success && success(d.projects), fail);

View File

@ -91,11 +91,16 @@ function App({ user, groups, syncedToDrift, driftReady, onOpenRegister, onCloseA
<Divider hidden section />
</div>
<div style={{ background: 'rgb(240,240,240)', padding: '15px 0px' }}>
<div style={{ background: 'rgb(240,240,240)', padding: '30px 0px' }}>
<Container>
<Grid>
<Grid.Column computer={8}>
<Link to="/"><img alt="Treadl logo" src={logo} style={{ width: '100px', paddingTop: 20, paddingBottom: 20 }} /></Link>
<p style={{color: '#888888'}}>&copy; Treadl 2022</p>
<p style={{marginTop: 10}}><small>Treadl software is free and open-source. Contributions to the project are always welcome.
<br />
<Icon name="code" /> <a href="https://git.wilw.dev/seastorm/treadl" target="_blank" rel="noopener noreferrer">Project source homepage</a>
</small></p>
</Grid.Column>
<Grid.Column computer={8} textAlign="right">
<div style={{ paddingTop: 40 }}>
@ -107,12 +112,7 @@ function App({ user, groups, syncedToDrift, driftReady, onOpenRegister, onCloseA
<Icon name='book' />
<a href='https://git.wilw.dev/seastorm/treadl/wiki' target='_blank' rel='noopener noreferrer'>Documentation</a>
</p>
<Divider />
<p><small>Treadl software is free and open-source.<br />Contributions to the project are always welcome.</small></p>
<p>
<Icon name="code" />
<a href="https://git.wilw.dev/seastorm/treadl" target="_blank" rel="noopener noreferrer">View source code</a>
</p>
<Divider />
<p>
<Icon name="file alternate outline" />

View File

@ -14,11 +14,11 @@ const LinkContainer = styled.span`
}
`;
function HelpLink({ text, link, marginTop, marginLeft, marginBottom, style }) {
function HelpLink({ className, text, link, marginTop, marginLeft, marginBottom, style }) {
if (!link) return null;
return (
<LinkContainer style={style} marginTop={marginTop} marginLeft={marginLeft} marginBottom={marginBottom}>
<a href={link} target='_blank' rel='noopener noreferrer'>
<a className={className} href={link} target='_blank' rel='noopener noreferrer'>
<span className='emoji'>🪧</span>
{text || 'Get help with this page'}
</a>

View File

@ -101,8 +101,8 @@ function NavBar({ user, groups, onOpenLogin, onOpenRegister, isAuthenticated, on
<div className='nav-links'>
<Popup basic on='focus' open={searchPopupOpen} onOpen={e => openSearchPopup(true)} onClose={e => openSearchPopup(false)}
trigger={<SearchBar><input placeholder='Click to search...' value={searchTerm} onChange={e => updateSearchTerm(e.target.value)} onKeyDown={e => e.keyCode === 13 && search()} /></SearchBar>}
content={<div style={{width: 300}}>
{!searchResults?.users && !searchResults?.groups ?
content={<div style={{width: 300}} className='joyride-search'>
{!searchResults?.users && !searchResults?.groups ?
<small>
{searching
? <span><Loader size='tiny' inline active style={{marginRight: 10}}/> Searching...</span>

View File

@ -0,0 +1,202 @@
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Joyride from 'react-joyride';
import api from '../../api';
import actions from '../../actions';
const tours = {
home: [{
disableBeacon: true,
disableOverlay: false,
placement: 'center',
target: 'body',
title: 'Welcome to Treadl!',
content: (<div>
<p><strong>Thanks for signing-up 😀. We'd love to quickly show you around your homepage.</strong></p>
<p>You can skip this tour if you just want to get on with things.</p>
</div>)
},
{
target: '.joyride-projects',
title: 'Projects',
content: (<div>
<p><strong>Treadl contents (patterns, images, files, and more) are stored in projects</strong></p>
<p>Your projects will appear in this area. You can think of them like folders on your computer.</p>
</div>)
},
{
target: '.joyride-createProject',
title: 'Create a new project',
content: (<div>
<p>Use this button to create a new project. Projects can be public to the community or kept private.</p>
</div>)
},
{
target: '.joyride-groups',
title: 'Groups',
content: (<div>
<p><strong>Treadl groups</strong></p>
<p>Your group memberships will show here. Groups allow you to talk and share content with other people on Treadl.</p>
</div>)
},
{
target: '.joyride-createGroup',
title: 'Creating groups',
content: (<div>
<p><strong>You can create your own groups to build a community</strong></p>
<p>People use groups for weaving classes, organisation, community groups, and more.</p>
</div>)
},
{
target: '.joyride-help',
title: 'One last thing...',
content: (<div>
<p><strong>Help and support</strong></p>
<p>These types of links point to places to get help and support, so please use them if you get stuck!</p>
</div>)
},
],
pattern: [
{
disableBeacon: true,
disableOverlay: false,
placement: 'center',
target: 'body',
title: 'Welcome to the Treadl pattern editor!',
content: (<div>
<p><strong>The editor can look a bit daunting at first, and so we'd like to briefly show you how it works.</strong></p>
<p>If you already know what you're doing, then feel free to skip this.</p>
</div>)
},
{
target: '.joyride-drawdown',
placement: 'top',
title: 'The drawdown',
content: (<div>
<p><strong>This is where your pattern is displayed. It updates in real-time.</strong></p>
<p>You need to have threads in both your warp and weft, as well as tieups specified, in order for a pattern to show.</p>
</div>)
},
{
target: '.joyride-warp',
title: 'The warp',
content: (<div>
<p><strong>Add threads to your warp by selecting a tool (from the sidebar) and clicking and dragging your mouse over this area.</strong></p>
<p>Different tools produce different thread patterns.</p>
</div>)
},
{
target: '.joyride-warpColourway',
title: 'The warp colourway',
content: (<div>
<p><strong>Add colours to your threads</strong></p>
<p>Select the colour tool (from the sidebar) and drag over your warp to change the thread colours.</p>
</div>)
},
{
target: '.joyride-weft',
title: 'The weft',
content: (<div>
<p><strong>The weft works in the same way as the warp</strong></p>
<p>Drag drawing tools and colour tools over this area to change the thread patterns and colours.</p>
</div>)
},
{
target: '.joyride-tieups',
title: 'The tieups area',
content: (<div>
<p><strong>The tieups determine how your warp and weft threads will be linked</strong></p>
<p>Select individual tieups by clicking the squares relevant to your pattern.</p>
</div>)
},
{
target: '.joyride-threads',
title: 'Shafts and treadles',
content: (<div>
<p><strong>You may need to update the shafts and treadles used by your pattern so that it can be used with your loom</strong></p>
<p>Changing these values will update the available threads in your warp and weft.</p>
</div>)
},
{
target: '.joyride-pan',
title: 'Panning tool',
content: (<div>
<p><strong>Select this and click-and-drag over your drawdown to move it around</strong></p>
</div>)
},
{
target: '.joyride-colour',
title: 'Colour tool',
content: (<div>
<p><strong>Select this tool and click-and-drag over your warp and weft to add colours to your threads</strong></p>
</div>)
},
{
target: '.joyride-straight',
title: 'Straight draw tool',
placement: 'left',
content: (<div>
<p><strong>Select this tool and click-and-drag over your warp and weft to add threads in a straight pattern</strong></p>
</div>)
},
{
target: '.joyride-point',
title: 'Point draw tool',
placement: 'left',
content: (<div>
<p><strong>Select this tool and click-and-drag over your warp and weft to add threads in a point pattern</strong></p>
</div>)
},
{
target: '.joyride-tools',
title: 'Rich tooling',
placement: 'left',
content: (<div>
<p><strong>Expand the sections in the toolbox to see what else is possible</strong></p>
<p>For example, you can change your pattern's zoom level and view different types of interlacements.</p>
</div>)
},
{
target: '.joyride-help',
title: 'Help is available',
content: (<div>
<p><strong>Click this link to view documentation about the editor</strong></p>
<p>And you can always reach out to us directly if you're stuck or have questions.</p>
</div>)
},
],
};
function Tour({ id, run }) {
const dispatch = useDispatch();
const { user } = useSelector(state => {
const user = state.users.users.filter(u => state.auth.currentUserId === u._id)[0];
return { user };
});
if (!user || user.finishedTours?.indexOf(id) > -1) return null;
const cb = event => {
if (event.type === 'tour:end') {
const status = event.status == 'skipped' ? 'skipped' : 'completed';
api.users.finishTour(user.username, id, status);
const finishedTours = user.finishedTours;
finishedTours.push(id);
dispatch(actions.users.update(id, { finishedTours }));
}
};
return (
<Joyride steps={tours[id]} run={run} continuous={true} scrollToFirstStep={true} showSkipButton={true} disableCloseOnEsc={true} locale={{last: 'Finish'}}
styles={{
options: {
primaryColor: '#ed0176'
}
}}
callback={cb}
/>
);
}
export default Tour;

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useState, useEffect } from 'react';
import { Helmet } from 'react-helmet';
import { Loader, Divider, Button, Message, Container, Segment, Grid, Card, Icon, List } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
@ -11,8 +11,10 @@ import utils from 'utils/utils.js';
import UserChip from 'components/includes/UserChip';
import HelpLink from 'components/includes/HelpLink';
import ProjectCard from 'components/includes/ProjectCard';
import Tour from 'components/includes/Tour';
function Home({ user, groups, projects, invitations, loadingProjects, onReceiveProjects, onReceiveInvitations, onDismissInvitation, onReceiveGroup, onJoinGroup }) {
const [runJoyride, setRunJoyride] = useState(false);
useEffect(() => {
api.invitations.get(({ invitations, sentInvitations}) => {
@ -21,6 +23,8 @@ function Home({ user, groups, projects, invitations, loadingProjects, onReceiveP
}, [onReceiveInvitations]);
useEffect(() => {
api.users.getMyProjects(onReceiveProjects);
setTimeout(() =>
setRunJoyride(true), 2000);
}, [onReceiveProjects]);
const declineInvite = (invite) => {
@ -45,6 +49,8 @@ function Home({ user, groups, projects, invitations, loadingProjects, onReceiveP
return (
<Container style={{ marginTop: '40px' }}>
<Tour id='home' run={runJoyride} />
<Helmet title='Dashboard' />
<Grid stackable>
<Grid.Column computer={5}>
@ -88,7 +94,7 @@ function Home({ user, groups, projects, invitations, loadingProjects, onReceiveP
</Card>
{(groups && groups.length) ?
<Card fluid>
<Card fluid className='joyride-groups'>
<Card.Content>
<Card.Header>Your groups</Card.Header>
@ -103,7 +109,7 @@ function Home({ user, groups, projects, invitations, loadingProjects, onReceiveP
</List.Item>
)}
</List>
<Button fluid size='small' icon='plus' content='Create a new group' as={Link} to='/groups/new' />
<Button className='joyride-createGroup' fluid size='small' icon='plus' content='Create a new group' as={Link} to='/groups/new' />
<HelpLink link='https://git.wilw.dev/seastorm/treadl/wiki/Groups' text='Learn more about groups' marginTop/>
</Card.Content>
</Card>
@ -111,13 +117,13 @@ function Home({ user, groups, projects, invitations, loadingProjects, onReceiveP
<Message>
<Message.Header>Groups</Message.Header>
<p>Groups enable you to build communities of weavers and makers with similar interests. Create one for your weaving group or class today.</p>
<Button as={Link} to='/groups/new' size='small' color='purple' icon='plus' content='Create a group' />
<Button className='joyride-createGroup' as={Link} to='/groups/new' size='small' color='purple' icon='plus' content='Create a group' />
</Message>
}
</Grid.Column>
<Grid.Column computer={11}>
<Grid.Column computer={11} className='joyride-projects'>
{loadingProjects && !projects.length &&
<div style={{textAlign: 'center'}}>
<h4>Loading your projects...</h4>
@ -136,8 +142,8 @@ function Home({ user, groups, projects, invitations, loadingProjects, onReceiveP
<p>Projects can contain anything - from rough ideas or design experiments through to commissions and exhibitions. Treat them as if they were just weaving-related <span role="img" aria-label="folder">📁</span> folders on your computer.</p>
<Divider />
<h4>Start by creating a new project. Don't worry, you can keep it private.</h4>
<p><HelpLink link='https://git.wilw.dev/seastorm/treadl/wiki/Projects' text='Learn more about projects' marginTop/></p>
<Button as={Link} to="/projects/new" color="teal" icon="plus" content="Create a project" />
<p><HelpLink className='joyride-help' link='https://git.wilw.dev/seastorm/treadl/wiki/Projects' text='Learn more about projects' marginTop/></p>
<Button className='joyride-createProject' as={Link} to="/projects/new" color="teal" icon="plus" content="Create a project" />
</Segment>
</div>
@ -145,10 +151,10 @@ function Home({ user, groups, projects, invitations, loadingProjects, onReceiveP
{projects && projects.length > 0 &&
<div>
<Button as={Link} to="/projects/new" color='teal' content='Create a project' icon='plus' floated='right'/>
<Button className='joyride-createProject' as={Link} to="/projects/new" color='teal' content='Create a project' icon='plus' floated='right'/>
<h2><Icon name='book' /> Your projects</h2>
<p>Projects contain the patterns and files that make up your creations.
<HelpLink link='https://git.wilw.dev/seastorm/treadl/wiki/Projects' text='Learn more about projects' marginLeft/>
<HelpLink className='joyride-help' link='https://git.wilw.dev/seastorm/treadl/wiki/Projects' text='Learn more about projects' marginLeft/>
</p>
<Divider clearing hidden />
<Card.Group itemsPerRow={2} stackable>

View File

@ -7,6 +7,7 @@ import { toast } from 'react-toastify';
import styled from 'styled-components';
import ElementPan from 'components/includes/ElementPan';
import HelpLink from 'components/includes/HelpLink';
import Tour from 'components/includes/Tour';
import Warp from './Warp.js';
import Weft from './Weft.js';
@ -76,6 +77,7 @@ class Draft extends Component {
when={unsaved ? true : false}
message='You have unsaved changes. Are you sure you want to leave tnis page?'
/>
<Tour id='pattern' run={true} />
<div style={{display: 'flex'}}>
<div style={{flex: 1, overflow: 'hidden'}}>
@ -101,7 +103,7 @@ class Draft extends Component {
</div>
<div style={{width: 300, marginLeft: 20}}>
<HelpLink link='https://git.wilw.dev/seastorm/treadl/wiki/Editing-patterns#using-the-pattern-editor' marginBottom/>
<HelpLink className='joyride-help' link='https://git.wilw.dev/seastorm/treadl/wiki/Editing-patterns#using-the-pattern-editor' marginBottom/>
<Tools warp={warp} weft={weft} object={this.state} pattern={this.state.pattern} updateObject={this.updateObject} updatePattern={this.updatePattern} saveObject={this.saveObject} baseSize={baseSize} unsaved={unsaved} saving={saving}/>
</div>

View File

@ -105,7 +105,7 @@ class Drawdown extends Component {
render() {
const { warp, weft, baseSize } = this.props;
return (
<StyledDrawdown ref="drawdown" className="drawdown"
<StyledDrawdown ref="drawdown" className="drawdown joyride-drawdown"
width={warp.threads * baseSize}
height={weft.threads * baseSize}
weft={weft} warp={warp} baseSize={baseSize}

View File

@ -15,7 +15,7 @@ class Tieups extends Component {
componentDidMount() {
this.paintTieups();
}
fillUpTo = (tieups, limit) => {
let i = tieups.length;
while (i <= limit) {
@ -71,7 +71,7 @@ class Tieups extends Component {
render() {
const { warp, weft, baseSize } = this.props;
return (
<StyledTieups ref='tieups' className='tieups' width={weft.treadles * baseSize} height= {warp.shafts * baseSize} style={{width: weft.treadles * baseSize, height: warp.shafts * baseSize}} onClick={this.click}/>
<StyledTieups ref='tieups' className='tieups joyride-tieups' width={weft.treadles * baseSize} height= {warp.shafts * baseSize} style={{width: weft.treadles * baseSize, height: warp.shafts * baseSize}} onClick={this.click}/>
);
}
}

View File

@ -137,7 +137,7 @@ class Tools extends Component {
render() {
const { warp, weft, editor, unsaved, saving } = this.props;
return (
<div className="pattern-toolbox">
<div className="pattern-toolbox joyride-tools">
{unsaved &&
<Segment attached="top">
<Button fluid color="teal" icon="save" content="Save pattern" onClick={() => this.props.saveObject(this.refs.canvas)} loading={saving}/>
@ -176,7 +176,7 @@ class Tools extends Component {
<small>Name</small>
<Input type="text" size="small" fluid style={{ marginBottom: '5px' }} value={this.props.object.name} onChange={this.setName} />
<Grid columns={2}>
<Grid.Row>
<Grid.Row className='joyride-threads'>
<Grid.Column>
<small>Shafts</small>
<Input fluid type="number" value={warp.shafts} onKeyDown={e => false} onChange={this.setShafts} size="mini" />
@ -189,13 +189,13 @@ class Tools extends Component {
<Grid.Row style={{paddingTop: 0}}>
<Grid.Column>
<small>Width</small>
<Input fluid readOnly value={warp.threading?.length || 0} size="mini"
<Input fluid readOnly value={warp.threading?.length || 0} size="mini"
action={{icon: 'edit', onClick: this.changeWidth}}
/>
</Grid.Column>
<Grid.Column>
<small>Height</small>
<Input fluid readOnly value={weft.treadling?.length || 0} size="mini"
<Input fluid readOnly value={weft.treadling?.length || 0} size="mini"
action={{icon: 'edit', onClick: this.changeHeight}}
/>
</Grid.Column>
@ -208,10 +208,10 @@ class Tools extends Component {
</Accordion.Title>
<Accordion.Content active={this.drawerIsActive('drawing')}>
<Button.Group fluid>
<Button data-tooltip="Pan (drag to move) pattern" color={this.props.editor.tool === 'pan' && 'blue'} size="tiny" icon onClick={() => this.enableTool('pan')}><Icon name="move" /></Button>
<Button data-tooltip="Paint selected colour" color={this.props.editor.tool === 'colour' && 'blue'} size="tiny" icon onClick={() => this.enableTool('colour')}><Icon name="paint brush" /></Button>
<Button data-tooltip="Straight draw" color={this.props.editor.tool === 'straight' && 'blue'} size="tiny" icon onClick={() => this.enableTool('straight')}>/ /</Button>
<Button data-tooltip="Point draw" color={this.props.editor.tool === 'point' && 'blue'} size="tiny" icon onClick={() => this.enableTool('point')}><Icon name="chevron up" /></Button>
<Button className='joyride-pan' data-tooltip="Pan (drag to move) pattern" color={this.props.editor.tool === 'pan' && 'blue'} size="tiny" icon onClick={() => this.enableTool('pan')}><Icon name="move" /></Button>
<Button className='joyride-colour' data-tooltip="Paint selected colour" color={this.props.editor.tool === 'colour' && 'blue'} size="tiny" icon onClick={() => this.enableTool('colour')}><Icon name="paint brush" /></Button>
<Button className='joyride-straight' data-tooltip="Straight draw" color={this.props.editor.tool === 'straight' && 'blue'} size="tiny" icon onClick={() => this.enableTool('straight')}>/ /</Button>
<Button className='joyride-point' data-tooltip="Point draw" color={this.props.editor.tool === 'point' && 'blue'} size="tiny" icon onClick={() => this.enableTool('point')}><Icon name="chevron up" /></Button>
</Button.Group>
</Accordion.Content>

View File

@ -191,13 +191,13 @@ class Warp extends Component {
}
}
render() {
const { warp, weft, baseSize } = this.props;
return (
<StyledWarp treadles={weft.treadles} shafts={warp.shafts} baseSize={baseSize}>
<canvas className='warp-colourway' ref="colourway" width={warp.threading.length * baseSize} height={10}
<canvas className='warp-colourway joyride-warpColourway' ref="colourway" width={warp.threading.length * baseSize} height={10}
style={{
position: 'absolute', top: 0, right: 0, height: 10, width: warp.threading.length * baseSize,
}}
@ -207,7 +207,7 @@ class Warp extends Component {
onMouseUp={this.mouseUpColourway}
onMouseLeave={this.mouseUpColourway}
/>
<canvas className='warp-threads' ref="warp" width={warp.threading.length * baseSize} height={warp.shafts * baseSize}
<canvas className='warp-threads joyride-warp' ref="warp" width={warp.threading.length * baseSize} height={warp.shafts * baseSize}
style={{
position: 'absolute', top: 10, right: 0,
height: warp.shafts * baseSize,

View File

@ -207,7 +207,7 @@ class Weft extends Component {
}
}
render() {
const { warp, weft, baseSize } = this.props;
@ -221,7 +221,7 @@ class Weft extends Component {
onMouseUp={this.mouseUpColourway}
onMouseLeave={this.mouseUpColourway}
/>
<canvas className='weft-threads' ref="weft" width={weft.treadles * baseSize} height={weft.threads * baseSize}
<canvas className='weft-threads joyride-weft' ref="weft" width={weft.treadles * baseSize} height={weft.threads * baseSize}
style={{
position: 'absolute',
top: 0, right: 10, height: weft.threads * baseSize, width: weft.treadles * baseSize,

View File

@ -1190,6 +1190,11 @@
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@gilbarbara/deep-equal@^0.1.0":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@gilbarbara/deep-equal/-/deep-equal-0.1.1.tgz#91c8f291da6bf7e21cdbfb585fb072d59963acf3"
integrity sha512-SjSBspHXlclODLtSoPIQwBhfeBjncC05NlNoFELJ6xZQkyYDJsVCcs7+f+etHR2cYPbHLjnh1C06lQlCbMEWEA==
"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@ -3719,6 +3724,11 @@ decode-uri-component@^0.2.0:
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
deep-diff@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-1.0.2.tgz#afd3d1f749115be965e89c63edc7abb1506b9c26"
integrity sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==
deep-equal@^1.0.1, deep-equal@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
@ -3736,6 +3746,11 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
default-gateway@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"
@ -5817,6 +5832,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
dependencies:
is-extglob "^2.1.1"
is-lite@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/is-lite/-/is-lite-0.8.1.tgz#a9bd03c90ea723d450c78c991b84f78e7e3126f9"
integrity sha512-ekSwuewzOmwFnzzAOWuA5fRFPqOeTrLIL3GWT7hdVVi+oLuD+Rau8gCmkb94vH5hjXc1Q/CfIW/y/td1RrNQIg==
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@ -7198,6 +7218,16 @@ neo-async@^2.5.0, neo-async@^2.6.1:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
nested-property@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/nested-property/-/nested-property-1.0.1.tgz#2001105b5c69413411b876bba9b86f4316af613f"
integrity sha512-BnBBoo/8bBNRdAnJc7+m79oWk7dXwW1+vCesaEQhfDGVwXGLMvmI4NwYgLTW94R/x+R2s/yr2g/hB/4w/YSAvA==
nested-property@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/nested-property/-/nested-property-4.0.0.tgz#a67b5a31991e701e03cdbaa6453bc5b1011bb88d"
integrity sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==
next-tick@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
@ -7926,7 +7956,7 @@ pnp-webpack-plugin@1.6.4:
dependencies:
ts-pnp "^1.1.6"
popper.js@^1.14.4:
popper.js@^1.14.4, popper.js@^1.16.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
@ -8983,6 +9013,18 @@ react-fast-compare@^3.1.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
react-floater@^0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/react-floater/-/react-floater-0.7.3.tgz#f57947960682586866ec21540e73c9049ca9f787"
integrity sha512-d1wAEph+xRxQ0RJ3woMmYLlZHTaCIsja7Bv6JNo2ezsVUgdMan4CxOR4Do4/xgpmRFfsQMdlygexLAZZypWirw==
dependencies:
deepmerge "^4.2.2"
exenv "^1.2.2"
is-lite "^0.8.1"
popper.js "^1.16.0"
react-proptype-conditional-require "^1.0.4"
tree-changes "^0.5.1"
react-helmet@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726"
@ -8993,11 +9035,27 @@ react-helmet@^6.0.0:
react-fast-compare "^3.1.1"
react-side-effect "^2.1.0"
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.6.3, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0:
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.6.3, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-joyride@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/react-joyride/-/react-joyride-2.4.0.tgz#273a99fea4804a48155e7cc7bae308dcbc8cb725"
integrity sha512-U0uDFspaAEZucsvYpEIEPnoWD0QwTFk06IgIlinmTDPHgoS+V0q16w1+JqHeEKkR8Q79DWEeBIJYMvCJ7jT2EQ==
dependencies:
deep-diff "^1.0.2"
deepmerge "^4.2.2"
exenv "^1.2.2"
is-lite "^0.8.1"
nested-property "^4.0.0"
react-floater "^0.7.3"
react-is "^16.13.1"
scroll "^3.0.1"
scrollparent "^2.0.1"
tree-changes "^0.9.0"
react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@ -9016,6 +9074,11 @@ react-popper@^1.3.4:
typed-styles "^0.0.7"
warning "^4.0.2"
react-proptype-conditional-require@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz#69c2d5741e6df5e08f230f36bbc2944ee1222555"
integrity sha1-acLVdB5t9eCPIw82u8KUTuEiJVU=
react-redux@^7.2.0:
version "7.2.1"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.1.tgz#8dedf784901014db2feca1ab633864dee68ad985"
@ -9670,6 +9733,16 @@ schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6
ajv "^6.12.2"
ajv-keywords "^3.4.1"
scroll@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/scroll/-/scroll-3.0.1.tgz#d5afb59fb3592ee3df31c89743e78b39e4cd8a26"
integrity sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==
scrollparent@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/scrollparent/-/scrollparent-2.0.1.tgz#715d5b9cc57760fb22bdccc3befb5bfe06b1a317"
integrity sha1-cV1bnMV3YPsivczDvvtb/gaxoxc=
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@ -10602,6 +10675,22 @@ tr46@^1.0.1:
dependencies:
punycode "^2.1.0"
tree-changes@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/tree-changes/-/tree-changes-0.5.1.tgz#e31cc8a0f56c8c401f0a88243d9165dbea4f570c"
integrity sha512-O873xzV2xRZ6N059Mn06QzmGKEE21LlvIPbsk2G+GS9ZX5OCur6PIwuuh0rWpAPvLWQZPj0XObyG27zZyLHUzw==
dependencies:
deep-diff "^1.0.2"
nested-property "1.0.1"
tree-changes@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/tree-changes/-/tree-changes-0.9.0.tgz#5dabc013f0f02d3f7f764596369adb35bf1b208a"
integrity sha512-k1lOWtcWfPzCyx45W3KsYkwNGf4DfnVb5VHV8/Shs8pr9bqhzjaIHgAnIyu9mydbYmluOVSgj2gdSOnIp+If3A==
dependencies:
"@gilbarbara/deep-equal" "^0.1.0"
is-lite "^0.8.1"
ts-pnp@1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a"