Skip to content

Private Sketch feat. #3034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export const DELETE_COLLECTION = 'DELETE_COLLECTION';
export const ADD_TO_COLLECTION = 'ADD_TO_COLLECTION';
export const REMOVE_FROM_COLLECTION = 'REMOVE_FROM_COLLECTION';
export const EDIT_COLLECTION = 'EDIT_COLLECTION';
export const CHANGE_VISIBILITY = 'CHANGE_VISIBILITY';
export const SET_PROJECT_VISIBILITY = 'SET_PROJECT_VISIBILITY';

export const DELETE_PROJECT = 'DELETE_PROJECT';

Expand Down
14 changes: 14 additions & 0 deletions client/images/earth.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions client/images/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions client/modules/IDE/actions/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,45 @@ export function deleteProject(id) {
});
};
}
export function changeVisibility(projectId, projectName, visibility) {
return (dispatch, getState) => {
const state = getState();

apiClient
.patch('/project/visibility', { projectId, visibility })
.then((response) => {
if (response.status === 200) {
const { visibility: newVisibility } = response.data;

dispatch({
type: ActionTypes.CHANGE_VISIBILITY,
payload: {
id: response.data.id,
visibility: newVisibility
}
});

if (state.project.id === response.data.id) {
dispatch({
type: ActionTypes.SET_PROJECT_VISIBILITY,
visibility: newVisibility
});
dispatch({
type: ActionTypes.SET_PROJECT_NAME,
name: response.data.name
});
}
dispatch(
setToastText(`${projectName} is now ${newVisibility.toLowerCase()}`)
);
dispatch(showToast(2000));
}
})
.catch((error) => {
dispatch({
type: ActionTypes.ERROR,
error: error?.response?.data
});
});
};
}
48 changes: 42 additions & 6 deletions client/modules/IDE/components/Header/MobileNav.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { setLanguage } from '../../actions/preferences';
import Overlay from '../../../App/components/Overlay';
import ProjectName from './ProjectName';
import CollectionCreate from '../../../User/components/CollectionCreate';
import { changeVisibility } from '../../actions/project';

const Nav = styled(Menubar)`
background: ${prop('MobilePanel.default.background')};
Expand Down Expand Up @@ -75,6 +76,13 @@ const Title = styled.div`
margin: 0;
}

> section {
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
}

