Compare commits

..

No commits in common. "30ebc7d22dfaf402ebd0486ce3cd507924b66900" and "4b656d31e1f0c1e5581e35b038c52926a121ce11" have entirely different histories.

15 changed files with 124 additions and 135 deletions

View File

@ -4,7 +4,6 @@ import { Helmet } from 'react-helmet';
import { useDispatch, useSelector } from 'react-redux';
import { ToastContainer, toast } from 'react-toastify';
import { Grid, Divider, Icon, Container } from 'semantic-ui-react';
import styled, { createGlobalStyle } from 'styled-components';
import api from '../api';
import actions from '../actions';
@ -51,17 +50,6 @@ import DocsDoc from './docs/Doc';
import Root from './main/root';
const StyledContent = styled.div`
display: flex;
flex-direction: column;
min-height: 100vh;
`;
const GlobalStyle = createGlobalStyle`
body {
background-color: rgb(255, 251, 248);
}
`;
function App() {
const dispatch = useDispatch();
@ -104,11 +92,10 @@ function App() {
}, [dispatch, user, driftReady, syncedToDrift]);
return (
<StyledContent>
<GlobalStyle whiteColor />
<div style={{display: 'flex', flexDirection: 'column', minHeight: '100vh'}}>
<Helmet defaultTitle={utils.appName()} titleTemplate={`%s | ${utils.appName()}`} />
<NavBar />
<div style={{ flex: '1' }}>
<div style={{ flex: '1 0 0' }}>
<Routes>
<Route end path="/" element={isAuthenticated
? <Home />
@ -154,19 +141,20 @@ function App() {
<Divider hidden section />
</div>
<div style={{ background: 'rgba(0,0,0,0.02)', padding: 10}}>
<div style={{ background: 'rgb(240,240,240)', padding: '30px 0px' }}>
<Container>
<Grid verticalAlign='middle' stackable columns={2}>
<Grid.Column>
<Link to="/"><img alt={`${utils.appName()} logo`} src={logo} style={{ width: '100px', opacity: 0.5 }} /></Link>
<Grid>
<Grid.Column computer={8}>
<Link to="/"><img alt={`${utils.appName()} logo`} src={logo} style={{ width: '100px', paddingTop: 20, paddingBottom: 20 }} /></Link>
{import.meta.env.VITE_SOURCE_REPO_URL &&
<p style={{marginTop: 5 }}><small>{utils.appName()} software is free and open-source.
<p style={{marginTop: 10}}><small>{utils.appName()} software is free and open-source. Contributions to the project are always welcome.
<br />
<Icon name="code" /> <a href={import.meta.env.VITE_SOURCE_REPO_URL} target="_blank" rel="noopener noreferrer" className='umami--click--source-footer'>Project source homepage</a>
</small></p>
}
</Grid.Column>
<Grid.Column textAlign="right" style={{ fontSize: 13 }}>
<Grid.Column computer={8} textAlign="right">
<div style={{ paddingTop: 40 }}>
{import.meta.env.VITE_PATREON_URL &&
<p>
<Icon name='trophy' />
@ -177,6 +165,8 @@ function App() {
<Icon name='book' />
<a href='/docs' target='_blank' rel='noopener noreferrer'>Documentation</a>
</p>
<Divider />
<p>
<Icon name="file alternate outline" />
<Link to="/privacy">Privacy Policy</Link>
@ -185,12 +175,13 @@ function App() {
<Icon name="file alternate outline" />
<Link to="terms-of-use">Terms of Use</Link>
</p>
</div>
</Grid.Column>
</Grid>
</Container>
</div>
</StyledContent>
</div>
);
}

View File

@ -155,11 +155,10 @@ Welcome back <Button floated="right" onClick={onClose} basic content="Close" />
<Input autoFocus size="large" fluid name="email" type="text" value={email} onChange={e => setEmail(e.target.value)} placeholder='Email or username' />
</Form.Field>
<Form.Field>
<label>Password</label>
<label>Password
<Link to="/password/forgotten" style={{ float: 'right' }} onClick={onClose}>Forgotten your password?</Link>
</label>
<Input size="large" fluid name="password" type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder='Password' />
<div style={{ display: 'flex', justifyContent: 'end' }}>
<Link to="/password/forgotten" onClick={onClose}>Forgotten your password?</Link>
</div>
</Form.Field>
<div className="ui hidden divider" />
<Form.Button type='submit' size="large" color="teal" fluid loading={loading}>Login</Form.Button>

View File

@ -77,8 +77,7 @@ const NewFeedMessage = connect(
)}
</div>
}
<div style={{ display: 'flex', justifyContent: 'end', marginTop: 10 }}>
<Button.Group>
<Button.Group style={{marginTop: 10, float:'right'}}>
{!noAttachments &&
<Dropdown
trigger={<Button size='small' icon='paperclip' content='Attach something' loading={attachmentUploading}/>}
@ -108,7 +107,7 @@ const NewFeedMessage = connect(
}
<Button disabled={posting} loading={posting} size='small' color='teal' icon='send' content={inReplyTo ? 'Post reply' : 'Post message'} onClick={createEntry}/>
</Button.Group>
</div>
<div style={{clear:'both'}} />
</>
);
});

