feat: projects can be set to public

This commit is contained in:
Jordan Knott 2021-04-30 22:55:37 -05:00
parent 3e72271d9b
commit 04c12e4da9
38 changed files with 1849 additions and 1186 deletions

View File

@ -30,6 +30,7 @@
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "off",
"react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }], "react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
"react/require-default-props": "off",
"no-case-declarations": "off", "no-case-declarations": "off",
"no-plusplus": "off", "no-plusplus": "off",
"react/prop-types": 0, "react/prop-types": 0,

View File

@ -24,7 +24,7 @@
"@types/react-router-dom": "^5.1.3", "@types/react-router-dom": "^5.1.3",
"@types/react-select": "^3.0.13", "@types/react-select": "^3.0.13",
"@types/react-timeago": "^4.1.1", "@types/react-timeago": "^4.1.1",
"@types/styled-components": "^5.0.0", "@types/styled-components": "^5.1.0",
"apollo-cache-inmemory": "^1.6.5", "apollo-cache-inmemory": "^1.6.5",
"apollo-client": "^2.6.8", "apollo-client": "^2.6.8",
"apollo-link": "^1.2.13", "apollo-link": "^1.2.13",
@ -64,7 +64,7 @@
"react-timeago": "^4.4.0", "react-timeago": "^4.4.0",
"react-toastify": "^6.0.8", "react-toastify": "^6.0.8",
"rich-markdown-editor": "^10.6.5", "rich-markdown-editor": "^10.6.5",
"styled-components": "^5.0.1", "styled-components": "^5.1.0",
"typescript": "~3.7.2" "typescript": "~3.7.2"
}, },
"proxy": "http://localhost:3333", "proxy": "http://localhost:3333",
@ -93,10 +93,10 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@graphql-codegen/cli": "^1.13.2", "@graphql-codegen/cli": "^1.21.4",
"@graphql-codegen/typescript": "^1.13.2", "@graphql-codegen/typescript": "^1.22.0",
"@graphql-codegen/typescript-operations": "^1.13.2", "@graphql-codegen/typescript-operations": "^1.17.16",
"@graphql-codegen/typescript-react-apollo": "^1.13.2", "@graphql-codegen/typescript-react-apollo": "^2.2.4",
"@typescript-eslint/eslint-plugin": "^2.20.0", "@typescript-eslint/eslint-plugin": "^2.20.0",
"@typescript-eslint/parser": "^2.20.0", "@typescript-eslint/parser": "^2.20.0",
"eslint": "^6.8.0", "eslint": "^6.8.0",

View File

