Compare commits

..

6 Commits

Author SHA1 Message Date
30ebc7d22d Reduce raised components
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-15 17:34:52 +00:00
49ccdbd8ab Removing all floats and replace with flex 2023-05-15 17:14:12 +00:00
57de689815 General UI enhancements 2023-05-15 16:27:22 +00:00
55325dfe8b Improved footer design 2023-05-15 16:12:55 +00:00
214b80b72c Improved handling of longer file names in object lists 2023-05-15 15:23:56 +00:00
084ae20664 Small UI tweaks 2023-05-15 15:11:18 +00:00
15 changed files with 135 additions and 124 deletions

View File

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

View File

@ -155,10 +155,11 @@ 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' /> <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>
<Form.Field> <Form.Field>
<label>Password <label>Password</label>
<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' /> <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> </Form.Field>
<div className="ui hidden divider" /> <div className="ui hidden divider" />
<Form.Button type='submit' size="large" color="teal" fluid loading={loading}>Login</Form.Button> <Form.Button type='submit' size="large" color="teal" fluid loading={loading}>Login</Form.Button>

View File

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

View File

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

View File

@ -126,7 +126,7 @@ function Home() {
} }
{(groups && groups.length) ? {(groups && groups.length) ?
<Card fluid className='joyride-groups'> <Card fluid className='joyride-groups' style={{opacity: 0.8}}>
<Card.Content> <Card.Content>
<Card.Header>Your groups</Card.Header> <Card.Header>Your groups</Card.Header>
@ -154,7 +154,7 @@ function Home() {
} }
{(import.meta.env.VITE_PATREON_URL || import.meta.env.VITE_KOFI_URL) && {(import.meta.env.VITE_PATREON_URL || import.meta.env.VITE_KOFI_URL) &&
<Card fluid color='blue'> <Card fluid style={{opacity: 0.8}}>
<Card.Content> <Card.Content>
<Card.Header><span role="img" aria-label="Dancer">🕺</span> Support {utils.appName()}</Card.Header> <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> <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,15 +181,16 @@ function Home() {
{user && !loadingProjects && (!projects || !projects.length) && {user && !loadingProjects && (!projects || !projects.length) &&
<div style={{textAlign: 'center'}}> <div style={{textAlign: 'center'}}>
<h1> <h1>
<span role="img" aria-label="chequered flag">🚀</span> Let's get started, {user?.username}! <span role="img" aria-label="chequered flag">🚀</span> Let's get started
</h1> </h1>
<Divider hidden/> <Divider hidden/>
<Segment placeholder textAlign='center'> <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> <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> <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>
<Divider /> <Divider />
<h4>Start by creating a new project. Don't worry, you can keep it private.</h4> <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" /> <Button className='joyride-createProject' as={Link} to="/projects/new" color="teal" icon="plus" content="Create a project" />
</Segment> </Segment>

View File

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

View File

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

View File

@ -75,13 +75,16 @@ function Projects() {
<h2>Projects in this group</h2> <h2>Projects in this group</h2>
<p>This tab lists projects that members have made available to this group.</p> <p>This tab lists projects that members have made available to this group.</p>
{myProjects?.length > 0 && <> {myProjects?.length > 0 && <div style={{ display: 'flex', justifyContent: 'end' }}>
<AddProject style={{float:'right'}} /> <AddProject />
</>} </div>}
<Divider />
{projects?.length > 0 && {projects?.length > 0 &&
<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...' /> <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>
} }
<Divider hidden clearing />
{projects?.length > 0 ? {projects?.length > 0 ?
<Card.Group itemsPerRow={3}> <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={{ height: 40, width:40, backgroundImage: `url(${logoGreyShort})`, backgroundSize: '50px' }} />
)} )}
<div style={{flex: 1, marginLeft: 5}}> <div style={{flex: 1, marginLeft: 5}}>
<h3 style={{fontSize: 13, marginBottom: 0}}>{object.name}</h3> <h3 style={{fontSize: 13, marginBottom: 0, wordBreak: 'break-all'}}>{object.name}</h3>
<Label size='mini' rounded> <Label size='mini' rounded>
{object.type === 'pattern' && <><Icon name='pencil' /> WIF pattern</>} {object.type === 'pattern' && <><Icon name='pencil' /> WIF pattern</>}
{object.type === 'file' && {object.type === 'file' &&
@ -166,7 +166,7 @@ function ObjectList({ compact }) {
) )
} }
<Card.Content> <Card.Content>
<Card.Header style={{ wordBreak: 'break-all' }}>{object.name}</Card.Header> <p style={{ wordBreak: 'break-all' }}>{object.name}</p>
</Card.Content> </Card.Content>
</Card> </Card>
))} ))}

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

View File

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