fix: images bugs & improvements, project details card reactivity, edit project link

This commit is contained in:
MTG2000
2022-09-25 13:34:07 +03:00
parent 3fd0b27a84
commit 8b3c0df5be
9 changed files with 166 additions and 218 deletions

View File

@@ -1,5 +1,5 @@
import UploadPreview, { PreviewComponentProps, PreviewMethods } from '@rpldy/upload-preview'
import { useAbortItem, useItemAbortListener, useItemCancelListener, useItemErrorListener, useItemProgressListener } from '@rpldy/uploady';
import { useAbortItem, useItemAbortListener, useItemCancelListener, useItemErrorListener, useItemFinishListener, useItemProgressListener } from '@rpldy/uploady';
import { useState } from 'react'
import ScreenShotsThumbnail from './ScreenshotThumbnail'
@@ -20,15 +20,11 @@ function CustomImagePreview({ id, url }: PreviewComponentProps) {
useItemProgressListener(item => {
if (item.completed > progress) {
setProgress(() => item.completed);
if (item.completed === 100) {
setItemState(STATES.DONE)
} else {
setItemState(STATES.PROGRESS)
}
}
}, id);
useItemFinishListener(() => setItemState(STATES.DONE), id)
useItemAbortListener(item => {
@@ -41,7 +37,10 @@ function CustomImagePreview({ id, url }: PreviewComponentProps) {
}, id);
useItemErrorListener(item => {
console.log(item);
setItemState(STATES.ERROR);
setTimeout(() => setItemState(STATES.CANCELLED), 2000)
}, id);
if (itemState === STATES.DONE || itemState === STATES.CANCELLED)

View File

@@ -45,7 +45,7 @@ export default function ScreenshotThumbnail({ url, isLoading, isError, onCancel
Failed...
</div>}
{!isEmpty &&
<button className="absolute bg-gray-900 hover:bg-opacity-100 bg-opacity-60 text-white rounded-full w-32 h-32 top-8 right-8 flex flex-col justify-center items-center" onClick={() => onCancel?.()}><FaTimes /></button>
<button type='button' className="absolute bg-gray-900 hover:bg-opacity-100 bg-opacity-60 text-white rounded-full w-32 h-32 top-8 right-8 flex flex-col justify-center items-center" onClick={() => onCancel?.()}><FaTimes /></button>
}
</div>
)

View File

@@ -13,6 +13,7 @@ import { FiCamera } from "react-icons/fi";
import { Control, Path, useController } from "react-hook-form";
import { ImageInput } from "src/graphql";
import { fetchUploadImageUrl } from "src/api/uploading";
import { removeArrayItemAtIndex } from "src/utils/helperFunctions";
@@ -43,6 +44,7 @@ export default function ScreenshotsInput(props: Props) {
return (
<Uploady
accept="image/*"
multiple={true}
inputFieldName='file'
grouped={false}
@@ -53,31 +55,24 @@ export default function ScreenshotsInput(props: Props) {
[UPLOADER_EVENTS.ITEM_FINALIZE]: () => setUploadingCount(v => v - 1),
[UPLOADER_EVENTS.ITEM_FINISH]: (item) => {
// Just for mocking purposes
const dataUrl = URL.createObjectURL(item.file);
const { id, filename, variants } = item?.uploadResponse?.data?.result;
const { id, filename, variants } = item?.uploadResponse?.data?.result ?? {
id: Math.random().toString(),
filename: item.file.name,
variants: [
"",
dataUrl
]
}
if (id) {
onChange([...uploadedFiles, { id, name: filename, url: variants[1] }].slice(-MAX_UPLOAD_COUNT))
const url = (variants as string[]).find(v => v.includes('public'));
if (id && url) {
onChange([...uploadedFiles, { id, name: filename, url: url }].slice(-MAX_UPLOAD_COUNT))
}
}
}}
>
<div className="grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-16 mt-24">
{canUploadMore && <DropZoneButton />}
{uploadedFiles.map(f => <ScreenshotThumbnail
<DropZoneButton extraProps={{ canUploadMore }} />
{uploadedFiles.map((f, idx) => <ScreenshotThumbnail
key={f.id}
url={f.url}
onCancel={() => {
onChange(uploadedFiles.filter(file => file.id !== f.id))
onChange(removeArrayItemAtIndex(uploadedFiles, idx))
}} />)}
<ImagePreviews />
{(placeholdersCount > 0) &&
@@ -88,7 +83,7 @@ export default function ScreenshotsInput(props: Props) {
}
const DropZone = forwardRef<any, any>((props, ref) => {
const { onClick, ...buttonProps } = props;
const { canUploadMore, onClick, ...buttonProps } = props;
useRequestPreSend(async (data) => {
@@ -115,6 +110,8 @@ const DropZone = forwardRef<any, any>((props, ref) => {
[onClick]
);
if (!canUploadMore) return null
return <UploadDropZone
{...buttonProps}
ref={ref}

View File

@@ -65,11 +65,11 @@ const schema: yup.SchemaOf<IListProjectForm> = yup.object({
description: yup.string().trim().required().min(50, 'Write at least 10 words descriping your project'),
thumbnail_image: imageSchema.required("Please pick a thumbnail image").default(undefined),
cover_image: imageSchema.required("Please pick a cover image").default(undefined),
twitter: yup.string().url(),
discord: yup.string().url(),
github: yup.string().url(),
slack: yup.string().url(),
telegram: yup.string().url(),
twitter: yup.string().url().nullable(),
discord: yup.string().url().nullable(),
github: yup.string().url().nullable(),
slack: yup.string().url().nullable(),
telegram: yup.string().url().nullable(),
category_id: yup.number().required("Please choose a category"),
capabilities: yup.array().of(yup.number().required()).default([]),
screenshots: yup.array().of(imageSchema.required()).default([]),

View File

@@ -1,51 +1,64 @@
fragment ProjectDetails on Project {
id
title
tagline
description
hashtag
cover_image
thumbnail_image
launch_status
twitter
discord
github
slack
telegram
screenshots
website
lightning_address
votes_count
category {
id
icon
title
}
permissions
members {
role
user {
id
name
jobTitle
avatar
}
}
awards {
title
image
url
id
}
tags {
id
title
}
recruit_roles {
id
title
icon
level
}
capabilities {
id
title
icon
}
}
mutation CreateProject($input: CreateProjectInput) {
createProject(input: $input) {
project {
id
title
description
cover_image
thumbnail_image
screenshots
website
lightning_address
lnurl_callback_url
votes_count
category {
id
icon
title
}
members {
role
user {
id
name
avatar
}
}
awards {
title
image
url
id
}
tags {
id
title
}
recruit_roles {
id
title
icon
level
}
capabilities {
id
title
icon
}
...ProjectDetails
}
}
}
@@ -53,52 +66,7 @@ mutation CreateProject($input: CreateProjectInput) {
mutation UpdateProject($input: UpdateProjectInput) {
updateProject(input: $input) {
project {
id
title
description
cover_image
thumbnail_image
screenshots
website
lightning_address
lnurl_callback_url
votes_count
category {
id
icon
title
}
members {
role
user {
id
name
avatar
}
}
awards {
title
image
url
id
}
tags {
id
title
}
recruit_roles {
id
title
icon
level
}
capabilities {
id
title
icon
}
...ProjectDetails
}
}
}

View File

@@ -18,8 +18,6 @@ export default function ProjectDetailsCardSkeleton({ onClose, direction, ...prop
const isMdScreen = useMediaQuery(MEDIA_QUERIES.isMedium)
return (
<motion.div
custom={direction}
@@ -27,7 +25,7 @@ export default function ProjectDetailsCardSkeleton({ onClose, direction, ...prop
initial='initial'
animate="animate"
exit='exit'
className={`modal-card max-w-[768px] ${props.isPageModal && !isMdScreen && 'rounded-0 w-full min-h-screen'}`}
className={`modal-card max-w-[676px] ${props.isPageModal && !isMdScreen && 'rounded-0 w-full min-h-screen'}`}
>
<div className="relative h-[100px] lg:h-[80px]">

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import { MdClose, MdLocalFireDepartment } from 'react-icons/md';
import { MdClose, MdEdit, MdLocalFireDepartment } from 'react-icons/md';
import { ModalCard } from 'src/Components/Modals/ModalsContainer/ModalsContainer';
import { useAppDispatch, useAppSelector, useMediaQuery } from 'src/utils/hooks';
import { openModal, scheduleModal } from 'src/redux/features/modals.slice';
@@ -8,7 +8,7 @@ import Button from 'src/Components/Button/Button';
import ProjectCardSkeleton from './ProjectDetailsCard.Skeleton'
// import VoteButton from 'src/features/Projects/pages/ProjectPage/VoteButton/VoteButton';
import { NotificationsService, Wallet_Service } from 'src/services'
import { useProjectDetailsQuery } from 'src/graphql';
import { ProjectPermissionEnum, useProjectDetailsQuery } from 'src/graphql';
import Lightbox from 'src/Components/Lightbox/Lightbox'
import linkifyHtml from 'linkify-html';
import ErrorMessage from 'src/Components/Errors/ErrorMessage/ErrorMessage';
@@ -108,6 +108,8 @@ export default function ProjectDetailsCard({ direction, projectId, ...props }: P
},
];
const canEdit = project.permissions.includes(ProjectPermissionEnum.UpdateInfo);
const onVote = (votes?: number) => {
dispatch(setVoteAmount(votes ?? 10));
dispatch(openModal({
@@ -140,9 +142,14 @@ export default function ProjectDetailsCard({ direction, projectId, ...props }: P
{/* Cover Image */}
<div className="relative h-[100px] lg:h-[80px]">
<img className="w-full h-full object-cover" src={project.cover_image} alt="" />
<button className="w-32 h-32 bg-gray-600 bg-opacity-80 text-white absolute top-24 right-24 rounded-full hover:bg-gray-800 text-center" onClick={closeModal}><MdClose className=' inline-block' /></button>
<button className="w-32 h-32 bg-gray-600 bg-opacity-80 text-white absolute top-24 right-24 rounded-full hover:bg-gray-800 text-center flex flex-col justify-center items-center" onClick={closeModal}><MdClose className=' inline-block' /></button>
</div>
<div className="p-24 flex flex-col gap-24">
<div className='flex justify-end'>
<Button color='gray' size='sm' className='ml-auto' onClick={() => props.onClose?.()} href={createRoute({ type: "edit-project", id: project.id })}>Edit Project</Button>
</div>
{/* Title & Basic Info */}
<div className="flex flex-col mt-[-80px] md:flex-row md:mt-0 gap-24 items-start relative">
<div className="flex-shrink-0 w-[108px] h-[108px]">
@@ -171,6 +178,7 @@ export default function ProjectDetailsCard({ direction, projectId, ...props }: P
</div>
</div>
{/* About */}
<div>
<p className="text-body5 text-gray-400 mb-8">About</p>

View File

@@ -1040,19 +1040,21 @@ export type GetAllCapabilitiesQueryVariables = Exact<{ [key: string]: never; }>;
export type GetAllCapabilitiesQuery = { __typename?: 'Query', getAllCapabilities: Array<{ __typename?: 'Capability', id: number, title: string, icon: string }> };
export type ProjectDetailsFragment = { __typename?: 'Project', id: number, title: string, tagline: string, description: string, hashtag: string, cover_image: string, thumbnail_image: string, launch_status: ProjectLaunchStatusEnum, twitter: string | null, discord: string | null, github: string | null, slack: string | null, telegram: string | null, screenshots: Array<string>, website: string, lightning_address: string | null, votes_count: number, permissions: Array<ProjectPermissionEnum>, category: { __typename?: 'Category', id: number, icon: string | null, title: string }, members: Array<{ __typename?: 'ProjectMember', role: Team_Member_Role, user: { __typename?: 'User', id: number, name: string, jobTitle: string | null, avatar: string } }>, awards: Array<{ __typename?: 'Award', title: string, image: string, url: string, id: number }>, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, recruit_roles: Array<{ __typename?: 'MakerRole', id: number, title: string, icon: string, level: RoleLevelEnum }>, capabilities: Array<{ __typename?: 'Capability', id: number, title: string, icon: string }> };
export type CreateProjectMutationVariables = Exact<{
input: InputMaybe<CreateProjectInput>;
}>;
export type CreateProjectMutation = { __typename?: 'Mutation', createProject: { __typename?: 'CreateProjectResponse', project: { __typename?: 'Project', id: number, title: string, description: string, cover_image: string, thumbnail_image: string, screenshots: Array<string>, website: string, lightning_address: string | null, lnurl_callback_url: string | null, votes_count: number, category: { __typename?: 'Category', id: number, icon: string | null, title: string }, members: Array<{ __typename?: 'ProjectMember', role: Team_Member_Role, user: { __typename?: 'User', id: number, name: string, avatar: string } }>, awards: Array<{ __typename?: 'Award', title: string, image: string, url: string, id: number }>, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, recruit_roles: Array<{ __typename?: 'MakerRole', id: number, title: string, icon: string, level: RoleLevelEnum }>, capabilities: Array<{ __typename?: 'Capability', id: number, title: string, icon: string }> } } | null };
export type CreateProjectMutation = { __typename?: 'Mutation', createProject: { __typename?: 'CreateProjectResponse', project: { __typename?: 'Project', id: number, title: string, tagline: string, description: string, hashtag: string, cover_image: string, thumbnail_image: string, launch_status: ProjectLaunchStatusEnum, twitter: string | null, discord: string | null, github: string | null, slack: string | null, telegram: string | null, screenshots: Array<string>, website: string, lightning_address: string | null, votes_count: number, permissions: Array<ProjectPermissionEnum>, category: { __typename?: 'Category', id: number, icon: string | null, title: string }, members: Array<{ __typename?: 'ProjectMember', role: Team_Member_Role, user: { __typename?: 'User', id: number, name: string, jobTitle: string | null, avatar: string } }>, awards: Array<{ __typename?: 'Award', title: string, image: string, url: string, id: number }>, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, recruit_roles: Array<{ __typename?: 'MakerRole', id: number, title: string, icon: string, level: RoleLevelEnum }>, capabilities: Array<{ __typename?: 'Capability', id: number, title: string, icon: string }> } } | null };
export type UpdateProjectMutationVariables = Exact<{
input: InputMaybe<UpdateProjectInput>;
}>;
export type UpdateProjectMutation = { __typename?: 'Mutation', updateProject: { __typename?: 'CreateProjectResponse', project: { __typename?: 'Project', id: number, title: string, description: string, cover_image: string, thumbnail_image: string, screenshots: Array<string>, website: string, lightning_address: string | null, lnurl_callback_url: string | null, votes_count: number, category: { __typename?: 'Category', id: number, icon: string | null, title: string }, members: Array<{ __typename?: 'ProjectMember', role: Team_Member_Role, user: { __typename?: 'User', id: number, name: string, avatar: string } }>, awards: Array<{ __typename?: 'Award', title: string, image: string, url: string, id: number }>, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, recruit_roles: Array<{ __typename?: 'MakerRole', id: number, title: string, icon: string, level: RoleLevelEnum }>, capabilities: Array<{ __typename?: 'Capability', id: number, title: string, icon: string }> } } | null };
export type UpdateProjectMutation = { __typename?: 'Mutation', updateProject: { __typename?: 'CreateProjectResponse', project: { __typename?: 'Project', id: number, title: string, tagline: string, description: string, hashtag: string, cover_image: string, thumbnail_image: string, launch_status: ProjectLaunchStatusEnum, twitter: string | null, discord: string | null, github: string | null, slack: string | null, telegram: string | null, screenshots: Array<string>, website: string, lightning_address: string | null, votes_count: number, permissions: Array<ProjectPermissionEnum>, category: { __typename?: 'Category', id: number, icon: string | null, title: string }, members: Array<{ __typename?: 'ProjectMember', role: Team_Member_Role, user: { __typename?: 'User', id: number, name: string, jobTitle: string | null, avatar: string } }>, awards: Array<{ __typename?: 'Award', title: string, image: string, url: string, id: number }>, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, recruit_roles: Array<{ __typename?: 'MakerRole', id: number, title: string, icon: string, level: RoleLevelEnum }>, capabilities: Array<{ __typename?: 'Capability', id: number, title: string, icon: string }> } } | null };
export type IsValidProjectHashtagQueryVariables = Exact<{
hashtag: Scalars['String'];
@@ -1181,6 +1183,63 @@ export const UserRolesSkillsFragmentDoc = gql`
}
}
`;
export const ProjectDetailsFragmentDoc = gql`
fragment ProjectDetails on Project {
id
title
tagline
description
hashtag
cover_image
thumbnail_image
launch_status
twitter
discord
github
slack
telegram
screenshots
website
lightning_address
votes_count
category {
id
icon
title
}
permissions
members {
role
user {
id
name
jobTitle
avatar
}
}
awards {
title
image
url
id
}
tags {
id
title
}
recruit_roles {
id
title
icon
level
}
capabilities {
id
title
icon
}
}
`;
export const OfficialTagsDocument = gql`
query OfficialTags {
officialTags {
@@ -2477,54 +2536,11 @@ export const CreateProjectDocument = gql`
mutation CreateProject($input: CreateProjectInput) {
createProject(input: $input) {
project {
id
title
description
cover_image
thumbnail_image
screenshots
website
lightning_address
lnurl_callback_url
votes_count
category {
id
icon
title
}
members {
role
user {
id
name
avatar
}
}
awards {
title
image
url
id
}
tags {
id
title
}
recruit_roles {
id
title
icon
level
}
capabilities {
id
title
icon
}
...ProjectDetails
}
}
}
`;
${ProjectDetailsFragmentDoc}`;
export type CreateProjectMutationFn = Apollo.MutationFunction<CreateProjectMutation, CreateProjectMutationVariables>;
/**
@@ -2555,54 +2571,11 @@ export const UpdateProjectDocument = gql`
mutation UpdateProject($input: UpdateProjectInput) {
updateProject(input: $input) {
project {
id
title
description
cover_image
thumbnail_image
screenshots
website
lightning_address
lnurl_callback_url
votes_count
category {
id
icon
title
}
members {
role
user {
id
name
avatar
}
}
awards {
title
image
url
id
}
tags {
id
title
}
recruit_roles {
id
title
icon
level
}
capabilities {
id
title
icon
}
...ProjectDetails
}
}
}
`;
${ProjectDetailsFragmentDoc}`;
export type UpdateProjectMutationFn = Apollo.MutationFunction<UpdateProjectMutation, UpdateProjectMutationVariables>;
/**

View File

@@ -182,3 +182,8 @@ export const getSpanDate = (_date1: string, _date2: string) => {
return `${dayjs(_date1).format('H:mm')} - ${dayjs(_date2).format('H:mm, Do MMM')}`
}
export function removeArrayItemAtIndex<T>(arr: T[], indexToRemove: number) {
return [...arr.slice(0, indexToRemove), ...arr.slice(indexToRemove + 1)]
}