> h5 {
font-size: ${remSize(13)};
font-weight: normal;
Expand Down Expand Up @@ -212,6 +220,7 @@ const MobileMenuItem = ({ children, ...props }) => (
const MobileNav = () => {
const project = useSelector((state) => state.project);
const user = useSelector((state) => state.user);
const dispatch = useDispatch();

const { t } = useTranslation();

Expand All @@ -237,21 +246,48 @@ const MobileNav = () => {
}

const title = useMemo(resolveTitle, [pageName, project.name]);

const userIsOwner = user?.username === project.owner?.username;
const Logo = AsteriskIcon;

const showPrivacyToggle =
project?.owner && title === project.name && userIsOwner;
const showOwner = project?.owner && title === project.name && !userIsOwner;

const toggleVisibility = (e) => {
try {
const isChecked = e.target.checked;
dispatch(
changeVisibility(
project.id,
project.name,
isChecked ? 'Private' : 'Public'
)
);
} catch (error) {
console.log(error);
}
};
return (
<Nav>
<LogoContainer>
<Logo />
</LogoContainer>
<Title>
<h1>{title === project.name ? <ProjectName /> : title}</h1>
{project?.owner && title === project.name && (
<Link to={`/${project.owner.username}/sketches`}>
by {project?.owner?.username}
</Link>
<h1>{title === project?.name ? <ProjectName /> : title}</h1>
{showPrivacyToggle && (
<main className="toolbar__makeprivate">
<p>Private</p>
<input
className="toolbar__togglevisibility"
type="checkbox"
onChange={toggleVisibility}
defaultChecked={project.visibility === 'Private'}
/>
</main>
)}
{showOwner && <h5>by {project?.owner?.username}</h5>}
</Title>

{/* check if the user is in login page */}
{pageName === 'login' || pageName === 'signup' ? (
// showing the CrossIcon
Expand Down
60 changes: 43 additions & 17 deletions client/modules/IDE/components/Header/Toolbar.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
Expand All @@ -15,7 +15,7 @@ import {
setGridOutput,
setTextOutput
} from '../../actions/preferences';

import { changeVisibility } from '../../actions/project';
import PlayIcon from '../../../../images/play.svg';
import StopIcon from '../../../../images/stop.svg';
import PreferencesIcon from '../../../../images/preferences.svg';
Expand All @@ -27,10 +27,30 @@ const Toolbar = (props) => {
(state) => state.ide
);
const project = useSelector((state) => state.project);
const user = useSelector((state) => state.user);
const autorefresh = useSelector((state) => state.preferences.autorefresh);
const dispatch = useDispatch();

const { t } = useTranslation();
const userIsOwner = user?.username === project.owner?.username;
const [isPrivate, setIsPrivate] = useState(project.visibility === 'Private');
useEffect(() => {
setIsPrivate(project.visibility === 'Private');
}, [project]);

const showPrivacyToggle = project?.owner && userIsOwner;
const showOwner = project?.owner && !userIsOwner;

const toggleVisibility = (e) => {
try {
const isChecked = e.target.checked;
const newVisibility = isChecked ? 'Private' : 'Public';
setIsPrivate(isChecked);
dispatch(changeVisibility(project.id, project.name, newVisibility));
} catch (error) {
console.log(error);
setIsPrivate(project.visibility === 'Private');
}
};

const playButtonClass = classNames({
'toolbar__play-button': true,
Expand Down Expand Up @@ -100,21 +120,27 @@ const Toolbar = (props) => {
</div>
<div className="toolbar__project-name-container">
<ProjectName />
{(() => {
if (project.owner) {
return (
<p className="toolbar__project-project.owner">
{t('Toolbar.By')}{' '}
<Link to={`/${project.owner.username}/sketches`}>
{project.owner.username}
</Link>
</p>
);
}
return null;
})()}
{showPrivacyToggle && (
<main className="toolbar__makeprivate">
<p>Private</p>
<input
type="checkbox"
className="toolbar__togglevisibility"
checked={isPrivate}
onChange={toggleVisibility}
/>
</main>
)}
{showOwner && (
<p className="toolbar__project-owner">
{t('Toolbar.By')}{' '}
<Link to={`/${project.owner.username}/sketches`}>
{project.owner.username}
</Link>
</p>
)}
<VersionIndicator />
</div>
<VersionIndicator />
<div style={{ flex: 1 }} />
<button
className={preferencesButtonClass}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,22 @@ exports[`Nav renders dashboard version for mobile 1`] = `
margin: 0;
}

.c2 > section {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
gap: 5px;
}

.c2 > h5 {
font-size: 1.0833333333333333rem;
font-weight: normal;
Expand Down Expand Up @@ -757,6 +773,22 @@ exports[`Nav renders editor version for mobile 1`] = `
margin: 0;
}

.c2 > section {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
gap: 5px;
}

.c2 > h5 {
font-size: 1.0833333333333333rem;
font-weight: normal;
Expand Down
13 changes: 8 additions & 5 deletions client/modules/IDE/components/SketchList.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-len */
import PropTypes from 'prop-types';
import classNames from 'classnames';
import React, { useEffect, useState, useMemo, useCallback } from 'react';
Expand All @@ -13,9 +14,9 @@ import getSortedSketches from '../selectors/projects';
import Loader from '../../App/components/loader';
import Overlay from '../../App/components/Overlay';
import AddToCollectionList from './AddToCollectionList';
import SketchListRowBase from './SketchListRowBase';
import ArrowUpIcon from '../../../images/sort-arrow-up.svg';
import ArrowDownIcon from '../../../images/sort-arrow-down.svg';
import SketchListRowBase from './SketchListRowBase';

const SketchList = ({
user,
Expand Down Expand Up @@ -59,9 +60,7 @@ const SketchList = ({

const renderEmptyTable = () => {
if (!isLoading() && sketches.length === 0) {
return (
<p className="sketches-table__empty">{t('SketchList.NoSketches')}</p>
);
return <p className="sketches-table">{t('SketchList.NoSketches')}</p>;
}
return null;
};
Expand Down Expand Up @@ -118,6 +117,8 @@ const SketchList = ({
[sorting, getButtonLabel, toggleDirectionForField, t]
);

const userIsOwner = user.username === username;

return (
<article className="sketches-table-container">
<Helmet>
Expand Down Expand Up @@ -145,6 +146,7 @@ const SketchList = ({
context: mobile ? 'mobile' : ''
})
)}
{userIsOwner && renderFieldHeader('makePrivate', 'Make Private')}
<th scope="col"></th>
</tr>
</thead>
Expand Down Expand Up @@ -187,7 +189,8 @@ SketchList.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
createdAt: PropTypes.string.isRequired,
updatedAt: PropTypes.string.isRequired
updatedAt: PropTypes.string.isRequired,
visibility: PropTypes.string
})
).isRequired,
username: PropTypes.string,
Expand Down
Loading
Loading