View File

@ -18,17 +18,21 @@ function ProjectCard({ project }) {
<Card.Meta>{shorten(project.description)}</Card.Meta>
</Card.Content>
<Card.Content extra>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<UserChip user={project.owner} />
<span style={{ float: 'right' }}>
{project.visibility === 'private'
? (
<div><Icon name="lock" /> Private</div>
<span>
<Icon name="lock" /> Private
</span>
)
: (
<div><Icon name="unlock" /> Public</div>
<span>
<Icon name="unlock" /> Public
</span>
)
}
</div>
</span>
</Card.Content>
</Card>
);

View File

@ -126,7 +126,7 @@ function Home() {
}
{(groups && groups.length) ?
<Card fluid className='joyride-groups' style={{opacity: 0.8}}>
<Card fluid className='joyride-groups'>
<Card.Content>
<Card.Header>Your groups</Card.Header>
@ -154,7 +154,7 @@ function Home() {
}
{(import.meta.env.VITE_PATREON_URL || import.meta.env.VITE_KOFI_URL) &&
<Card fluid style={{opacity: 0.8}}>
<Card fluid color='blue'>
<Card.Content>
<Card.Header><span role="img" aria-label="Dancer">🕺</span> Support {utils.appName()}</Card.Header>
<Card.Description>{utils.appName()} is offered free of charge, but costs money to run and build. If you get value out of {utils.appName()} you may like to consider supporting it.</Card.Description>
@ -181,16 +181,15 @@ function Home() {
{user && !loadingProjects && (!projects || !projects.length) &&
<div style={{textAlign: 'center'}}>
<h1>
<span role="img" aria-label="chequered flag">🚀</span> Let's get started
<span role="img" aria-label="chequered flag">🚀</span> Let's get started, {user?.username}!
</h1>
<Divider hidden/>
<Segment placeholder textAlign='center'>
<h3>On {utils.appName()}, your patterns and files are stored in <strong><span role="img" aria-label="box">📦</span> projects</strong></h3>
<p>Projects can contain anything: from rough ideas or design experiments through to commissions and exhibitions. Treat them as if they were just <span role="img" aria-label="folder">📁</span> folders on your computer.</p>
<p><HelpLink className='joyride-help' link={`/docs/projects`} text='Learn more about projects' marginTop/></p>
<h3>On {utils.appName()} your patterns and files are stored in <strong><span role="img" aria-label="box">📦</span> projects</strong></h3>
<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 className='joyride-help' link={`/docs/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>

View File

@ -152,7 +152,6 @@ function Group() {
</Grid.Column>
<Grid.Column computer={12}>
<Segment>
{user ?
<>
{!utils.isInGroup(user, group._id) &&
@ -183,7 +182,6 @@ function Group() {
:
<Message>Please login to view or join this group.</Message>
}
</Segment>
</Grid.Column>
</Grid>
</div>

View File

@ -85,7 +85,7 @@ function Members() {
<div>
{loading && (!members || !members.length) && <Loader active inline="centered" />}
{!loading && utils.isGroupAdmin(user, group) &&
<Segment color='blue' style={{marginBottom: 30}}>
<Segment raised color='blue' style={{marginBottom: 30}}>
{(members && members.length === 1 && members[0]._id === user._id) ?
<Header>You're the only person in this group</Header>
:
@ -109,7 +109,7 @@ function Members() {
}
{requests?.length > 0 &&
<Segment color='green' style={{marginBottom:30}}>
<Segment raised color='green' style={{marginBottom:30}}>
<h3>You have membership requests</h3>
<p>The following users want to join {group.name}.</p>
<Table relaxed='very' basic='very'>
@ -135,17 +135,15 @@ function Members() {
<Card.Content>
<UserChip user={i.recipientUser} />
</Card.Content>
<Card.Content extra>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Card.Content extra >
<Label size='tiny' color='yellow' content='Invited' />
{utils.isGroupAdmin(user, group) &&
<Dropdown text='Options'>
<Dropdown text='Options' style={{float:'right'}}>
<Dropdown.Menu>
<Dropdown.Item icon='trash' content='Delete invitation' onClick={e => deleteInvitation(i)} />
</Dropdown.Menu>
</Dropdown>
}
</div>
</Card.Content>
</Card>
)}
@ -156,18 +154,16 @@ function Members() {
<Card.Meta style={{marginTop: 10}}>{m.bio}</Card.Meta>
</Card.Content>
<Card.Content extra >
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
{utils.isGroupAdmin(m, group) &&
<Label size='tiny' color='violet' icon='rocket' content='Admin' />
}
{utils.isGroupAdmin(user, group) && user._id !== m._id &&
<Dropdown text='Options'>
<Dropdown text='Options' style={{float:'right'}}>
<Dropdown.Menu>
<Dropdown.Item icon='ban' content='Kick' onClick={e => kickUser(m._id)} />
</Dropdown.Menu>
</Dropdown>
}
</div>
</Card.Content>
</Card>
)}

View File

@ -75,16 +75,13 @@ function Projects() {
<h2>Projects in this group</h2>
<p>This tab lists projects that members have made available to this group.</p>
{myProjects?.length > 0 && <div style={{ display: 'flex', justifyContent: 'end' }}>
<AddProject />
</div>}
<Divider />
{myProjects?.length > 0 && <>
<AddProject style={{float:'right'}} />
</>}
{projects?.length > 0 &&
<div style={{ display: 'flex', justifyContent: 'end' }}>
<Input size='small' icon='search' value={projectFilter} onChange={e => dispatch(actions.groups.updateProjectFilter(e.target.value))} placeholder='Filter projects...' />
</div>
<Input autoFocus style={{float:'right', marginRight: 5}} size='small' icon='search' value={projectFilter} onChange={e => dispatch(actions.groups.updateProjectFilter(e.target.value))} placeholder='Filter projects...' />
}
<Divider hidden clearing />
{projects?.length > 0 ?
<Card.Group itemsPerRow={3}>

View File

@ -112,7 +112,7 @@ function ObjectList({ compact }) {
: <div style={{ height: 40, width:40, backgroundImage: `url(${logoGreyShort})`, backgroundSize: '50px' }} />
)}
<div style={{flex: 1, marginLeft: 5}}>
<h3 style={{fontSize: 13, marginBottom: 0, wordBreak: 'break-all'}}>{object.name}</h3>
<h3 style={{fontSize: 13, marginBottom: 0}}>{object.name}</h3>
<Label size='mini' rounded>
{object.type === 'pattern' && <><Icon name='pencil' /> WIF pattern</>}
{object.type === 'file' &&
@ -166,7 +166,7 @@ function ObjectList({ compact }) {
)
}
<Card.Content>
<p style={{ wordBreak: 'break-all' }}>{object.name}</p>
<Card.Header style={{ wordBreak: 'break-all' }}>{object.name}</Card.Header>
</Card.Content>
</Card>
))}

View File

@ -117,9 +117,9 @@ function ObjectViewer() {
<>
<Helmet title={`${object.name || 'Project Item'} | ${project?.name || 'Project'}`} />
<div style={{ display: 'flex', justifyContent: 'end' }}>
<span style={{float:'right'}}>
{object.type === 'pattern' && (project.user === (user && user._id) || project.openSource || object.preview) && <>
<Dropdown icon={null} trigger={<Button size='small' secondary icon='download' content='Download pattern' loading={downloading} disabled={downloading}/>}>
<Dropdown icon={null} trigger={<Button size='tiny' secondary icon='download' content='Download pattern' loading={downloading} disabled={downloading}/>}>
<Dropdown.Menu>
{object.preview &&
<Dropdown.Item onClick={e => downloadDrawdownImage(object)} content='Download drawdown as an image' icon='file outline' />
@ -160,7 +160,7 @@ function ObjectViewer() {
</>
}
</div>
</span>
{editingName ?
<div style={{marginBottom: 5}}>

View File

@ -54,6 +54,12 @@ function Project() {
{project
&& (
<div>
{/*history.location?.state?.prevPath &&
<div style={{marginBottom:15}}>
<Button basic secondary onClick={e => history.goBack()} icon='arrow left' content='Go back' />
</div>
*/}
{wideBody() && project.owner &&
<>
<h3 style={{ marginBottom: 0 }}>
@ -70,15 +76,14 @@ function Project() {
<Card fluid>
<Card.Content>
<Card.Header style={{ marginBottom: 10 }}>
{project.visibility === 'private' && <span data-tooltip="This project is private" data-position="right center"><Icon name="lock" /></span>}
{project.visibility === 'private' && <span data-tooltip="This project is private"><Icon name="lock" /></span>}
{project.name}
</Card.Header>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
{project.owner && <UserChip user={project.owner} />}
{utils.canEditProject(user, project) &&
<Button basic size='mini' icon='cogs' content='Settings' as={Link} to={`/${fullName}/settings`} />
<Button style={{float:'right'}} basic size='mini' icon='cogs' content='Settings' as={Link} to={`/${fullName}/settings`} />
}
</div>
</Card.Content>
<Card.Content extra>
{editingDescription

View File

@ -64,7 +64,7 @@ function ProjectSettings() {
<HelpLink link={`/docs/projects#changing-the-project-s-settings`} />
<Divider hidden />
<Divider hidden section />
<Segment>
<h3>General settings</h3>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 29 KiB

0
web/src/index.css Normal file
View File

View File

@ -9,6 +9,7 @@ import { BrowserTracing } from '@sentry/tracing';
import 'react-toastify/dist/ReactToastify.css';
import 'pell/dist/pell.min.css';
import './index.css';
import reducers from './reducers';
import App from './components/App';