@ -41,9 +41,7 @@ const AuthorizedRoutes = () => {
const { status } = x; const { status } = x;
const response: ValidateTokenResponse = await x.json(); const response: ValidateTokenResponse = await x.json();
const { valid, userID } = response; const { valid, userID } = response;
if (!valid) { if (valid) {
history.replace(`/login`);
} else {
setUser(userID); setUser(userID);
} }
setLoading(false); setLoading(false);

View File

@ -1,17 +1,28 @@
import React from 'react'; import React, { useState } from 'react';
import ProjectSettings, { DeleteConfirm, DELETE_INFO } from 'shared/components/ProjectSettings'; import ProjectSettings, { DeleteConfirm, DELETE_INFO, PublicConfirm } from 'shared/components/ProjectSettings';
import { useDeleteProjectMutation, GetProjectsDocument } from 'shared/generated/graphql'; import {
useDeleteProjectMutation,
GetProjectsDocument,
useToggleProjectVisibilityMutation,
} from 'shared/generated/graphql';
import { usePopup, Popup } from 'shared/components/PopupMenu'; import { usePopup, Popup } from 'shared/components/PopupMenu';
import produce from 'immer'; import produce from 'immer';
type ProjectPopupProps = { type ProjectPopupProps = {
history: any; history: any;
name: string; name: string;
publicOn: string | null;
projectID: string; projectID: string;
}; };
const ProjectPopup: React.FC<ProjectPopupProps> = ({ history, name, projectID }) => { const ProjectPopup: React.FC<ProjectPopupProps> = ({ history, name, projectID, publicOn: initialPublicOn }) => {
const { hidePopup, setTab } = usePopup(); const { hidePopup, setTab } = usePopup();
const [publicOn, setPublicOn] = useState(initialPublicOn);
const [toggleProjectVisibility] = useToggleProjectVisibilityMutation({
onCompleted: data => {
setPublicOn(data.toggleProjectVisibility.project.publicOn);
},
});
const [deleteProject] = useDeleteProjectMutation({ const [deleteProject] = useDeleteProjectMutation({
update: (client, deleteData) => { update: (client, deleteData) => {
const cacheData: any = client.readQuery({ const cacheData: any = client.readQuery({
@ -36,11 +47,28 @@ const ProjectPopup: React.FC<ProjectPopupProps> = ({ history, name, projectID })
<> <>
<Popup title={null} tab={0}> <Popup title={null} tab={0}>
<ProjectSettings <ProjectSettings
publicOn={publicOn}
onToggleProjectVisible={visible => {
if (visible) {
setTab(2, { width: 300 });
} else {
toggleProjectVisibility({ variables: { projectID, isPublic: false } });
}
}}
onDeleteProject={() => { onDeleteProject={() => {
setTab(1, { width: 300 }); setTab(1, { width: 300 });
}} }}
/> />
</Popup> </Popup>
<Popup title="Change to public?" tab={1}>
<PublicConfirm
onConfirm={() => {
if (projectID) {
toggleProjectVisibility({ variables: { projectID, isPublic: true } });
}
}}
/>
</Popup>
<Popup title={`Delete the "${name}" project?`} tab={1}> <Popup title={`Delete the "${name}" project?`} tab={1}>
<DeleteConfirm <DeleteConfirm
description={DELETE_INFO.DELETE_PROJECTS.description} description={DELETE_INFO.DELETE_PROJECTS.description}

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import TopNavbar, { MenuItem } from 'shared/components/TopNavbar'; import TopNavbar, { MenuItem } from 'shared/components/TopNavbar';
import LoggedOutNavbar from 'shared/components/TopNavbar/LoggedOut';
import { ProfileMenu } from 'shared/components/DropdownMenu'; import { ProfileMenu } from 'shared/components/DropdownMenu';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { useCurrentUser } from 'App/context'; import { useCurrentUser } from 'App/context';
@ -11,6 +12,8 @@ import NotificationPopup, { NotificationItem } from 'shared/components/Notifcati
import theme from 'App/ThemeStyles'; import theme from 'App/ThemeStyles';
import ProjectFinder from './ProjectFinder'; import ProjectFinder from './ProjectFinder';
// TODO: Move to context based navbar?
type GlobalTopNavbarProps = { type GlobalTopNavbarProps = {
nameOnly?: boolean; nameOnly?: boolean;
projectID: string | null; projectID: string | null;
@ -30,7 +33,7 @@ type GlobalTopNavbarProps = {
onRemoveInvitedFromBoard?: (email: string) => void; onRemoveInvitedFromBoard?: (email: string) => void;
}; };
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ const LoggedInNavbar: React.FC<GlobalTopNavbarProps> = ({
currentTab, currentTab,
onSetTab, onSetTab,
menuType, menuType,
@ -46,9 +49,9 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
onRemoveInvitedFromBoard, onRemoveInvitedFromBoard,
onRemoveFromBoard, onRemoveFromBoard,
}) => { }) => {
const { user, setUser } = useCurrentUser();
const { data } = useTopNavbarQuery(); const { data } = useTopNavbarQuery();
const { showPopup, hidePopup } = usePopup(); const { showPopup, hidePopup } = usePopup();
const { setUser } = useCurrentUser();
const history = useHistory(); const history = useHistory();
const onLogout = () => { const onLogout = () => {
fetch('/auth/logout', { fetch('/auth/logout', {
@ -109,9 +112,6 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
} }
}; };
if (!user) {
return null;
}
// TODO: readd permision check // TODO: readd permision check
// const userIsTeamOrProjectAdmin = user.isAdmin(PermissionLevel.TEAM, PermissionObjectType.TEAM, teamID); // const userIsTeamOrProjectAdmin = user.isAdmin(PermissionLevel.TEAM, PermissionObjectType.TEAM, teamID);
const userIsTeamOrProjectAdmin = true; const userIsTeamOrProjectAdmin = true;
@ -174,6 +174,8 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
} }
}; };
const user = data ? data.me?.user : null;
return ( return (
<> <>
<TopNavbar <TopNavbar
@ -188,7 +190,7 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
); );
}} }}
currentTab={currentTab} currentTab={currentTab}
user={data ? data.me.user : null} user={user ?? null}
canEditProjectName={userIsTeamOrProjectAdmin} canEditProjectName={userIsTeamOrProjectAdmin}
canInviteUser={userIsTeamOrProjectAdmin} canInviteUser={userIsTeamOrProjectAdmin}
onMemberProfile={onMemberProfile} onMemberProfile={onMemberProfile}
@ -215,4 +217,45 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
); );
}; };
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
currentTab,
onSetTab,
menuType,
teamID,
onChangeProjectOwner,
onChangeRole,
name,
popupContent,
projectMembers,
projectInvitedMembers,
onInviteUser,
onSaveProjectName,
onRemoveInvitedFromBoard,
onRemoveFromBoard,
}) => {
const { user } = useCurrentUser();
if (user) {
return (
<LoggedInNavbar
currentTab={currentTab}
projectID={null}
onSetTab={onSetTab}
menuType={menuType}
teamID={teamID}
onChangeRole={onChangeRole}
onChangeProjectOwner={onChangeProjectOwner}
name={name}
popupContent={popupContent}
projectMembers={projectMembers}
projectInvitedMembers={projectInvitedMembers}
onInviteUser={onInviteUser}
onSaveProjectName={onSaveProjectName}
onRemoveInvitedFromBoard={onRemoveInvitedFromBoard}
onRemoveFromBoard={onRemoveFromBoard}
/>
);
}
return <LoggedOutNavbar name={name} menuType={menuType} />;
};
export default GlobalTopNavbar; export default GlobalTopNavbar;

View File

@ -62,7 +62,7 @@ const Projects = () => {
}} }}
/> />
<GlobalTopNavbar projectID={null} onSaveProjectName={NOOP} name={null} /> <GlobalTopNavbar projectID={null} onSaveProjectName={NOOP} name={null} />
{!loading && data && ( {!loading && data && data.me && (
<Settings <Settings
profile={data.me.user} profile={data.me.user}
onProfileAvatarChange={() => { onProfileAvatarChange={() => {

View File

@ -198,6 +198,7 @@ type ProjectBoardProps = {
}; };
export const BoardLoading = () => { export const BoardLoading = () => {
const { user } = useCurrentUser();
return ( return (
<> <>
<ProjectBar> <ProjectBar>
@ -215,20 +216,22 @@ export const BoardLoading = () => {
<ProjectActionText>Filter</ProjectActionText> <ProjectActionText>Filter</ProjectActionText>
</ProjectAction> </ProjectAction>
</ProjectActions> </ProjectActions>
<ProjectActions> {user && (
<ProjectAction> <ProjectActions>
<Tags width={13} height={13} /> <ProjectAction>
<ProjectActionText>Labels</ProjectActionText> <Tags width={13} height={13} />
</ProjectAction> <ProjectActionText>Labels</ProjectActionText>
<ProjectAction disabled> </ProjectAction>
<ToggleOn width={13} height={13} /> <ProjectAction disabled>
<ProjectActionText>Fields</ProjectActionText> <ToggleOn width={13} height={13} />
</ProjectAction> <ProjectActionText>Fields</ProjectActionText>
<ProjectAction disabled> </ProjectAction>
<Bolt width={13} height={13} /> <ProjectAction disabled>
<ProjectActionText>Rules</ProjectActionText> <Bolt width={13} height={13} />
</ProjectAction> <ProjectActionText>Rules</ProjectActionText>
</ProjectActions> </ProjectAction>
</ProjectActions>
)}
</ProjectBar> </ProjectBar>
<EmptyBoard /> <EmptyBoard />
</> </>
@ -469,7 +472,7 @@ const ProjectBoard: React.FC<ProjectBoardProps> = ({ projectID, onCardLabelClick
} }
return 'All Tasks'; return 'All Tasks';
}; };
if (data && user) { if (data) {
labelsRef.current = data.findProject.labels; labelsRef.current = data.findProject.labels;
membersRef.current = data.findProject.members; membersRef.current = data.findProject.members;
const onQuickEditorOpen = ($target: React.RefObject<HTMLElement>, taskID: string, taskGroupID: string) => { const onQuickEditorOpen = ($target: React.RefObject<HTMLElement>, taskID: string, taskGroupID: string) => {
@ -570,34 +573,37 @@ const ProjectBoard: React.FC<ProjectBoardProps> = ({ projectID, onCardLabelClick
); );
})} })}
</ProjectActions> </ProjectActions>
<ProjectActions> {user && (
<ProjectAction <ProjectActions>
onClick={$labelsRef => { <ProjectAction
showPopup( onClick={$labelsRef => {
$labelsRef, showPopup(
<LabelManagerEditor $labelsRef,
taskLabels={null} <LabelManagerEditor
labelColors={data.labelColors} taskLabels={null}
labels={labelsRef} labelColors={data.labelColors}
projectID={projectID ?? ''} labels={labelsRef}
/>, projectID={projectID ?? ''}
); />,
}} );
> }}
<Tags width={13} height={13} /> >
<ProjectActionText>Labels</ProjectActionText> <Tags width={13} height={13} />
</ProjectAction> <ProjectActionText>Labels</ProjectActionText>
<ProjectAction disabled> </ProjectAction>
<ToggleOn width={13} height={13} /> <ProjectAction disabled>
<ProjectActionText>Fields</ProjectActionText> <ToggleOn width={13} height={13} />
</ProjectAction> <ProjectActionText>Fields</ProjectActionText>
<ProjectAction disabled> </ProjectAction>
<Bolt width={13} height={13} /> <ProjectAction disabled>
<ProjectActionText>Rules</ProjectActionText> <Bolt width={13} height={13} />
</ProjectAction> <ProjectActionText>Rules</ProjectActionText>
</ProjectActions> </ProjectAction>
</ProjectActions>
)}
</ProjectBar> </ProjectBar>
<SimpleLists <SimpleLists
isPublic={user === null}
onTaskClick={task => { onTaskClick={task => {
history.push(`${match.url}/c/${task.id}`); history.push(`${match.url}/c/${task.id}`);
}} }}

View File

@ -424,7 +424,7 @@ const Details: React.FC<DetailsProps> = ({
updateTaskComment({ variables: { commentID, message } }); updateTaskComment({ variables: { commentID, message } });
}} }}
editableComment={editableComment} editableComment={editableComment}
me={data.me.user} me={data.me ? data.me.user : null}
onCommentShowActions={(commentID, $targetRef) => { onCommentShowActions={(commentID, $targetRef) => {
showPopup( showPopup(
$targetRef, $targetRef,

View File

@ -163,10 +163,6 @@ const Project = () => {
} }
}, [data]); }, [data]);
if (error) {
history.push('/projects');
}
if (data) { if (data) {
labelsRef.current = data.findProject.labels; labelsRef.current = data.findProject.labels;
@ -204,7 +200,14 @@ const Project = () => {
/>, />,
); );
}} }}
popupContent={<ProjectPopup history={history} name={data.findProject.name} projectID={projectID} />} popupContent={
<ProjectPopup // eslint-disable-line
history={history}
publicOn={data.findProject.publicOn}
name={data.findProject.name}
projectID={projectID}
/>
}
menuType={[{ name: 'Board', link: location.pathname }]} menuType={[{ name: 'Board', link: location.pathname }]}
currentTab={0} currentTab={0}
projectMembers={data.findProject.members} projectMembers={data.findProject.members}

View File

@ -58,12 +58,14 @@ type Props = {
onCardTitleChange?: (name: string) => void; onCardTitleChange?: (name: string) => void;
labelVariant?: CardLabelVariant; labelVariant?: CardLabelVariant;
toggleLabels?: boolean; toggleLabels?: boolean;
isPublic?: boolean;
toggleDirection?: 'shrink' | 'expand'; toggleDirection?: 'shrink' | 'expand';
}; };
const Card = React.forwardRef( const Card = React.forwardRef(
( (
{ {
isPublic = false,
wrapperProps, wrapperProps,
onContextMenu, onContextMenu,
taskID, taskID,
@ -120,9 +122,11 @@ const Card = React.forwardRef(
} }
}; };
const onTaskContext = (e: React.MouseEvent) => { const onTaskContext = (e: React.MouseEvent) => {
e.preventDefault(); if (!isPublic) {
e.stopPropagation(); e.preventDefault();
onOpenComposer(); e.stopPropagation();
onOpenComposer();
}
}; };
const onOperationClick = (e: React.MouseEvent<HTMLOrSVGElement>) => { const onOperationClick = (e: React.MouseEvent<HTMLOrSVGElement>) => {
e.preventDefault(); e.preventDefault();
@ -145,7 +149,7 @@ const Card = React.forwardRef(
{...wrapperProps} {...wrapperProps}
> >
<ListCardInnerContainer ref={$innerCardRef}> <ListCardInnerContainer ref={$innerCardRef}>
{isActive && !editable && ( {!isPublic && isActive && !editable && (
<ListCardOperation <ListCardOperation
onClick={e => { onClick={e => {
e.stopPropagation(); e.stopPropagation();

View File

@ -72,6 +72,9 @@ export const HeaderName = styled(TextareaAutosize)`
box-shadow: none; box-shadow: none;
font-weight: 600; font-weight: 600;
margin: -4px 0; margin: -4px 0;
&:disabled {
opacity: 1;
}
letter-spacing: normal; letter-spacing: normal;
word-spacing: normal; word-spacing: normal;

View File

@ -24,6 +24,7 @@ type Props = {
onOpenComposer: (id: string) => void; onOpenComposer: (id: string) => void;
wrapperProps?: any; wrapperProps?: any;
headerProps?: any; headerProps?: any;
isPublic: boolean;
index?: number; index?: number;
onExtraMenuOpen: (taskGroupID: string, $targetRef: React.RefObject<HTMLElement>) => void; onExtraMenuOpen: (taskGroupID: string, $targetRef: React.RefObject<HTMLElement>) => void;
}; };
@ -37,6 +38,7 @@ const List = React.forwardRef(
isComposerOpen, isComposerOpen,
onOpenComposer, onOpenComposer,
children, children,
isPublic,
wrapperProps, wrapperProps,
headerProps, headerProps,
onExtraMenuOpen, onExtraMenuOpen,
@ -86,39 +88,37 @@ const List = React.forwardRef(
<Container ref={$wrapperRef} {...wrapperProps}> <Container ref={$wrapperRef} {...wrapperProps}>
<Wrapper> <Wrapper>
<Header {...headerProps} isEditing={isEditingTitle}> <Header {...headerProps} isEditing={isEditingTitle}>
<HeaderEditTarget onClick={onClick} isHidden={isEditingTitle} /> {!isPublic && <HeaderEditTarget onClick={onClick} isHidden={isEditingTitle} />}
<HeaderName <HeaderName
ref={$listNameRef} ref={$listNameRef}
disabled={isPublic}
onBlur={onBlur} onBlur={onBlur}
onChange={onChange} onChange={onChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
spellCheck={false} spellCheck={false}
value={listName} value={listName}
/> />
<ListExtraMenuButtonWrapper ref={$extraActionsRef} onClick={handleExtraMenuOpen}> {!isPublic && (
<Ellipsis size={16} color="#c2c6dc" /> <ListExtraMenuButtonWrapper ref={$extraActionsRef} onClick={handleExtraMenuOpen}>
</ListExtraMenuButtonWrapper> <Ellipsis size={16} color="#c2c6dc" />
</ListExtraMenuButtonWrapper>
)}
</Header> </Header>
{children && children} {children && children}
<AddCardContainer hidden={isComposerOpen}> {!isPublic && (
<AddCardButton onClick={() => onOpenComposer(id)}> <AddCardContainer hidden={isComposerOpen}>
<Plus width={12} height={12} /> <AddCardButton onClick={() => onOpenComposer(id)}>
<AddCardButtonText>Add another card</AddCardButtonText> <Plus width={12} height={12} />
</AddCardButton> <AddCardButtonText>Add another card</AddCardButtonText>
</AddCardContainer> </AddCardButton>
</AddCardContainer>
)}
</Wrapper> </Wrapper>
</Container> </Container>
); );
}, },
); );
List.defaultProps = {
children: null,
isComposerOpen: false,
wrapperProps: {},
headerProps: {},
};
List.displayName = 'List'; List.displayName = 'List';
export default List; export default List;

View File

@ -151,6 +151,7 @@ interface SimpleProps {
onCardMemberClick: OnCardMemberClick; onCardMemberClick: OnCardMemberClick;
onCardLabelClick: () => void; onCardLabelClick: () => void;
cardLabelVariant: CardLabelVariant; cardLabelVariant: CardLabelVariant;
isPublic?: boolean;
taskStatusFilter?: TaskStatusFilter; taskStatusFilter?: TaskStatusFilter;
taskMetaFilters?: TaskMetaFilters; taskMetaFilters?: TaskMetaFilters;
taskSorting?: TaskSorting; taskSorting?: TaskSorting;
@ -188,6 +189,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
onExtraMenuOpen, onExtraMenuOpen,
onCardMemberClick, onCardMemberClick,
taskStatusFilter = initTaskStatusFilter, taskStatusFilter = initTaskStatusFilter,
isPublic = false,
taskMetaFilters = initTaskMetaFilters, taskMetaFilters = initTaskMetaFilters,
taskSorting = initTaskSorting, taskSorting = initTaskSorting,
}) => { }) => {
@ -300,6 +302,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
onOpenComposer={id => setCurrentComposer(id)} onOpenComposer={id => setCurrentComposer(id)}
isComposerOpen={currentComposer === taskGroup.id} isComposerOpen={currentComposer === taskGroup.id}
onSaveName={name => onChangeTaskGroupName(taskGroup.id, name)} onSaveName={name => onChangeTaskGroupName(taskGroup.id, name)}
isPublic={isPublic}
ref={columnDragProvided.innerRef} ref={columnDragProvided.innerRef}
wrapperProps={columnDragProvided.draggableProps} wrapperProps={columnDragProvided.draggableProps}
headerProps={columnDragProvided.dragHandleProps} headerProps={columnDragProvided.dragHandleProps}
@ -328,6 +331,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
<Card <Card
toggleDirection={toggleDirection} toggleDirection={toggleDirection}
toggleLabels={toggleLabels} toggleLabels={toggleLabels}
isPublic={isPublic}
labelVariant={cardLabelVariant} labelVariant={cardLabelVariant}
wrapperProps={{ wrapperProps={{
...taskProvided.draggableProps, ...taskProvided.draggableProps,
@ -396,11 +400,13 @@ const SimpleLists: React.FC<SimpleProps> = ({
)} )}
</Droppable> </Droppable>
</DragDropContext> </DragDropContext>
<AddList {!isPublic && (
onSave={listName => { <AddList
onCreateTaskGroup(listName); onSave={listName => {
}} onCreateTaskGroup(listName);
/> }}
/>
)}
</BoardWrapper> </BoardWrapper>
</BoardContainer> </BoardContainer>
); );

View File

@ -38,12 +38,17 @@ export const ListSeparator = styled.hr`
`; `;
type Props = { type Props = {
publicOn: null | string;
onDeleteProject: () => void; onDeleteProject: () => void;
onToggleProjectVisible: (visible: boolean) => void;
}; };
const ProjectSettings: React.FC<Props> = ({ onDeleteProject }) => { const ProjectSettings: React.FC<Props> = ({ publicOn, onDeleteProject, onToggleProjectVisible }) => {
return ( return (
<> <>
<ListActionsWrapper> <ListActionsWrapper>
<ListActionItemWrapper onClick={() => onToggleProjectVisible(publicOn === null)}>
<ListActionItem>{`Make ${publicOn === null ? 'public' : 'private'}`}</ListActionItem>
</ListActionItemWrapper>
<ListActionItemWrapper onClick={() => onDeleteProject()}> <ListActionItemWrapper onClick={() => onDeleteProject()}>
<ListActionItem>Delete Project</ListActionItem> <ListActionItem>Delete Project</ListActionItem>
</ListActionItemWrapper> </ListActionItemWrapper>
@ -127,5 +132,18 @@ const DeleteConfirm: React.FC<DeleteConfirmProps> = ({ description, deletedItems
); );
}; };
export { DeleteConfirm }; type PublicConfirmProps = {
onConfirm: () => void;
};
const PublicConfirm: React.FC<PublicConfirmProps> = ({ onConfirm }) => {
return (
<ConfirmWrapper>
<ConfirmDescription>Public projects can be accessed by anyone with a link to the project.</ConfirmDescription>
<ConfirmDeleteButton onClick={() => onConfirm()}>Make public</ConfirmDeleteButton>
</ConfirmWrapper>
);
};
export { DeleteConfirm, PublicConfirm };
export default ProjectSettings; export default ProjectSettings;

View File

@ -69,7 +69,7 @@ const CommentCreator: React.FC<CommentCreatorProps> = ({
)} )}
<CommentEditorContainer> <CommentEditorContainer>
<CommentTextArea <CommentTextArea
showCommentActions={showCommentActions} $showCommentActions={showCommentActions}
placeholder="Write a comment..." placeholder="Write a comment..."
ref={$comment} ref={$comment}
disabled={disabled} disabled={disabled}

View File

@ -80,40 +80,44 @@ import {
ActivityItemHeaderTitleName, ActivityItemHeaderTitleName,
ActivityItemComment, ActivityItemComment,
} from './Styles'; } from './Styles';
import { useCurrentUser } from 'App/context';
type TaskDetailsProps = {}; type TaskDetailsProps = {};
const TaskDetailsLoading: React.FC<TaskDetailsProps> = () => { const TaskDetailsLoading: React.FC<TaskDetailsProps> = () => {
const { user } = useCurrentUser();
return ( return (
<Container> <Container>
<LeftSidebar> <LeftSidebar>
<LeftSidebarContent> <LeftSidebarContent>
<LeftSidebarSection> <LeftSidebarSection>
<SidebarTitle>TASK GROUP</SidebarTitle> <SidebarTitle>TASK GROUP</SidebarTitle>
<SidebarButton loading> <SidebarButton $loading>
<SidebarSkeleton /> <SidebarSkeleton />
</SidebarButton> </SidebarButton>
<DueDateTitle>DUE DATE</DueDateTitle> <DueDateTitle>DUE DATE</DueDateTitle>
<SidebarButton loading> <SidebarButton $loading>
<SidebarSkeleton /> <SidebarSkeleton />
</SidebarButton> </SidebarButton>
</LeftSidebarSection> </LeftSidebarSection>
<AssignedUsersSection> <AssignedUsersSection>
<DueDateTitle>MEMBERS</DueDateTitle> <DueDateTitle>MEMBERS</DueDateTitle>
<SidebarButton loading> <SidebarButton $loading>
<SidebarSkeleton /> <SidebarSkeleton />
</SidebarButton> </SidebarButton>
</AssignedUsersSection> </AssignedUsersSection>
<ExtraActionsSection> {user && (
<DueDateTitle>ACTIONS</DueDateTitle> <ExtraActionsSection>
<ActionButton disabled icon={<Tags width={12} height={12} />}> <DueDateTitle>ACTIONS</DueDateTitle>
Labels <ActionButton disabled icon={<Tags width={12} height={12} />}>
</ActionButton> Labels
<ActionButton disabled icon={<CheckSquareOutline width={12} height={12} />}> </ActionButton>
Checklist <ActionButton disabled icon={<CheckSquareOutline width={12} height={12} />}>
</ActionButton> Checklist
<ActionButton disabled>Cover</ActionButton> </ActionButton>
</ExtraActionsSection> <ActionButton disabled>Cover</ActionButton>
</ExtraActionsSection>
)}
</LeftSidebarContent> </LeftSidebarContent>
</LeftSidebar> </LeftSidebar>
<ContentContainer> <ContentContainer>
@ -125,23 +129,25 @@ const TaskDetailsLoading: React.FC<TaskDetailsProps> = () => {
<span>Mark complete</span> <span>Mark complete</span>
</MarkCompleteButton> </MarkCompleteButton>
</HeaderLeft> </HeaderLeft>
<HeaderRight> {user && (
<HeaderActionIcon> <HeaderRight>
<Paperclip width={16} height={16} /> <HeaderActionIcon>
</HeaderActionIcon> <Paperclip width={16} height={16} />
<HeaderActionIcon> </HeaderActionIcon>
<Clone width={16} height={16} /> <HeaderActionIcon>
</HeaderActionIcon> <Clone width={16} height={16} />
<HeaderActionIcon> </HeaderActionIcon>
<Share width={16} height={16} /> <HeaderActionIcon>
</HeaderActionIcon> <Share width={16} height={16} />
<HeaderActionIcon> </HeaderActionIcon>
<Trash width={16} height={16} /> <HeaderActionIcon>
</HeaderActionIcon> <Trash width={16} height={16} />
</HeaderRight> </HeaderActionIcon>
</HeaderRight>
)}
</HeaderInnerContainer> </HeaderInnerContainer>
<TaskDetailsTitleWrapper loading> <TaskDetailsTitleWrapper $loading>
<TaskDetailsTitle value="" disabled loading /> <TaskDetailsTitle value="" disabled $loading />
</TaskDetailsTitleWrapper> </TaskDetailsTitleWrapper>
</HeaderContainer> </HeaderContainer>
<InnerContentContainer> <InnerContentContainer>
@ -151,9 +157,11 @@ const TaskDetailsLoading: React.FC<TaskDetailsProps> = () => {
</TabBarSection> </TabBarSection>
<ActivitySection /> <ActivitySection />
</InnerContentContainer> </InnerContentContainer>
<CommentContainer> {user && (
<CommentCreator disabled onCreateComment={() => null} onMemberProfile={() => null} /> <CommentContainer>
</CommentContainer> <CommentCreator disabled onCreateComment={() => null} onMemberProfile={() => null} />
</CommentContainer>
)}
</ContentContainer> </ContentContainer>
</Container> </Container>
); );

View File

@ -108,7 +108,7 @@ export const skeletonKeyframes = keyframes`
} }
`; `;
export const SidebarButton = styled.div<{ loading?: boolean }>` export const SidebarButton = styled.div<{ $loading?: boolean }>`
font-size: 14px; font-size: 14px;
color: ${props => props.theme.colors.text.primary}; color: ${props => props.theme.colors.text.primary};
min-height: 32px; min-height: 32px;
@ -116,7 +116,7 @@ export const SidebarButton = styled.div<{ loading?: boolean }>`
border-radius: 6px; border-radius: 6px;
${props => ${props =>
props.loading props.$loading
? css` ? css`
background: ${props.theme.colors.bg.primary}; background: ${props.theme.colors.bg.primary};
` `
@ -178,15 +178,15 @@ export const HeaderLeft = styled.div`
justify-content: flex-start; justify-content: flex-start;
`; `;
export const TaskDetailsTitleWrapper = styled.div<{ loading?: boolean }>` export const TaskDetailsTitleWrapper = styled.div<{ $loading?: boolean }>`
width: 100%; width: 100%;
margin: 8px 0 4px 0; margin: 8px 0 4px 0;
display: flex; display: flex;
border-radius: 6px; border-radius: 6px;
${props => props.loading && `background: ${props.theme.colors.bg.primary};`} ${props => props.$loading && `background: ${props.theme.colors.bg.primary};`}
`; `;
export const TaskDetailsTitle = styled(TextareaAutosize)<{ loading?: boolean }>` export const TaskDetailsTitle = styled(TextareaAutosize)<{ $loading?: boolean }>`
padding: 9px 8px 7px 8px; padding: 9px 8px 7px 8px;
border-color: transparent; border-color: transparent;
border-radius: 6px; border-radius: 6px;
@ -198,8 +198,11 @@ export const TaskDetailsTitle = styled(TextareaAutosize)<{ loading?: boolean }>`
font-weight: 700; font-weight: 700;
background: none; background: none;
&:disabled {
opacity: 1;
}
${props => ${props =>
props.loading props.$loading
? css` ? css`
background-image: linear-gradient(90deg, ${defaultBaseColor}, ${defaultHighlightColor}, ${defaultBaseColor}); background-image: linear-gradient(90deg, ${defaultBaseColor}, ${defaultHighlightColor}, ${defaultBaseColor});
background-size: 200px 100%; background-size: 200px 100%;
@ -207,7 +210,7 @@ export const TaskDetailsTitle = styled(TextareaAutosize)<{ loading?: boolean }>`
animation: ${skeletonKeyframes} 1.2s ease-in-out infinite; animation: ${skeletonKeyframes} 1.2s ease-in-out infinite;
` `
: css` : css`
&:hover { &:not(:disabled):hover {
border-color: #414561; border-color: #414561;
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
@ -534,7 +537,7 @@ export const CommentProfile = styled(TaskAssignee)`
align-items: normal; align-items: normal;
`; `;
export const CommentTextArea = styled(TextareaAutosize)<{ showCommentActions: boolean }>` export const CommentTextArea = styled(TextareaAutosize)<{ $showCommentActions: boolean }>`
width: 100%; width: 100%;
line-height: 28px; line-height: 28px;
padding: 4px 6px; padding: 4px 6px;
@ -546,7 +549,7 @@ export const CommentTextArea = styled(TextareaAutosize)<{ showCommentActions: bo
min-height: 36px; min-height: 36px;
max-height: 36px; max-height: 36px;
${props => ${props =>
props.showCommentActions props.$showCommentActions
? css` ? css`
min-height: 80px; min-height: 80px;
max-height: none; max-height: none;

View File

@ -83,6 +83,7 @@ import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist';
import onDragEnd from './onDragEnd'; import onDragEnd from './onDragEnd';
import { plugin as em } from './remark'; import { plugin as em } from './remark';
import ActivityMessage from './ActivityMessage'; import ActivityMessage from './ActivityMessage';
import { useCurrentUser } from 'App/context';
const parseEmojis = (value: string) => { const parseEmojis = (value: string) => {
const emojisArray = toArray(value); const emojisArray = toArray(value);
@ -277,6 +278,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
onToggleChecklistItem, onToggleChecklistItem,
onMemberProfile, onMemberProfile,
}) => { }) => {
const { user } = useCurrentUser();
const [taskName, setTaskName] = useState(task.name); const [taskName, setTaskName] = useState(task.name);
const [editTaskDescription, setEditTaskDescription] = useState(() => { const [editTaskDescription, setEditTaskDescription] = useState(() => {
if (task.description) { if (task.description) {
@ -338,7 +340,9 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
<SidebarButton <SidebarButton
ref={$dueDateBtn} ref={$dueDateBtn}
onClick={() => { onClick={() => {
onOpenDueDatePopop(task, $dueDateBtn); if (user) {
onOpenDueDatePopop(task, $dueDateBtn);
}
}} }}
> >
{task.dueDate ? ( {task.dueDate ? (
@ -360,14 +364,18 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
member={m} member={m}
size={32} size={32}
onMemberProfile={$target => { onMemberProfile={$target => {
onMemberProfile($target, m.id); if (user) {
onMemberProfile($target, m.id);
}
}} }}
/> />
))} ))}
<AssignUserIcon <AssignUserIcon
ref={$addMemberBtn} ref={$addMemberBtn}
onClick={() => { onClick={() => {
onOpenAddMemberPopup(task, $addMemberBtn); if (user) {
onOpenAddMemberPopup(task, $addMemberBtn);
}
}} }}
> >
<Plus width={16} height={16} /> <Plus width={16} height={16} />
@ -377,7 +385,9 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
<AssignUsersButton <AssignUsersButton
ref={$noMemberBtn} ref={$noMemberBtn}
onClick={() => { onClick={() => {
onOpenAddMemberPopup(task, $noMemberBtn); if (user) {
onOpenAddMemberPopup(task, $noMemberBtn);
}
}} }}
> >
<AssignUserIcon> <AssignUserIcon>
@ -387,26 +397,28 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
</AssignUsersButton> </AssignUsersButton>
)} )}
</AssignedUsersSection> </AssignedUsersSection>
<ExtraActionsSection> {user && (
<DueDateTitle>ACTIONS</DueDateTitle> <ExtraActionsSection>
<ActionButton <DueDateTitle>ACTIONS</DueDateTitle>
onClick={$target => { <ActionButton
onOpenAddLabelPopup(task, $target); onClick={$target => {
}} onOpenAddLabelPopup(task, $target);
icon={<Tags width={12} height={12} />} }}
> icon={<Tags width={12} height={12} />}
Labels >
</ActionButton> Labels
<ActionButton </ActionButton>
onClick={$target => { <ActionButton
onOpenAddChecklistPopup(task, $target); onClick={$target => {
}} onOpenAddChecklistPopup(task, $target);
icon={<CheckSquareOutline width={12} height={12} />} }}
> icon={<CheckSquareOutline width={12} height={12} />}
Checklist >
</ActionButton> Checklist
<ActionButton>Cover</ActionButton> </ActionButton>
</ExtraActionsSection> <ActionButton>Cover</ActionButton>
</ExtraActionsSection>
)}
</LeftSidebarContent> </LeftSidebarContent>
</LeftSidebar> </LeftSidebar>
<ContentContainer> <ContentContainer>
@ -414,34 +426,40 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
<HeaderInnerContainer> <HeaderInnerContainer>
<HeaderLeft> <HeaderLeft>
<MarkCompleteButton <MarkCompleteButton
disabled={user === null}
invert={task.complete ?? false} invert={task.complete ?? false}
onClick={() => { onClick={() => {
onToggleTaskComplete(task); if (user) {
onToggleTaskComplete(task);
}
}} }}
> >
<Checkmark width={8} height={8} /> <Checkmark width={8} height={8} />
<span>{task.complete ? 'Completed' : 'Mark complete'}</span> <span>{task.complete ? 'Completed' : 'Mark complete'}</span>
</MarkCompleteButton> </MarkCompleteButton>
</HeaderLeft> </HeaderLeft>
<HeaderRight> {user && (
<HeaderActionIcon> <HeaderRight>
<Paperclip width={16} height={16} /> <HeaderActionIcon>
</HeaderActionIcon> <Paperclip width={16} height={16} />
<HeaderActionIcon> </HeaderActionIcon>
<Clone width={16} height={16} /> <HeaderActionIcon>
</HeaderActionIcon> <Clone width={16} height={16} />
<HeaderActionIcon> </HeaderActionIcon>
<Share width={16} height={16} /> <HeaderActionIcon>
</HeaderActionIcon> <Share width={16} height={16} />
<HeaderActionIcon onClick={() => onDeleteTask(task)}> </HeaderActionIcon>
<Trash width={16} height={16} /> <HeaderActionIcon onClick={() => onDeleteTask(task)}>
</HeaderActionIcon> <Trash width={16} height={16} />
</HeaderRight> </HeaderActionIcon>
</HeaderRight>
)}
</HeaderInnerContainer> </HeaderInnerContainer>
<TaskDetailsTitleWrapper> <TaskDetailsTitleWrapper>
<TaskDetailsTitle <TaskDetailsTitle
value={taskName} value={taskName}
ref={$detailsTitle} ref={$detailsTitle}
disabled={user === null}
onKeyDown={e => { onKeyDown={e => {
if (e.keyCode === 13) { if (e.keyCode === 13) {
e.preventDefault(); e.preventDefault();
@ -496,7 +514,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
<Editor <Editor
defaultValue={task.description ?? ''} defaultValue={task.description ?? ''}
theme={dark} theme={dark}
readOnly={!editTaskDescription} readOnly={user === null || !editTaskDescription}
autoFocus autoFocus
onChange={value => { onChange={value => {
setSaveTimeout(() => { setSaveTimeout(() => {
@ -612,15 +630,15 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
)} )}
</ActivitySection> </ActivitySection>
</InnerContentContainer> </InnerContentContainer>
<CommentContainer> {me && (
{me && ( <CommentContainer>
<CommentCreator <CommentCreator
me={me} me={me}
onCreateComment={message => onCreateComment(task, message)} onCreateComment={message => onCreateComment(task, message)}
onMemberProfile={onMemberProfile} onMemberProfile={onMemberProfile}
/> />
)} </CommentContainer>
</CommentContainer> )}
</ContentContainer> </ContentContainer>
</Container> </Container>
); );

View File

@ -0,0 +1,63 @@
import React, { useRef, useState, useEffect } from 'react';
import { Home, Star, Bell, AngleDown, BarChart, CheckCircle, ListUnordered } from 'shared/icons';
import { RoleCode } from 'shared/generated/graphql';
import * as S from './Styles';
import { Link } from 'react-router-dom';
export type MenuItem = {
name: string;
link: string;
};
type NavBarProps = {
menuType?: Array<MenuItem> | null;
name: string | null;
};
const NavBar: React.FC<NavBarProps> = ({ menuType, name }) => {
return (
<S.NavbarWrapper>
<S.NavbarHeader>
<S.ProjectActions>
<S.ProjectSwitch>
<S.ProjectSwitchInner>
<S.TaskcafeLogo innerColor="#9f46e4" outerColor="#000" width={32} height={32} />
</S.ProjectSwitchInner>
</S.ProjectSwitch>
<S.ProjectInfo>
<S.ProjectMeta>{name && <S.ProjectName>{name}</S.ProjectName>}</S.ProjectMeta>
{name && (
<S.ProjectTabs>
{menuType &&
menuType.map((menu, idx) => {
return (
<S.ProjectTab
key={menu.name}
to={menu.link}
exact
onClick={() => {
// TODO
}}
>
{menu.name}
</S.ProjectTab>
);
})}
</S.ProjectTabs>
)}
</S.ProjectInfo>
</S.ProjectActions>
<S.LogoContainer to="/">
<S.TaskcafeTitle>Taskcafé</S.TaskcafeTitle>
</S.LogoContainer>
<S.GlobalActions>
<Link to="/login">
<S.SignIn>Sign In</S.SignIn>
</Link>
</S.GlobalActions>
</S.NavbarHeader>
</S.NavbarWrapper>
);
};
export default NavBar;

View File

@ -297,6 +297,16 @@ export const ProjectFinder = styled(Button)`
padding: 6px 12px; padding: 6px 12px;
`; `;
export const SignUp = styled(Button)`
margin-right: 8px;
padding: 6px 12px;
`;
export const SignIn = styled(Button)`
margin-right: 20px;
padding: 6px 12px;
`;
export const NavSeparator = styled.div` export const NavSeparator = styled.div`
width: 1px; width: 1px;
background: ${props => props.theme.colors.border}; background: ${props => props.theme.colors.border};

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ const FIND_PROJECT_QUERY = gql`
query findProject($projectID: UUID!) { query findProject($projectID: UUID!) {
findProject(input: { projectID: $projectID }) { findProject(input: { projectID: $projectID }) {
name name
publicOn
team { team {
id id
} }

View File

@ -0,0 +1,14 @@
import gql from 'graphql-tag';
export const DELETE_PROJECT_MUTATION = gql`
mutation toggleProjectVisibility($projectID: UUID!, $isPublic: Boolean!) {
toggleProjectVisibility(input: { projectID: $projectID, isPublic: $isPublic }) {
project {
id
publicOn
}
}
}
`;
export default DELETE_PROJECT_MUTATION;

View File

@ -39,11 +39,6 @@
ts-invariant "^0.4.4" ts-invariant "^0.4.4"
tslib "^1.10.0" tslib "^1.10.0"
"@ardatan/aggregate-error@0.0.1":
version "0.0.1"
resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.1.tgz#1403ac5de10d8ca689fc1f65844c27179ae1d44f"
integrity sha512-UQ9BequOTIavs0pTHLMwQwKQF8tTV1oezY/H2O9chA+JNPFZSua55xpU5dPSjAU9/jLJ1VwU+HJuTVN8u7S6Fg==
"@ardatan/aggregate-error@0.0.6": "@ardatan/aggregate-error@0.0.6":
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609"
@ -1349,13 +1344,13 @@
ts-node "^9" ts-node "^9"
tslib "^2" tslib "^2"
"@graphql-codegen/cli@^1.13.2": "@graphql-codegen/cli@^1.21.4":
version "1.19.4" version "1.21.4"
resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-1.19.4.tgz#16e5caaa46ee159cbb01e2a2699203f8f25faac2" resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-1.21.4.tgz#41ce6abc6b33e369a3ee795621373b8ffa1aadeb"
integrity sha512-PN1xgaXZaTTEPhBfQkIRK82RzrzY8Pr+tfCqOtibeJMed0mtIJ5gSQfollRbtZ0/t7ZEmYCxojqQiVSbKzXDMw== integrity sha512-fCrPn7DeGnCU6d9xjo04VSzIGioVLj1BAjuZpgRJNNjyzTmv+qZJJVTWfD55J17XxlhFsfKJBprtdmRxZ3V2hw==
dependencies: dependencies:
"@graphql-codegen/core" "1.17.9" "@graphql-codegen/core" "1.17.9"
"@graphql-codegen/plugin-helpers" "^1.18.2" "@graphql-codegen/plugin-helpers" "^1.18.5"
"@graphql-tools/apollo-engine-loader" "^6" "@graphql-tools/apollo-engine-loader" "^6"
"@graphql-tools/code-file-loader" "^6" "@graphql-tools/code-file-loader" "^6"
"@graphql-tools/git-loader" "^6" "@graphql-tools/git-loader" "^6"
@ -1365,16 +1360,15 @@
"@graphql-tools/load" "^6" "@graphql-tools/load" "^6"
"@graphql-tools/prisma-loader" "^6" "@graphql-tools/prisma-loader" "^6"
"@graphql-tools/url-loader" "^6" "@graphql-tools/url-loader" "^6"
"@graphql-tools/utils" "^6" "@graphql-tools/utils" "^7.0.0"
ansi-escapes "^4.3.1" ansi-escapes "^4.3.1"
camel-case "^4.1.1"
chalk "^4.1.0" chalk "^4.1.0"
change-case-all "1.0.14"
chokidar "^3.4.3" chokidar "^3.4.3"
common-tags "^1.8.0" common-tags "^1.8.0"
constant-case "^3.0.3"
cosmiconfig "^7.0.0" cosmiconfig "^7.0.0"
debounce "^1.2.0" debounce "^1.2.0"
dependency-graph "^0.9.0" dependency-graph "^0.11.0"
detect-indent "^6.0.0" detect-indent "^6.0.0"
glob "^7.1.6" glob "^7.1.6"
graphql-config "^3.2.0" graphql-config "^3.2.0"
@ -1386,17 +1380,14 @@
listr "^0.14.3" listr "^0.14.3"
listr-update-renderer "^0.5.0" listr-update-renderer "^0.5.0"
log-symbols "^4.0.0" log-symbols "^4.0.0"
lower-case "^2.0.1"
minimatch "^3.0.4" minimatch "^3.0.4"
mkdirp "^1.0.4" mkdirp "^1.0.4"
pascal-case "^3.1.1"
request "^2.88.2"
string-env-interpolation "^1.0.1" string-env-interpolation "^1.0.1"
ts-log "^2.2.3" ts-log "^2.2.3"
tslib "~2.0.1" tslib "~2.2.0"
upper-case "^2.0.1"
valid-url "^1.0.9" valid-url "^1.0.9"
wrap-ansi "^7.0.0" wrap-ansi "^7.0.0"
yaml "^1.10.0"
yargs "^16.1.1" yargs "^16.1.1"
"@graphql-codegen/core@1.17.9": "@graphql-codegen/core@1.17.9":
@ -1409,23 +1400,6 @@
"@graphql-tools/utils" "^6" "@graphql-tools/utils" "^6"
tslib "~2.0.1" tslib "~2.0.1"
"@graphql-codegen/plugin-helpers@1.17.7":
version "1.17.7"
resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.17.7.tgz#5903105fda9470aafefe9da29e3a6fb3a52b8376"
integrity sha512-LsXS0s/ZOACZXa3W29ekcaQLzP8TsYzow6nIjW6rtkWX5T0EDooBQbDn1cdLdlpenqbUU+vtONwR6Qqc6hrq2Q==
dependencies:
"@graphql-tools/utils" "^6.0.0"
camel-case "4.1.1"
common-tags "1.8.0"
constant-case "3.0.3"
import-from "3.0.0"
lodash "~4.17.15"
lower-case "2.0.1"
param-case "3.0.3"
pascal-case "3.1.1"
tslib "~2.0.0"
upper-case "2.0.1"
"@graphql-codegen/plugin-helpers@^1.18.2": "@graphql-codegen/plugin-helpers@^1.18.2":
version "1.18.2" version "1.18.2"
resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.2.tgz#57011076cb8b8f5d04d37d226a5eda300c01be94" resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.2.tgz#57011076cb8b8f5d04d37d226a5eda300c01be94"
@ -1443,69 +1417,64 @@
tslib "~2.0.1" tslib "~2.0.1"
upper-case "2.0.1" upper-case "2.0.1"
"@graphql-codegen/typescript-operations@^1.13.2": "@graphql-codegen/plugin-helpers@^1.18.5":
version "1.17.12" version "1.18.5"
resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-1.17.12.tgz#25e0f0ede1bcd165196f0c161286d745395c7768" resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.5.tgz#e1d875cfb6a2f7bf4b4318135f7fee6e1200f3b0"
integrity sha512-UXe/O3kNAlLptURuripAhK8n/Gf6FthL7xr6sG4vpuLVwrWjXu/KlS7+TP+UvlI9OH2LICm0KHqA0xoFCjNbMg== integrity sha512-xY8dWdU4+mm+253esLYcKQIgWZEgI3spKPmMRQ+oAxlrCn9oIcdhhiMqNxa9rHS20xpZtlAjozxHulrqjFLuyA==
dependencies: dependencies:
"@graphql-codegen/plugin-helpers" "^1.18.2" "@graphql-tools/utils" "^7.0.0"
"@graphql-codegen/typescript" "^1.18.1" common-tags "1.8.0"
"@graphql-codegen/visitor-plugin-common" "^1.17.20" import-from "3.0.0"
auto-bind "~4.0.0" lodash "~4.17.20"
tslib "~2.0.1" tslib "~2.2.0"
"@graphql-codegen/typescript-react-apollo@^1.13.2": "@graphql-codegen/typescript-operations@^1.17.16":
version "1.17.7" version "1.17.16"
resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-1.17.7.tgz#b1d1ad1150c07097566f46c208ca54d42a8ad4f5" resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-1.17.16.tgz#75eb389f268b2dbd2e46b235bcb957be561c31cb"
integrity sha512-Yoc7XwruETJ1Aoi5UBLF+AVsyReGPxI9jEpsNWv/Xm5zzGGk8iVwKIwos9hBjzkDO0sQooZZa0UXBKXNQa7j0w== integrity sha512-DoWIhg/c2XS4IpgRLRXRBJDwmKoIl6KuzL2iO9GElX0rdjYguwbqx6iDV1pgktTTajMy8pXP56oZhmEcZLRD2Q==
dependencies: dependencies:
"@graphql-codegen/plugin-helpers" "1.17.7" "@graphql-codegen/plugin-helpers" "^1.18.5"
"@graphql-codegen/visitor-plugin-common" "1.17.7" "@graphql-codegen/typescript" "^1.22.0"
"@graphql-codegen/visitor-plugin-common" "^1.20.0"
auto-bind "~4.0.0" auto-bind "~4.0.0"
camel-case "4.1.1" tslib "~2.2.0"
pascal-case "3.1.1"
tslib "~2.0.0"
"@graphql-codegen/typescript@^1.13.2", "@graphql-codegen/typescript@^1.18.1": "@graphql-codegen/typescript-react-apollo@^2.2.4":
version "1.19.0" version "2.2.4"
resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.19.0.tgz#05b1b4502b91dee53ec4f0ae39e9537eff004c5c" resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-2.2.4.tgz#149b8e4f2b684295a2e6be8b9e88285a0f8be0f6"
integrity sha512-ThpMdtb6LmEKzLNxlotpk33eIMfX1i1aAlYDaqctIhFjZ2Rbvkgdb8q/5/my7yeH33BLnuqHDKrdkDsAAvCHcw== integrity sha512-2db+KrujuBjQ8GraamEPGEp74Bxcw6Vd2AubhqenoTmHjK/Jw3HLpO8ndetcoND08e3EZCb/CNyKkLcxwYD0eg==
dependencies: dependencies:
"@graphql-codegen/plugin-helpers" "^1.18.2" "@graphql-codegen/plugin-helpers" "^1.18.5"
"@graphql-codegen/visitor-plugin-common" "^1.17.21" "@graphql-codegen/visitor-plugin-common" "^1.20.0"
auto-bind "~4.0.0" auto-bind "~4.0.0"
tslib "~2.0.1" change-case-all "1.0.14"
tslib "~2.2.0"
"@graphql-codegen/visitor-plugin-common@1.17.7": "@graphql-codegen/typescript@^1.22.0":
version "1.17.7" version "1.22.0"
resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.17.7.tgz#3158ca4fc7d45a0f5a6ad0706061015eae481d9e" resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.22.0.tgz#d05be3a971e5d75a076a43e123b6330f4366a6ab"
integrity sha512-z5WvYqgCgPAAuMJMOE0e0nEicdaQRm59/vP+yYihKQmwrASzPlqa1BUiGDnfrPcCooB9fEUHB+cQb3gt9GRfEg== integrity sha512-YzN/3MBYHrP110m8JgUWQIHt7Ivi3JXiq0RT5XNx/F9mVOSbZz6Ezbaji8YJA3y04Gl2f6ZgtdGazWANUvcOcg==
dependencies: dependencies:
"@graphql-codegen/plugin-helpers" "1.17.7" "@graphql-codegen/plugin-helpers" "^1.18.5"
"@graphql-tools/relay-operation-optimizer" "6.0.15" "@graphql-codegen/visitor-plugin-common" "^1.20.0"
array.prototype.flatmap "1.2.3"
auto-bind "~4.0.0" auto-bind "~4.0.0"
dependency-graph "0.9.0" tslib "~2.2.0"
graphql-tag "2.11.0"
parse-filepath "1.0.2"
pascal-case "3.1.1"
tslib "~2.0.0"
"@graphql-codegen/visitor-plugin-common@^1.17.20", "@graphql-codegen/visitor-plugin-common@^1.17.21": "@graphql-codegen/visitor-plugin-common@^1.20.0":
version "1.17.21" version "1.20.0"
resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.17.21.tgz#e47a5623392bcdb89d5af2484798a2fd9a59df8c" resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.20.0.tgz#38d829eab7370c79aa5229190788f94adcae8f76"
integrity sha512-rhdJdj+4DAMgMSBlgkvfOMw65L46sAtYb7G/n9ucc9uJGUbiIcgvS/wpoQ/gxz2eUaNfcoFmjVLm+dkTM/XwqA== integrity sha512-AYrpy8NA3DpvhDLqYGerQRv44S+YAMPKtwT8x9GNVjzP0gVfmqi3gG1bDWbP5sm6kOZKvDC0kTxGePuBSZerxw==
dependencies: dependencies:
"@graphql-codegen/plugin-helpers" "^1.18.2" "@graphql-codegen/plugin-helpers" "^1.18.5"
"@graphql-tools/optimize" "^1.0.1" "@graphql-tools/optimize" "^1.0.1"
"@graphql-tools/relay-operation-optimizer" "^6" "@graphql-tools/relay-operation-optimizer" "^6"
array.prototype.flatmap "^1.2.4" array.prototype.flatmap "^1.2.4"
auto-bind "~4.0.0" auto-bind "~4.0.0"
dependency-graph "^0.9.0" change-case-all "1.0.14"
dependency-graph "^0.11.0"
graphql-tag "^2.11.0" graphql-tag "^2.11.0"
parse-filepath "^1.0.2" parse-filepath "^1.0.2"
pascal-case "^3.1.1" tslib "~2.2.0"
tslib "~2.0.1"
"@graphql-tools/apollo-engine-loader@^6": "@graphql-tools/apollo-engine-loader@^6":
version "6.2.5" version "6.2.5"
@ -1665,14 +1634,6 @@
tslib "~2.0.1" tslib "~2.0.1"
yaml-ast-parser "^0.0.43" yaml-ast-parser "^0.0.43"
"@graphql-tools/relay-operation-optimizer@6.0.15":
version "6.0.15"
resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.0.15.tgz#f991499c54945cb8fa2396bb5292252fbda0a773"
integrity sha512-Y4h2kclKh5HvyvmoxeZiDhqdhMKfLKamOYx6UVpFsbeKb6Tt9RCNPVhpa+YToXxUXl0PvjhxZWeQ4lZY0GE0ug==
dependencies:
"@graphql-tools/utils" "6.0.15"
relay-compiler "10.0.0"
"@graphql-tools/relay-operation-optimizer@^6": "@graphql-tools/relay-operation-optimizer@^6":
version "6.3.0" version "6.3.0"
resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz#f8c7f6c8aa4a9cf50ab151fbc5db4f4282a79532" resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz#f8c7f6c8aa4a9cf50ab151fbc5db4f4282a79532"
@ -1713,14 +1674,6 @@
valid-url "1.0.9" valid-url "1.0.9"
ws "7.4.1" ws "7.4.1"
"@graphql-tools/utils@6.0.15":
version "6.0.15"
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.0.15.tgz#6d54d383285bea3c22797531933b62a408e78e49"
integrity sha512-VG5cMLPgh9RDLGHamGpXVnBrNw7bZGT46LrxK7IIqDZI9H0GPsRCo8+p+CfDkw0IlDiEECb624WVCpm9IYNecA==
dependencies:
"@ardatan/aggregate-error" "0.0.1"
camel-case "4.1.1"
"@graphql-tools/utils@^6", "@graphql-tools/utils@^6.0.0": "@graphql-tools/utils@^6", "@graphql-tools/utils@^6.0.0":
version "6.2.4" version "6.2.4"
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.2.4.tgz#38a2314d2e5e229ad4f78cca44e1199e18d55856" resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.2.4.tgz#38a2314d2e5e229ad4f78cca44e1199e18d55856"
@ -2413,10 +2366,10 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
"@types/styled-components@^5.0.0": "@types/styled-components@^5.1.0":
version "5.1.7" version "5.1.9"
resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.7.tgz#3cd10b088c1cb1acde2e4b166b3e8275a3083710" resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.9.tgz#00d3d84b501420521c4db727e3c195459f87a6cf"
integrity sha512-BJzPhFygYspyefAGFZTZ/8lCEY4Tk+Iqktvnko3xmJf9LrLqs3+grxPeU3O0zLl6yjbYBopD0/VikbHgXDbJtA== integrity sha512-kbEG6YlwK8rucITpKEr6pA4Ho9KSQHUUOzZ9lY3va1mtcjvS3D0wDciFyHEiNHKLL/npZCKDQJqm0x44sPO9oA==
dependencies: dependencies:
"@types/hoist-non-react-statics" "*" "@types/hoist-non-react-statics" "*"
"@types/react" "*" "@types/react" "*"
@ -3076,15 +3029,6 @@ array.prototype.flat@^1.2.1, array.prototype.flat@^1.2.3:
define-properties "^1.1.3" define-properties "^1.1.3"
es-abstract "^1.18.0-next.1" es-abstract "^1.18.0-next.1"
array.prototype.flatmap@1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.3.tgz#1c13f84a178566042dd63de4414440db9222e443"
integrity sha512-OOEk+lkePcg+ODXIpvuU9PAryCikCJyo7GlDG1upleEpQRx6mzL9puEBkozQ5iAx20KV0l3DbyQwqciJtqe5Pg==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.0-next.1"
function-bind "^1.1.1"
array.prototype.flatmap@^1.2.3, array.prototype.flatmap@^1.2.4: array.prototype.flatmap@^1.2.3, array.prototype.flatmap@^1.2.4:
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9"
@ -3881,7 +3825,7 @@ camel-case@4.1.1:
pascal-case "^3.1.1" pascal-case "^3.1.1"
tslib "^1.10.0" tslib "^1.10.0"
camel-case@4.1.2, camel-case@^4.1.1: camel-case@4.1.2, camel-case@^4.1.1, camel-case@^4.1.2:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
@ -3919,6 +3863,15 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, can
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz#6fcd098c139d003b9bd484cbb9ca26cb89907f9a" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz#6fcd098c139d003b9bd484cbb9ca26cb89907f9a"
integrity sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ== integrity sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ==
capital-case@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case-first "^2.0.2"
capture-exit@^2.0.0: capture-exit@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4"
@ -3964,6 +3917,40 @@ chalk@^4.0.0, chalk@^4.1.0:
ansi-styles "^4.1.0" ansi-styles "^4.1.0"
supports-color "^7.1.0" supports-color "^7.1.0"
change-case-all@1.0.14:
version "1.0.14"
resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.14.tgz#bac04da08ad143278d0ac3dda7eccd39280bfba1"
integrity sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA==
dependencies:
change-case "^4.1.2"
is-lower-case "^2.0.2"
is-upper-case "^2.0.2"
lower-case "^2.0.2"
lower-case-first "^2.0.2"
sponge-case "^1.0.1"
swap-case "^2.0.2"
title-case "^3.0.3"
upper-case "^2.0.2"
upper-case-first "^2.0.2"
change-case@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12"
integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==
dependencies:
camel-case "^4.1.2"
capital-case "^1.0.4"
constant-case "^3.0.4"
dot-case "^3.0.4"
header-case "^2.0.4"
no-case "^3.0.4"
param-case "^3.0.4"
pascal-case "^3.1.2"
path-case "^3.0.4"
sentence-case "^3.0.4"
snake-case "^3.0.4"
tslib "^2.0.3"
character-entities-legacy@^1.0.0: character-entities-legacy@^1.0.0:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1"
@ -4369,7 +4356,7 @@ constant-case@3.0.3:
tslib "^1.10.0" tslib "^1.10.0"
upper-case "^2.0.1" upper-case "^2.0.1"
constant-case@^3.0.3: constant-case@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1"
integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==
@ -4459,7 +4446,7 @@ core-js-pure@^3.0.0:
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.1.tgz#23f84048f366fdfcf52d3fd1c68fec349177d119" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.1.tgz#23f84048f366fdfcf52d3fd1c68fec349177d119"
integrity sha512-Se+LaxqXlVXGvmexKGPvnUIYC1jwXu1H6Pkyb3uBM5d8/NELMYCHs/4/roD7721NxrTLyv7e5nXd5/QLBO+10g== integrity sha512-Se+LaxqXlVXGvmexKGPvnUIYC1jwXu1H6Pkyb3uBM5d8/NELMYCHs/4/roD7721NxrTLyv7e5nXd5/QLBO+10g==
core-js@^2.4.0, core-js@^2.4.1: core-js@^2.4.0:
version "2.6.12" version "2.6.12"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
@ -5042,10 +5029,10 @@ depd@~1.1.2:
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
dependency-graph@0.9.0, dependency-graph@^0.9.0: dependency-graph@^0.11.0:
version "0.9.0" version "0.11.0"
resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.9.0.tgz#11aed7e203bc8b00f48356d92db27b265c445318" resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27"
integrity sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w== integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==
des.js@^1.0.0: des.js@^1.0.0:
version "1.0.1" version "1.0.1"
@ -5407,13 +5394,6 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
encoding@^0.1.11:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
dependencies:
iconv-lite "^0.6.2"
end-of-stream@^1.0.0, end-of-stream@^1.1.0: end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@ -6117,20 +6097,6 @@ fbjs-css-vars@^1.0.0:
resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8"
integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==
fbjs@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a"
integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==
dependencies:
core-js "^2.4.1"
fbjs-css-vars "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
fbjs@^3.0.0: fbjs@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.0.tgz#0907067fb3f57a78f45d95f1eacffcacd623c165" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.0.tgz#0907067fb3f57a78f45d95f1eacffcacd623c165"
@ -6711,7 +6677,7 @@ graphql-request@^3.3.0:
extract-files "^9.0.0" extract-files "^9.0.0"
form-data "^3.0.0" form-data "^3.0.0"
graphql-tag@2.11.0, graphql-tag@^2.10.3, graphql-tag@^2.11.0: graphql-tag@^2.10.3, graphql-tag@^2.11.0:
version "2.11.0" version "2.11.0"
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.11.0.tgz#1deb53a01c46a7eb401d6cb59dec86fa1cccbffd" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.11.0.tgz#1deb53a01c46a7eb401d6cb59dec86fa1cccbffd"
integrity sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA== integrity sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA==
@ -6876,6 +6842,14 @@ he@^1.1.0, he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
header-case@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063"
integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==
dependencies:
capital-case "^1.0.4"
tslib "^2.0.3"
hex-color-regex@^1.1.0: hex-color-regex@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
@ -7128,13 +7102,6 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
dependencies: dependencies:
safer-buffer ">= 2.1.2 < 3" safer-buffer ">= 2.1.2 < 3"
iconv-lite@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
icss-utils@^4.0.0, icss-utils@^4.1.1: icss-utils@^4.0.0, icss-utils@^4.1.1:
version "4.1.1" version "4.1.1"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
@ -7591,6 +7558,13 @@ is-hexadecimal@^1.0.0:
resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7"
integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==
is-lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a"
integrity sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==
dependencies:
tslib "^2.0.3"
is-negative-zero@^2.0.0: is-negative-zero@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
@ -7695,7 +7669,7 @@ is-root@2.1.0:
resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c"
integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==
is-stream@^1.0.1, is-stream@^1.1.0: is-stream@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@ -7731,6 +7705,13 @@ is-unc-path@^1.0.0:
dependencies: dependencies:
unc-path-regex "^0.1.2" unc-path-regex "^0.1.2"
is-upper-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-2.0.2.tgz#f1105ced1fe4de906a5f39553e7d3803fd804649"
integrity sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==
dependencies:
tslib "^2.0.3"
is-whitespace-character@^1.0.0: is-whitespace-character@^1.0.0:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7"
@ -7790,14 +7771,6 @@ isobject@^4.0.0:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0"
integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
isomorphic-fetch@^3.0.0: isomorphic-fetch@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4"
@ -8841,7 +8814,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5, lodash@~4.17.15, lodash@~4.17.20: "lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5, lodash@~4.17.20:
version "4.17.20" version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
@ -8881,6 +8854,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
dependencies: dependencies:
js-tokens "^3.0.0 || ^4.0.0" js-tokens "^3.0.0 || ^4.0.0"
lower-case-first@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-2.0.2.tgz#64c2324a2250bf7c37c5901e76a5b5309301160b"
integrity sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==
dependencies:
tslib "^2.0.3"
lower-case@2.0.1: lower-case@2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7"
@ -8888,7 +8868,7 @@ lower-case@2.0.1:
dependencies: dependencies:
tslib "^1.10.0" tslib "^1.10.0"
lower-case@^2.0.1, lower-case@^2.0.2: lower-case@^2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
@ -9397,14 +9377,6 @@ node-fetch@2.6.1, node-fetch@^2.6.1:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
node-forge@^0.10.0: node-forge@^0.10.0:
version "0.10.0" version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
@ -9919,7 +9891,7 @@ param-case@3.0.3:
dot-case "^3.0.3" dot-case "^3.0.3"
tslib "^1.10.0" tslib "^1.10.0"
param-case@^3.0.3: param-case@^3.0.3, param-case@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
@ -9969,7 +9941,7 @@ parse-entities@^2.0.0:
is-decimal "^1.0.0" is-decimal "^1.0.0"
is-hexadecimal "^1.0.0" is-hexadecimal "^1.0.0"
parse-filepath@1.0.2, parse-filepath@^1.0.2: parse-filepath@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891"
integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=
@ -10044,6 +10016,14 @@ path-browserify@0.0.1:
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
path-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f"
integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
path-dirname@^1.0.0: path-dirname@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
@ -11844,28 +11824,6 @@ relateurl@^0.2.7:
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
relay-compiler@10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-10.0.0.tgz#04d50d8ec53e3f683bc379b756cf0542a76105af"
integrity sha512-EVBMcMCiP+waOPR2930cNCCsac1sNhfQayzS+bOEMz2Lls5Bx7grhaadkBZLTEdCHQ1kf7lrsmcMDqj9mxABFw==
dependencies:
"@babel/core" "^7.0.0"
"@babel/generator" "^7.5.0"
"@babel/parser" "^7.0.0"
"@babel/runtime" "^7.0.0"
"@babel/traverse" "^7.0.0"
"@babel/types" "^7.0.0"
babel-preset-fbjs "^3.3.0"
chalk "^4.0.0"
fb-watchman "^2.0.0"
fbjs "^1.0.0"
glob "^7.1.1"
immutable "~3.7.6"
nullthrows "^1.1.1"
relay-runtime "10.0.0"
signedsource "^1.0.0"
yargs "^15.3.1"
relay-compiler@10.1.0: relay-compiler@10.1.0:
version "10.1.0" version "10.1.0"
resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-10.1.0.tgz#fb4672cdbe9b54869a3a79759edd8c2d91609cbe" resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-10.1.0.tgz#fb4672cdbe9b54869a3a79759edd8c2d91609cbe"
@ -11888,14 +11846,6 @@ relay-compiler@10.1.0:
signedsource "^1.0.0" signedsource "^1.0.0"
yargs "^15.3.1" yargs "^15.3.1"
relay-runtime@10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-10.0.0.tgz#cfceb0f8453b39a385d63093f2dbf1702ddc02b3"
integrity sha512-QEpFwEjvGgWgQ0MPJyrZKggaCoGMKwxPQx7NwYl4FcMmxZcicc8wk6vI1iTxl0tsPKgW/YG8FgueQR+X7ZtZqw==
dependencies:
"@babel/runtime" "^7.0.0"
fbjs "^1.0.0"
relay-runtime@10.1.0: relay-runtime@10.1.0:
version "10.1.0" version "10.1.0"
resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-10.1.0.tgz#4753bf36e95e8d862cef33608e3d98b4ed730d16" resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-10.1.0.tgz#4753bf36e95e8d862cef33608e3d98b4ed730d16"
@ -11987,7 +11937,7 @@ request-promise-native@^1.0.5:
stealthy-require "^1.1.1" stealthy-require "^1.1.1"
tough-cookie "^2.3.3" tough-cookie "^2.3.3"
request@^2.87.0, request@^2.88.0, request@^2.88.2: request@^2.87.0, request@^2.88.0:
version "2.88.2" version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@ -12267,7 +12217,7 @@ safe-regex@^1.1.0:
dependencies: dependencies:
ret "~0.1.10" ret "~0.1.10"
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@ -12411,6 +12361,15 @@ send@0.17.1:
range-parser "~1.2.1" range-parser "~1.2.1"
statuses "~1.5.0" statuses "~1.5.0"
sentence-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f"
integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case-first "^2.0.2"
serialize-javascript@^2.1.2: serialize-javascript@^2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
@ -12616,6 +12575,14 @@ smooth-scroll-into-view-if-needed@^1.1.27:
dependencies: dependencies:
scroll-into-view-if-needed "^2.2.26" scroll-into-view-if-needed "^2.2.26"
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
snapdragon-node@^2.0.1: snapdragon-node@^2.0.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@ -12778,6 +12745,13 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies: dependencies:
extend-shallow "^3.0.0" extend-shallow "^3.0.0"
sponge-case@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c"
integrity sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==
dependencies:
tslib "^2.0.3"
sprintf-js@~1.0.2: sprintf-js@~1.0.2:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -13081,7 +13055,7 @@ style-loader@0.23.1:
loader-utils "^1.1.0" loader-utils "^1.1.0"
schema-utils "^1.0.0" schema-utils "^1.0.0"
styled-components@^5.0.1, styled-components@^5.1.0: styled-components@^5.1.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.2.1.tgz#6ed7fad2dc233825f64c719ffbdedd84ad79101a" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.2.1.tgz#6ed7fad2dc233825f64c719ffbdedd84ad79101a"
integrity sha512-sBdgLWrCFTKtmZm/9x7jkIabjFNVzCUeKfoQsM6R3saImkUnjx0QYdLwJHBjY9ifEcmjDamJDVfknWm1yxZPxQ== integrity sha512-sBdgLWrCFTKtmZm/9x7jkIabjFNVzCUeKfoQsM6R3saImkUnjx0QYdLwJHBjY9ifEcmjDamJDVfknWm1yxZPxQ==
@ -13156,6 +13130,13 @@ svgo@^1.0.0, svgo@^1.2.2:
unquote "~1.1.1" unquote "~1.1.1"
util.promisify "~1.0.0" util.promisify "~1.0.0"
swap-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-2.0.2.tgz#671aedb3c9c137e2985ef51c51f9e98445bf70d9"
integrity sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==
dependencies:
tslib "^2.0.3"
symbol-observable@^1.0.2, symbol-observable@^1.1.0, symbol-observable@^1.2.0: symbol-observable@^1.0.2, symbol-observable@^1.1.0, symbol-observable@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
@ -13298,6 +13279,13 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3:
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
title-case@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982"
integrity sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==
dependencies:
tslib "^2.0.3"
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@ -13462,11 +13450,16 @@ tslib@^1.10.0, tslib@^1.14.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@~2.0.0, tslib@~2.0.1: tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@~2.0.1:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
tslib@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
tsutils@^3.17.1: tsutils@^3.17.1:
version "3.17.1" version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
@ -13704,6 +13697,13 @@ upath@^1.1.1:
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
upper-case-first@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"
integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==
dependencies:
tslib "^2.0.3"
upper-case@2.0.1: upper-case@2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.1.tgz#6214d05e235dc817822464ccbae85822b3d8665f" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.1.tgz#6214d05e235dc817822464ccbae85822b3d8665f"
@ -14092,7 +14092,7 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5:
dependencies: dependencies:
iconv-lite "0.4.24" iconv-lite "0.4.24"
whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0, whatwg-fetch@^3.4.1: whatwg-fetch@^3.0.0, whatwg-fetch@^3.4.1:
version "3.5.0" version "3.5.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868"
integrity sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A== integrity sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==

View File

@ -53,10 +53,11 @@ type PersonalProject struct {
} }
type Project struct { type Project struct {
ProjectID uuid.UUID `json:"project_id"` ProjectID uuid.UUID `json:"project_id"`
TeamID uuid.UUID `json:"team_id"` TeamID uuid.UUID `json:"team_id"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
Name string `json:"name"` Name string `json:"name"`
PublicOn sql.NullTime `json:"public_on"`
} }
type ProjectLabel struct { type ProjectLabel struct {

View File

@ -5,13 +5,14 @@ package db
import ( import (
"context" "context"
"database/sql"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
) )
const createPersonalProject = `-- name: CreatePersonalProject :one const createPersonalProject = `-- name: CreatePersonalProject :one
INSERT INTO project(team_id, created_at, name) VALUES (null, $1, $2) RETURNING project_id, team_id, created_at, name INSERT INTO project(team_id, created_at, name) VALUES (null, $1, $2) RETURNING project_id, team_id, created_at, name, public_on
` `
type CreatePersonalProjectParams struct { type CreatePersonalProjectParams struct {
@ -27,6 +28,7 @@ func (q *Queries) CreatePersonalProject(ctx context.Context, arg CreatePersonalP
&i.TeamID, &i.TeamID,
&i.CreatedAt, &i.CreatedAt,
&i.Name, &i.Name,
&i.PublicOn,
) )
return i, err return i, err
} }
@ -78,7 +80,7 @@ func (q *Queries) CreateProjectMember(ctx context.Context, arg CreateProjectMemb
} }
const createTeamProject = `-- name: CreateTeamProject :one const createTeamProject = `-- name: CreateTeamProject :one
INSERT INTO project(team_id, created_at, name) VALUES ($1, $2, $3) RETURNING project_id, team_id, created_at, name INSERT INTO project(team_id, created_at, name) VALUES ($1, $2, $3) RETURNING project_id, team_id, created_at, name, public_on
` `
type CreateTeamProjectParams struct { type CreateTeamProjectParams struct {
@ -95,6 +97,7 @@ func (q *Queries) CreateTeamProject(ctx context.Context, arg CreateTeamProjectPa
&i.TeamID, &i.TeamID,
&i.CreatedAt, &i.CreatedAt,
&i.Name, &i.Name,
&i.PublicOn,
) )
return i, err return i, err
} }
@ -132,7 +135,7 @@ func (q *Queries) DeleteProjectMember(ctx context.Context, arg DeleteProjectMemb
} }
const getAllProjectsForTeam = `-- name: GetAllProjectsForTeam :many const getAllProjectsForTeam = `-- name: GetAllProjectsForTeam :many
SELECT project_id, team_id, created_at, name FROM project WHERE team_id = $1 SELECT project_id, team_id, created_at, name, public_on FROM project WHERE team_id = $1
` `
func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) { func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) {
@ -149,6 +152,7 @@ func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) (
&i.TeamID, &i.TeamID,
&i.CreatedAt, &i.CreatedAt,
&i.Name, &i.Name,
&i.PublicOn,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -164,7 +168,7 @@ func (q *Queries) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) (
} }
const getAllTeamProjects = `-- name: GetAllTeamProjects :many const getAllTeamProjects = `-- name: GetAllTeamProjects :many
SELECT project_id, team_id, created_at, name FROM project WHERE team_id IS NOT null SELECT project_id, team_id, created_at, name, public_on FROM project WHERE team_id IS NOT null
` `
func (q *Queries) GetAllTeamProjects(ctx context.Context) ([]Project, error) { func (q *Queries) GetAllTeamProjects(ctx context.Context) ([]Project, error) {
@ -181,6 +185,7 @@ func (q *Queries) GetAllTeamProjects(ctx context.Context) ([]Project, error) {
&i.TeamID, &i.TeamID,
&i.CreatedAt, &i.CreatedAt,
&i.Name, &i.Name,
&i.PublicOn,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -196,7 +201,7 @@ func (q *Queries) GetAllTeamProjects(ctx context.Context) ([]Project, error) {
} }
const getAllVisibleProjectsForUserID = `-- name: GetAllVisibleProjectsForUserID :many const getAllVisibleProjectsForUserID = `-- name: GetAllVisibleProjectsForUserID :many
SELECT project.project_id, project.team_id, project.created_at, project.name FROM project LEFT JOIN SELECT project.project_id, project.team_id, project.created_at, project.name, project.public_on FROM project LEFT JOIN
project_member ON project_member.project_id = project.project_id WHERE project_member.user_id = $1 project_member ON project_member.project_id = project.project_id WHERE project_member.user_id = $1
` `
@ -214,6 +219,7 @@ func (q *Queries) GetAllVisibleProjectsForUserID(ctx context.Context, userID uui
&i.TeamID, &i.TeamID,
&i.CreatedAt, &i.CreatedAt,
&i.Name, &i.Name,
&i.PublicOn,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -292,7 +298,7 @@ func (q *Queries) GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.
} }
const getPersonalProjectsForUserID = `-- name: GetPersonalProjectsForUserID :many const getPersonalProjectsForUserID = `-- name: GetPersonalProjectsForUserID :many
SELECT project.project_id, project.team_id, project.created_at, project.name FROM project SELECT project.project_id, project.team_id, project.created_at, project.name, project.public_on FROM project
LEFT JOIN personal_project ON personal_project.project_id = project.project_id LEFT JOIN personal_project ON personal_project.project_id = project.project_id
WHERE personal_project.user_id = $1 WHERE personal_project.user_id = $1
` `
@ -311,6 +317,7 @@ func (q *Queries) GetPersonalProjectsForUserID(ctx context.Context, userID uuid.
&i.TeamID, &i.TeamID,
&i.CreatedAt, &i.CreatedAt,
&i.Name, &i.Name,
&i.PublicOn,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -326,7 +333,7 @@ func (q *Queries) GetPersonalProjectsForUserID(ctx context.Context, userID uuid.
} }
const getProjectByID = `-- name: GetProjectByID :one const getProjectByID = `-- name: GetProjectByID :one
SELECT project_id, team_id, created_at, name FROM project WHERE project_id = $1 SELECT project_id, team_id, created_at, name, public_on FROM project WHERE project_id = $1
` `
func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error) { func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error) {
@ -337,6 +344,7 @@ func (q *Queries) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Proj
&i.TeamID, &i.TeamID,
&i.CreatedAt, &i.CreatedAt,
&i.Name, &i.Name,
&i.PublicOn,
) )
return i, err return i, err
} }
@ -426,6 +434,17 @@ func (q *Queries) GetProjectRolesForUserID(ctx context.Context, userID uuid.UUID
return items, nil return items, nil
} }
const getPublicOn = `-- name: GetPublicOn :one
SELECT public_on FROM project WHERE project_id = $1
`
func (q *Queries) GetPublicOn(ctx context.Context, projectID uuid.UUID) (sql.NullTime, error) {
row := q.db.QueryRowContext(ctx, getPublicOn, projectID)
var public_on sql.NullTime
err := row.Scan(&public_on)
return public_on, err
}
const getRoleForProjectMemberByUserID = `-- name: GetRoleForProjectMemberByUserID :one const getRoleForProjectMemberByUserID = `-- name: GetRoleForProjectMemberByUserID :one
SELECT code, role.name FROM project_member INNER JOIN role ON role.code = project_member.role_code SELECT code, role.name FROM project_member INNER JOIN role ON role.code = project_member.role_code
WHERE user_id = $1 AND project_id = $2 WHERE user_id = $1 AND project_id = $2
@ -469,6 +488,28 @@ func (q *Queries) GetUserRolesForProject(ctx context.Context, arg GetUserRolesFo
return i, err return i, err
} }
const setPublicOn = `-- name: SetPublicOn :one
UPDATE project SET public_on = $2 WHERE project_id = $1 RETURNING project_id, team_id, created_at, name, public_on
`
type SetPublicOnParams struct {
ProjectID uuid.UUID `json:"project_id"`
PublicOn sql.NullTime `json:"public_on"`
}
func (q *Queries) SetPublicOn(ctx context.Context, arg SetPublicOnParams) (Project, error) {
row := q.db.QueryRowContext(ctx, setPublicOn, arg.ProjectID, arg.PublicOn)
var i Project
err := row.Scan(
&i.ProjectID,
&i.TeamID,
&i.CreatedAt,
&i.Name,
&i.PublicOn,
)
return i, err
}
const updateProjectMemberRole = `-- name: UpdateProjectMemberRole :one const updateProjectMemberRole = `-- name: UpdateProjectMemberRole :one
UPDATE project_member SET role_code = $3 WHERE project_id = $1 AND user_id = $2 UPDATE project_member SET role_code = $3 WHERE project_id = $1 AND user_id = $2
RETURNING project_member_id, project_id, user_id, added_at, role_code RETURNING project_member_id, project_id, user_id, added_at, role_code
@ -494,7 +535,7 @@ func (q *Queries) UpdateProjectMemberRole(ctx context.Context, arg UpdateProject
} }
const updateProjectNameByID = `-- name: UpdateProjectNameByID :one const updateProjectNameByID = `-- name: UpdateProjectNameByID :one
UPDATE project SET name = $2 WHERE project_id = $1 RETURNING project_id, team_id, created_at, name UPDATE project SET name = $2 WHERE project_id = $1 RETURNING project_id, team_id, created_at, name, public_on
` `
type UpdateProjectNameByIDParams struct { type UpdateProjectNameByIDParams struct {
@ -510,6 +551,7 @@ func (q *Queries) UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNa
&i.TeamID, &i.TeamID,
&i.CreatedAt, &i.CreatedAt,
&i.Name, &i.Name,
&i.PublicOn,
) )
return i, err return i, err
} }

View File

@ -4,6 +4,7 @@ package db
import ( import (
"context" "context"
"database/sql"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -100,6 +101,7 @@ type Querier interface {
GetProjectMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectMember, error) GetProjectMembersForProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectMember, error)
GetProjectRolesForUserID(ctx context.Context, userID uuid.UUID) ([]GetProjectRolesForUserIDRow, error) GetProjectRolesForUserID(ctx context.Context, userID uuid.UUID) ([]GetProjectRolesForUserIDRow, error)
GetProjectsForInvitedMember(ctx context.Context, email string) ([]uuid.UUID, error) GetProjectsForInvitedMember(ctx context.Context, email string) ([]uuid.UUID, error)
GetPublicOn(ctx context.Context, projectID uuid.UUID) (sql.NullTime, error)
GetRecentlyAssignedTaskForUserID(ctx context.Context, arg GetRecentlyAssignedTaskForUserIDParams) ([]Task, error) GetRecentlyAssignedTaskForUserID(ctx context.Context, arg GetRecentlyAssignedTaskForUserIDParams) ([]Task, error)
GetRoleForProjectMemberByUserID(ctx context.Context, arg GetRoleForProjectMemberByUserIDParams) (Role, error) GetRoleForProjectMemberByUserID(ctx context.Context, arg GetRoleForProjectMemberByUserIDParams) (Role, error)
GetRoleForTeamMember(ctx context.Context, arg GetRoleForTeamMemberParams) (Role, error) GetRoleForTeamMember(ctx context.Context, arg GetRoleForTeamMemberParams) (Role, error)
@ -132,6 +134,7 @@ type Querier interface {
HasAnyUser(ctx context.Context) (bool, error) HasAnyUser(ctx context.Context) (bool, error)
SetFirstUserActive(ctx context.Context) (UserAccount, error) SetFirstUserActive(ctx context.Context) (UserAccount, error)
SetInactiveLastMoveForTaskID(ctx context.Context, taskID uuid.UUID) error SetInactiveLastMoveForTaskID(ctx context.Context, taskID uuid.UUID) error
SetPublicOn(ctx context.Context, arg SetPublicOnParams) (Project, error)
SetTaskChecklistItemComplete(ctx context.Context, arg SetTaskChecklistItemCompleteParams) (TaskChecklistItem, error) SetTaskChecklistItemComplete(ctx context.Context, arg SetTaskChecklistItemCompleteParams) (TaskChecklistItem, error)
SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams) (Task, error) SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams) (Task, error)
SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error) SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error)

View File

@ -77,3 +77,9 @@ SELECT p.team_id, COALESCE(tm.role_code, '') AS team_role, COALESCE(pm.role_code
-- name: CreatePersonalProjectLink :one -- name: CreatePersonalProjectLink :one
INSERT INTO personal_project (project_id, user_id) VALUES ($1, $2) RETURNING *; INSERT INTO personal_project (project_id, user_id) VALUES ($1, $2) RETURNING *;
-- name: SetPublicOn :one
UPDATE project SET public_on = $2 WHERE project_id = $1 RETURNING *;
-- name: GetPublicOn :one
SELECT public_on FROM project WHERE project_id = $1;

View File

@ -57,7 +57,8 @@ type ResolverRoot interface {
} }
type DirectiveRoot struct { type DirectiveRoot struct {
HasRole func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []RoleLevel, level ActionLevel, typeArg ObjectType) (res interface{}, err error) HasRole func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []RoleLevel, level ActionLevel, typeArg ObjectType) (res interface{}, err error)
RequiresUser func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error)
} }
type ComplexityRoot struct { type ComplexityRoot struct {
@ -248,6 +249,7 @@ type ComplexityRoot struct {
SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int
SetTaskComplete func(childComplexity int, input SetTaskComplete) int SetTaskComplete func(childComplexity int, input SetTaskComplete) int
SortTaskGroup func(childComplexity int, input SortTaskGroup) int SortTaskGroup func(childComplexity int, input SortTaskGroup) int
ToggleProjectVisibility func(childComplexity int, input ToggleProjectVisibility) int
ToggleTaskLabel func(childComplexity int, input ToggleTaskLabelInput) int ToggleTaskLabel func(childComplexity int, input ToggleTaskLabelInput) int
UnassignTask func(childComplexity int, input *UnassignTaskInput) int UnassignTask func(childComplexity int, input *UnassignTaskInput) int
UpdateProjectLabel func(childComplexity int, input UpdateProjectLabel) int UpdateProjectLabel func(childComplexity int, input UpdateProjectLabel) int
@ -327,6 +329,7 @@ type ComplexityRoot struct {
Members func(childComplexity int) int Members func(childComplexity int) int
Name func(childComplexity int) int Name func(childComplexity int) int
Permission func(childComplexity int) int Permission func(childComplexity int) int
PublicOn func(childComplexity int) int
TaskGroups func(childComplexity int) int TaskGroups func(childComplexity int) int
Team func(childComplexity int) int Team func(childComplexity int) int
} }
@ -476,6 +479,10 @@ type ComplexityRoot struct {
TeamID func(childComplexity int) int TeamID func(childComplexity int) int
} }
ToggleProjectVisibilityPayload struct {
Project func(childComplexity int) int
}
ToggleTaskLabelPayload struct { ToggleTaskLabelPayload struct {
Active func(childComplexity int) int Active func(childComplexity int) int
Task func(childComplexity int) int Task func(childComplexity int) int
@ -547,6 +554,7 @@ type MutationResolver interface {
CreateProject(ctx context.Context, input NewProject) (*db.Project, error) CreateProject(ctx context.Context, input NewProject) (*db.Project, error)
DeleteProject(ctx context.Context, input DeleteProject) (*DeleteProjectPayload, error) DeleteProject(ctx context.Context, input DeleteProject) (*DeleteProjectPayload, error)
UpdateProjectName(ctx context.Context, input *UpdateProjectName) (*db.Project, error) UpdateProjectName(ctx context.Context, input *UpdateProjectName) (*db.Project, error)
ToggleProjectVisibility(ctx context.Context, input ToggleProjectVisibility) (*ToggleProjectVisibilityPayload, error)
CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error)
DeleteProjectLabel(ctx context.Context, input DeleteProjectLabel) (*db.ProjectLabel, error) DeleteProjectLabel(ctx context.Context, input DeleteProjectLabel) (*db.ProjectLabel, error)
UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error) UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error)
@ -619,6 +627,7 @@ type ProjectResolver interface {
TaskGroups(ctx context.Context, obj *db.Project) ([]db.TaskGroup, error) TaskGroups(ctx context.Context, obj *db.Project) ([]db.TaskGroup, error)
Members(ctx context.Context, obj *db.Project) ([]Member, error) Members(ctx context.Context, obj *db.Project) ([]Member, error)
InvitedMembers(ctx context.Context, obj *db.Project) ([]InvitedMember, error) InvitedMembers(ctx context.Context, obj *db.Project) ([]InvitedMember, error)
PublicOn(ctx context.Context, obj *db.Project) (*time.Time, error)
Permission(ctx context.Context, obj *db.Project) (*ProjectPermission, error) Permission(ctx context.Context, obj *db.Project) (*ProjectPermission, error)
Labels(ctx context.Context, obj *db.Project) ([]db.ProjectLabel, error) Labels(ctx context.Context, obj *db.Project) ([]db.ProjectLabel, error)
} }
@ -1624,6 +1633,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.SortTaskGroup(childComplexity, args["input"].(SortTaskGroup)), true return e.complexity.Mutation.SortTaskGroup(childComplexity, args["input"].(SortTaskGroup)), true
case "Mutation.toggleProjectVisibility":
if e.complexity.Mutation.ToggleProjectVisibility == nil {
break
}
args, err := ec.field_Mutation_toggleProjectVisibility_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.ToggleProjectVisibility(childComplexity, args["input"].(ToggleProjectVisibility)), true
case "Mutation.toggleTaskLabel": case "Mutation.toggleTaskLabel":
if e.complexity.Mutation.ToggleTaskLabel == nil { if e.complexity.Mutation.ToggleTaskLabel == nil {
break break
@ -2098,6 +2119,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Project.Permission(childComplexity), true return e.complexity.Project.Permission(childComplexity), true
case "Project.publicOn":
if e.complexity.Project.PublicOn == nil {
break
}
return e.complexity.Project.PublicOn(childComplexity), true
case "Project.taskGroups": case "Project.taskGroups":
if e.complexity.Project.TaskGroups == nil { if e.complexity.Project.TaskGroups == nil {
break break
@ -2763,6 +2791,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.TeamRole.TeamID(childComplexity), true return e.complexity.TeamRole.TeamID(childComplexity), true
case "ToggleProjectVisibilityPayload.project":
if e.complexity.ToggleProjectVisibilityPayload.Project == nil {
break
}
return e.complexity.ToggleProjectVisibilityPayload.Project(childComplexity), true
case "ToggleTaskLabelPayload.active": case "ToggleTaskLabelPayload.active":
if e.complexity.ToggleTaskLabelPayload.Active == nil { if e.complexity.ToggleTaskLabelPayload.Active == nil {
break break
@ -3158,6 +3193,7 @@ type Project {
taskGroups: [TaskGroup!]! taskGroups: [TaskGroup!]!
members: [Member!]! members: [Member!]!
invitedMembers: [InvitedMember!]! invitedMembers: [InvitedMember!]!
publicOn: Time
permission: ProjectPermission! permission: ProjectPermission!
labels: [ProjectLabel!]! labels: [ProjectLabel!]!
} }
@ -3294,6 +3330,7 @@ enum ObjectType {
} }
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
directive @requiresUser on FIELD_DEFINITION
type Query { type Query {
organizations: [Organization!]! organizations: [Organization!]!
@ -3301,7 +3338,7 @@ type Query {
invitedUsers: [InvitedUserAccount!]! invitedUsers: [InvitedUserAccount!]!
findUser(input: FindUser!): UserAccount! findUser(input: FindUser!): UserAccount!
findProject(input: FindProject!): findProject(input: FindProject!):
Project! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) Project!
findTask(input: FindTask!): Task! findTask(input: FindTask!): Task!
projects(input: ProjectsFilter): [Project!]! projects(input: ProjectsFilter): [Project!]!
findTeam(input: FindTeam!): Team! findTeam(input: FindTeam!): Team!
@ -3309,7 +3346,7 @@ type Query {
myTasks(input: MyTasks!): MyTasksPayload! myTasks(input: MyTasks!): MyTasksPayload!
labelColors: [LabelColor!]! labelColors: [LabelColor!]!
taskGroups: [TaskGroup!]! taskGroups: [TaskGroup!]!
me: MePayload! me: MePayload
} }
@ -3427,6 +3464,16 @@ extend type Mutation {
DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
updateProjectName(input: UpdateProjectName): updateProjectName(input: UpdateProjectName):
Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
}
input ToggleProjectVisibility {
projectID: UUID!
isPublic: Boolean!
}
type ToggleProjectVisibilityPayload {
project: Project!
} }
input NewProject { input NewProject {
@ -4558,6 +4605,21 @@ func (ec *executionContext) field_Mutation_sortTaskGroup_args(ctx context.Contex
return args, nil return args, nil
} }
func (ec *executionContext) field_Mutation_toggleProjectVisibility_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 ToggleProjectVisibility
if tmp, ok := rawArgs["input"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input"))
arg0, err = ec.unmarshalNToggleProjectVisibility2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibility(ctx, tmp)
if err != nil {
return nil, err
}
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_toggleTaskLabel_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation_toggleTaskLabel_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
@ -7744,6 +7806,80 @@ func (ec *executionContext) _Mutation_updateProjectName(ctx context.Context, fie
return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res) return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation_toggleProjectVisibility(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Mutation",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Mutation_toggleProjectVisibility_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().ToggleProjectVisibility(rctx, args["input"].(ToggleProjectVisibility))
}
directive1 := func(ctx context.Context) (interface{}, error) {
roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN"})
if err != nil {
return nil, err
}
level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT")
if err != nil {
return nil, err
}
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
if err != nil {
return nil, err
}
if ec.directives.HasRole == nil {
return nil, errors.New("directive hasRole is not implemented")
}
return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*ToggleProjectVisibilityPayload); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/graph.ToggleProjectVisibilityPayload`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*ToggleProjectVisibilityPayload)
fc.Result = res
return ec.marshalNToggleProjectVisibilityPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibilityPayload(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_createProjectLabel(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Mutation_createProjectLabel(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -12601,6 +12737,38 @@ func (ec *executionContext) _Project_invitedMembers(ctx context.Context, field g
return ec.marshalNInvitedMember2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInvitedMemberᚄ(ctx, field.Selections, res) return ec.marshalNInvitedMember2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐInvitedMemberᚄ(ctx, field.Selections, res)
} }
func (ec *executionContext) _Project_publicOn(ctx context.Context, field graphql.CollectedField, obj *db.Project) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Project",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Project().PublicOn(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*time.Time)
fc.Result = res
return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res)
}
func (ec *executionContext) _Project_permission(ctx context.Context, field graphql.CollectedField, obj *db.Project) (ret graphql.Marshaler) { func (ec *executionContext) _Project_permission(ctx context.Context, field graphql.CollectedField, obj *db.Project) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -13224,40 +13392,8 @@ func (ec *executionContext) _Query_findProject(ctx context.Context, field graphq
} }
fc.Args = args fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children
ctx = rctx // use context from middleware stack in children return ec.resolvers.Query().FindProject(rctx, args["input"].(FindProject))
return ec.resolvers.Query().FindProject(rctx, args["input"].(FindProject))
}
directive1 := func(ctx context.Context) (interface{}, error) {
roles, err := ec.unmarshalNRoleLevel2ᚕgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleLevelᚄ(ctx, []interface{}{"ADMIN", "MEMBER"})
if err != nil {
return nil, err
}
level, err := ec.unmarshalNActionLevel2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐActionLevel(ctx, "PROJECT")
if err != nil {
return nil, err
}
typeArg, err := ec.unmarshalNObjectType2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐObjectType(ctx, "PROJECT")
if err != nil {
return nil, err
}
if ec.directives.HasRole == nil {
return nil, errors.New("directive hasRole is not implemented")
}
return ec.directives.HasRole(ctx, nil, directive0, roles, level, typeArg)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*db.Project); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/jordanknott/taskcafe/internal/db.Project`, tmp)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -13572,14 +13708,11 @@ func (ec *executionContext) _Query_me(ctx context.Context, field graphql.Collect
return graphql.Null return graphql.Null
} }
if resTmp == nil { if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null return graphql.Null
} }
res := resTmp.(*MePayload) res := resTmp.(*MePayload)
fc.Result = res fc.Result = res
return ec.marshalNMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx, field.Selections, res) return ec.marshalOMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx, field.Selections, res)
} }
func (ec *executionContext) _Query_notifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query_notifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
@ -15885,6 +16018,41 @@ func (ec *executionContext) _TeamRole_roleCode(ctx context.Context, field graphq
return ec.marshalNRoleCode2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleCode(ctx, field.Selections, res) return ec.marshalNRoleCode2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐRoleCode(ctx, field.Selections, res)
} }
func (ec *executionContext) _ToggleProjectVisibilityPayload_project(ctx context.Context, field graphql.CollectedField, obj *ToggleProjectVisibilityPayload) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "ToggleProjectVisibilityPayload",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Project, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*db.Project)
fc.Result = res
return ec.marshalNProject2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋdbᚐProject(ctx, field.Selections, res)
}
func (ec *executionContext) _ToggleTaskLabelPayload_active(ctx context.Context, field graphql.CollectedField, obj *ToggleTaskLabelPayload) (ret graphql.Marshaler) { func (ec *executionContext) _ToggleTaskLabelPayload_active(ctx context.Context, field graphql.CollectedField, obj *ToggleTaskLabelPayload) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -19238,6 +19406,34 @@ func (ec *executionContext) unmarshalInputTaskPositionUpdate(ctx context.Context
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputToggleProjectVisibility(ctx context.Context, obj interface{}) (ToggleProjectVisibility, error) {
var it ToggleProjectVisibility
var asMap = obj.(map[string]interface{})
for k, v := range asMap {
switch k {
case "projectID":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("projectID"))
it.ProjectID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
if err != nil {
return it, err
}
case "isPublic":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("isPublic"))
it.IsPublic, err = ec.unmarshalNBoolean2bool(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputToggleTaskLabelInput(ctx context.Context, obj interface{}) (ToggleTaskLabelInput, error) { func (ec *executionContext) unmarshalInputToggleTaskLabelInput(ctx context.Context, obj interface{}) (ToggleTaskLabelInput, error) {
var it ToggleTaskLabelInput var it ToggleTaskLabelInput
var asMap = obj.(map[string]interface{}) var asMap = obj.(map[string]interface{})
@ -20841,6 +21037,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "toggleProjectVisibility":
out.Values[i] = ec._Mutation_toggleProjectVisibility(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "createProjectLabel": case "createProjectLabel":
out.Values[i] = ec._Mutation_createProjectLabel(ctx, field) out.Values[i] = ec._Mutation_createProjectLabel(ctx, field)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@ -21541,6 +21742,17 @@ func (ec *executionContext) _Project(ctx context.Context, sel ast.SelectionSet,
} }
return res return res
}) })
case "publicOn":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Project_publicOn(ctx, field, obj)
return res
})
case "permission": case "permission":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
@ -21939,9 +22151,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
} }
}() }()
res = ec._Query_me(ctx, field) res = ec._Query_me(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res return res
}) })
case "notifications": case "notifications":
@ -22860,6 +23069,33 @@ func (ec *executionContext) _TeamRole(ctx context.Context, sel ast.SelectionSet,
return out return out
} }
var toggleProjectVisibilityPayloadImplementors = []string{"ToggleProjectVisibilityPayload"}
func (ec *executionContext) _ToggleProjectVisibilityPayload(ctx context.Context, sel ast.SelectionSet, obj *ToggleProjectVisibilityPayload) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, toggleProjectVisibilityPayloadImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("ToggleProjectVisibilityPayload")
case "project":
out.Values[i] = ec._ToggleProjectVisibilityPayload_project(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var toggleTaskLabelPayloadImplementors = []string{"ToggleTaskLabelPayload"} var toggleTaskLabelPayloadImplementors = []string{"ToggleTaskLabelPayload"}
func (ec *executionContext) _ToggleTaskLabelPayload(ctx context.Context, sel ast.SelectionSet, obj *ToggleTaskLabelPayload) graphql.Marshaler { func (ec *executionContext) _ToggleTaskLabelPayload(ctx context.Context, sel ast.SelectionSet, obj *ToggleTaskLabelPayload) graphql.Marshaler {
@ -24186,20 +24422,6 @@ func (ec *executionContext) unmarshalNLogoutUser2githubᚗcomᚋjordanknottᚋta
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
} }
func (ec *executionContext) marshalNMePayload2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx context.Context, sel ast.SelectionSet, v MePayload) graphql.Marshaler {
return ec._MePayload(ctx, sel, &v)
}
func (ec *executionContext) marshalNMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx context.Context, sel ast.SelectionSet, v *MePayload) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._MePayload(ctx, sel, v)
}
func (ec *executionContext) marshalNMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMember(ctx context.Context, sel ast.SelectionSet, v Member) graphql.Marshaler { func (ec *executionContext) marshalNMember2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMember(ctx context.Context, sel ast.SelectionSet, v Member) graphql.Marshaler {
return ec._Member(ctx, sel, &v) return ec._Member(ctx, sel, &v)
} }
@ -25468,6 +25690,25 @@ func (ec *executionContext) marshalNTime2ᚖtimeᚐTime(ctx context.Context, sel
return res return res
} }
func (ec *executionContext) unmarshalNToggleProjectVisibility2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibility(ctx context.Context, v interface{}) (ToggleProjectVisibility, error) {
res, err := ec.unmarshalInputToggleProjectVisibility(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNToggleProjectVisibilityPayload2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibilityPayload(ctx context.Context, sel ast.SelectionSet, v ToggleProjectVisibilityPayload) graphql.Marshaler {
return ec._ToggleProjectVisibilityPayload(ctx, sel, &v)
}
func (ec *executionContext) marshalNToggleProjectVisibilityPayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleProjectVisibilityPayload(ctx context.Context, sel ast.SelectionSet, v *ToggleProjectVisibilityPayload) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._ToggleProjectVisibilityPayload(ctx, sel, v)
}
func (ec *executionContext) unmarshalNToggleTaskLabelInput2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleTaskLabelInput(ctx context.Context, v interface{}) (ToggleTaskLabelInput, error) { func (ec *executionContext) unmarshalNToggleTaskLabelInput2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐToggleTaskLabelInput(ctx context.Context, v interface{}) (ToggleTaskLabelInput, error) {
res, err := ec.unmarshalInputToggleTaskLabelInput(ctx, v) res, err := ec.unmarshalInputToggleTaskLabelInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
@ -26081,6 +26322,13 @@ func (ec *executionContext) unmarshalODeleteTaskComment2ᚖgithubᚗcomᚋjordan
return &res, graphql.ErrorOnPath(ctx, err) return &res, graphql.ErrorOnPath(ctx, err)
} }
func (ec *executionContext) marshalOMePayload2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐMePayload(ctx context.Context, sel ast.SelectionSet, v *MePayload) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._MePayload(ctx, sel, v)
}
func (ec *executionContext) marshalOProfileIcon2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐProfileIcon(ctx context.Context, sel ast.SelectionSet, v *ProfileIcon) graphql.Marshaler { func (ec *executionContext) marshalOProfileIcon2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐProfileIcon(ctx context.Context, sel ast.SelectionSet, v *ProfileIcon) graphql.Marshaler {
if v == nil { if v == nil {
return graphql.Null return graphql.Null

View File

@ -46,6 +46,12 @@ func NewHandler(repo db.Repository, emailConfig utils.EmailConfig) http.Handler
} }
*/ */
userID, ok := GetUserID(ctx)
if !ok {
return nil, errors.New("user must be logged in")
}
log.Info("has role")
var subjectID uuid.UUID var subjectID uuid.UUID
in := graphql.GetFieldContext(ctx).Args["input"] in := graphql.GetFieldContext(ctx).Args["input"]
val := reflect.ValueOf(in) // could be any underlying type val := reflect.ValueOf(in) // could be any underlying type
@ -78,7 +84,7 @@ func NewHandler(repo db.Repository, emailConfig utils.EmailConfig) http.Handler
// TODO: add config setting to disable personal projects // TODO: add config setting to disable personal projects
return next(ctx) return next(ctx)
} }
subjectID, ok := subjectField.Interface().(uuid.UUID) subjectID, ok = subjectField.Interface().(uuid.UUID)
if !ok { if !ok {
logger.New(ctx).Error("error while casting subject UUID") logger.New(ctx).Error("error while casting subject UUID")
return nil, errors.New("error while casting subject uuid") return nil, errors.New("error while casting subject uuid")
@ -130,10 +136,6 @@ func NewHandler(repo db.Repository, emailConfig utils.EmailConfig) http.Handler
}, },
} }
} else if level == ActionLevelTeam { } else if level == ActionLevelTeam {
userID, ok := GetUserID(ctx)
if !ok {
return nil, errors.New("user id is missing")
}
role, err := repo.GetTeamRoleForUserID(ctx, db.GetTeamRoleForUserIDParams{UserID: userID, TeamID: subjectID}) role, err := repo.GetTeamRoleForUserID(ctx, db.GetTeamRoleForUserIDParams{UserID: userID, TeamID: subjectID})
if err != nil { if err != nil {
logger.New(ctx).WithError(err).Error("error while getting team roles for user ID") logger.New(ctx).WithError(err).Error("error while getting team roles for user ID")
@ -270,3 +272,23 @@ const (
TASK_CHECKLIST_ADDED int32 = 9 TASK_CHECKLIST_ADDED int32 = 9
TASK_CHECKLIST_REMOVED int32 = 10 TASK_CHECKLIST_REMOVED int32 = 10
) )
func NotAuthorized() error {
return &gqlerror.Error{
Message: "Not authorized",
Extensions: map[string]interface{}{
"code": "UNAUTHENTICATED",
},
}
}
func IsProjectPublic(ctx context.Context, repo db.Repository, projectID uuid.UUID) (bool, error) {
publicOn, err := repo.GetPublicOn(ctx, projectID)
if err != nil {
return false, err
}
if !publicOn.Valid {
return false, nil
}
return true, nil
}

View File

@ -448,6 +448,15 @@ type TeamRole struct {
RoleCode RoleCode `json:"roleCode"` RoleCode RoleCode `json:"roleCode"`
} }
type ToggleProjectVisibility struct {
ProjectID uuid.UUID `json:"projectID"`
IsPublic bool `json:"isPublic"`
}
type ToggleProjectVisibilityPayload struct {
Project *db.Project `json:"project"`
}
type ToggleTaskLabelInput struct { type ToggleTaskLabelInput struct {
TaskID uuid.UUID `json:"taskID"` TaskID uuid.UUID `json:"taskID"`
ProjectLabelID uuid.UUID `json:"projectLabelID"` ProjectLabelID uuid.UUID `json:"projectLabelID"`

View File

@ -119,6 +119,7 @@ type Project {
taskGroups: [TaskGroup!]! taskGroups: [TaskGroup!]!
members: [Member!]! members: [Member!]!
invitedMembers: [InvitedMember!]! invitedMembers: [InvitedMember!]!
publicOn: Time
permission: ProjectPermission! permission: ProjectPermission!
labels: [ProjectLabel!]! labels: [ProjectLabel!]!
} }
@ -255,6 +256,7 @@ enum ObjectType {
} }
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
directive @requiresUser on FIELD_DEFINITION
type Query { type Query {
organizations: [Organization!]! organizations: [Organization!]!
@ -262,7 +264,7 @@ type Query {
invitedUsers: [InvitedUserAccount!]! invitedUsers: [InvitedUserAccount!]!
findUser(input: FindUser!): UserAccount! findUser(input: FindUser!): UserAccount!
findProject(input: FindProject!): findProject(input: FindProject!):
Project! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) Project!
findTask(input: FindTask!): Task! findTask(input: FindTask!): Task!
projects(input: ProjectsFilter): [Project!]! projects(input: ProjectsFilter): [Project!]!
findTeam(input: FindTeam!): Team! findTeam(input: FindTeam!): Team!
@ -270,7 +272,7 @@ type Query {
myTasks(input: MyTasks!): MyTasksPayload! myTasks(input: MyTasks!): MyTasksPayload!
labelColors: [LabelColor!]! labelColors: [LabelColor!]!
taskGroups: [TaskGroup!]! taskGroups: [TaskGroup!]!
me: MePayload! me: MePayload
} }
@ -388,6 +390,16 @@ extend type Mutation {
DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
updateProjectName(input: UpdateProjectName): updateProjectName(input: UpdateProjectName):
Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
}
input ToggleProjectVisibility {
projectID: UUID!
isPublic: Boolean!
}
type ToggleProjectVisibilityPayload {
project: Project!
} }
input NewProject { input NewProject {
@ -982,3 +994,4 @@ type DeleteUserAccountPayload {
ok: Boolean! ok: Boolean!
userAccount: UserAccount! userAccount: UserAccount!
} }

View File

@ -84,6 +84,15 @@ func (r *mutationResolver) UpdateProjectName(ctx context.Context, input *UpdateP
return &project, nil return &project, nil
} }
func (r *mutationResolver) ToggleProjectVisibility(ctx context.Context, input ToggleProjectVisibility) (*ToggleProjectVisibilityPayload, error) {
if input.IsPublic {
project, err := r.Repository.SetPublicOn(ctx, db.SetPublicOnParams{ProjectID: input.ProjectID, PublicOn: sql.NullTime{Valid: true, Time: time.Now().UTC()}})
return &ToggleProjectVisibilityPayload{Project: &project}, err
}
project, err := r.Repository.SetPublicOn(ctx, db.SetPublicOnParams{ProjectID: input.ProjectID, PublicOn: sql.NullTime{Valid: false, Time: time.Time{}}})
return &ToggleProjectVisibilityPayload{Project: &project}, err
}
func (r *mutationResolver) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error) { func (r *mutationResolver) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error) {
createdAt := time.Now().UTC() createdAt := time.Now().UTC()
@ -1206,6 +1215,13 @@ func (r *projectResolver) InvitedMembers(ctx context.Context, obj *db.Project) (
return invited, err return invited, err
} }
func (r *projectResolver) PublicOn(ctx context.Context, obj *db.Project) (*time.Time, error) {
if obj.PublicOn.Valid {
return &obj.PublicOn.Time, nil
}
return nil, nil
}
func (r *projectResolver) Permission(ctx context.Context, obj *db.Project) (*ProjectPermission, error) { func (r *projectResolver) Permission(ctx context.Context, obj *db.Project) (*ProjectPermission, error) {
panic(fmt.Errorf("not implemented")) panic(fmt.Errorf("not implemented"))
} }
@ -1277,12 +1293,19 @@ func (r *queryResolver) FindUser(ctx context.Context, input FindUser) (*db.UserA
func (r *queryResolver) FindProject(ctx context.Context, input FindProject) (*db.Project, error) { func (r *queryResolver) FindProject(ctx context.Context, input FindProject) (*db.Project, error) {
logger.New(ctx).Info("finding project user") logger.New(ctx).Info("finding project user")
_, isLoggedIn := GetUser(ctx)
if !isLoggedIn {
isPublic, _ := IsProjectPublic(ctx, r.Repository, input.ProjectID)
if !isPublic {
return &db.Project{}, NotAuthorized()
}
}
project, err := r.Repository.GetProjectByID(ctx, input.ProjectID) project, err := r.Repository.GetProjectByID(ctx, input.ProjectID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return &db.Project{}, &gqlerror.Error{ return &db.Project{}, &gqlerror.Error{
Message: "Project not found", Message: "Project not found",
Extensions: map[string]interface{}{ Extensions: map[string]interface{}{
"code": "11-404", "code": "NOT_FOUND",
}, },
} }
} }
@ -1497,7 +1520,7 @@ func (r *queryResolver) TaskGroups(ctx context.Context) ([]db.TaskGroup, error)
func (r *queryResolver) Me(ctx context.Context) (*MePayload, error) { func (r *queryResolver) Me(ctx context.Context) (*MePayload, error) {
userID, ok := GetUserID(ctx) userID, ok := GetUserID(ctx)
if !ok { if !ok {
return &MePayload{}, fmt.Errorf("internal server error") return nil, nil
} }
user, err := r.Repository.GetUserAccountByID(ctx, userID) user, err := r.Repository.GetUserAccountByID(ctx, userID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {

View File

@ -119,6 +119,7 @@ type Project {
taskGroups: [TaskGroup!]! taskGroups: [TaskGroup!]!
members: [Member!]! members: [Member!]!
invitedMembers: [InvitedMember!]! invitedMembers: [InvitedMember!]!
publicOn: Time
permission: ProjectPermission! permission: ProjectPermission!
labels: [ProjectLabel!]! labels: [ProjectLabel!]!
} }

View File

@ -25,6 +25,7 @@ enum ObjectType {
} }
directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION directive @hasRole(roles: [RoleLevel!]!, level: ActionLevel!, type: ObjectType!) on FIELD_DEFINITION
directive @requiresUser on FIELD_DEFINITION
type Query { type Query {
organizations: [Organization!]! organizations: [Organization!]!
@ -32,7 +33,7 @@ type Query {
invitedUsers: [InvitedUserAccount!]! invitedUsers: [InvitedUserAccount!]!
findUser(input: FindUser!): UserAccount! findUser(input: FindUser!): UserAccount!
findProject(input: FindProject!): findProject(input: FindProject!):
Project! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT) Project!
findTask(input: FindTask!): Task! findTask(input: FindTask!): Task!
projects(input: ProjectsFilter): [Project!]! projects(input: ProjectsFilter): [Project!]!
findTeam(input: FindTeam!): Team! findTeam(input: FindTeam!): Team!
@ -40,7 +41,7 @@ type Query {
myTasks(input: MyTasks!): MyTasksPayload! myTasks(input: MyTasks!): MyTasksPayload!
labelColors: [LabelColor!]! labelColors: [LabelColor!]!
taskGroups: [TaskGroup!]! taskGroups: [TaskGroup!]!
me: MePayload! me: MePayload
} }

View File

@ -4,6 +4,16 @@ extend type Mutation {
DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) DeleteProjectPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
updateProjectName(input: UpdateProjectName): updateProjectName(input: UpdateProjectName):
Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT) Project! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
toggleProjectVisibility(input: ToggleProjectVisibility!): ToggleProjectVisibilityPayload! @hasRole(roles: [ADMIN], level: PROJECT, type: PROJECT)
}
input ToggleProjectVisibility {
projectID: UUID!
isPublic: Boolean!
}
type ToggleProjectVisibilityPayload {
project: Project!
} }
input NewProject { input NewProject {

View File

@ -18,6 +18,7 @@ type AuthenticationMiddleware struct {
// Middleware returns the middleware handler // Middleware returns the middleware handler
func (m *AuthenticationMiddleware) Middleware(next http.Handler) http.Handler { func (m *AuthenticationMiddleware) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Info("middleware")
requestID := uuid.New() requestID := uuid.New()
foundToken := true foundToken := true
tokenRaw := "" tokenRaw := ""
@ -25,42 +26,26 @@ func (m *AuthenticationMiddleware) Middleware(next http.Handler) http.Handler {
if err != nil { if err != nil {
if err == http.ErrNoCookie { if err == http.ErrNoCookie {
foundToken = false foundToken = false
} else {
log.WithError(err).Error("unknown error")
w.WriteHeader(http.StatusBadRequest)
return
} }
} }
if !foundToken { if !foundToken {
token := r.Header.Get("Authorization") token := r.Header.Get("Authorization")
if token == "" { if token != "" {
log.WithError(err).Error("no auth token found in cookie or authorization header") tokenRaw = token
w.WriteHeader(http.StatusBadRequest)
return
} }
tokenRaw = token
} else { } else {
tokenRaw = c.Value tokenRaw = c.Value
} }
authTokenID := uuid.MustParse(tokenRaw) authTokenID, err := uuid.Parse(tokenRaw)
token, err := m.repo.GetAuthTokenByID(r.Context(), authTokenID) log.Info("checking if logged in")
ctx := r.Context()
if err != nil { if err == nil {
w.WriteHeader(http.StatusUnauthorized) token, err := m.repo.GetAuthTokenByID(r.Context(), authTokenID)
w.Write([]byte(`{ if err == nil {
"data": {}, ctx = context.WithValue(ctx, utils.UserIDKey, token.UserID)
"errors": [ }
{
"extensions": {
"code": "UNAUTHENTICATED"
}
}
]
}`))
return
} }
ctx := context.WithValue(r.Context(), utils.UserIDKey, token.UserID)
// ctx = context.WithValue(ctx, utils.RestrictedModeKey, accessClaims.Restricted) // ctx = context.WithValue(ctx, utils.RestrictedModeKey, accessClaims.Restricted)
// ctx = context.WithValue(ctx, utils.OrgRoleKey, accessClaims.OrgRole) // ctx = context.WithValue(ctx, utils.OrgRoleKey, accessClaims.OrgRole)
ctx = context.WithValue(ctx, utils.ReqIDKey, requestID) ctx = context.WithValue(ctx, utils.ReqIDKey, requestID)

View File

@ -0,0 +1 @@
ALTER TABLE project ADD COLUMN public_on timestamptz;