Skip to content

Commit

Permalink
Enable cover image editing for Event Admin (#145)
Browse files Browse the repository at this point in the history
* Implement event cover editing for Admin Event UI

* Testing on preview...

* fix

* Add vercel_env as isDevelopment tracker

* testing

* i will get to the bottom of this

* Retrigger deploy for preview

* Refactor isDevelopment check in config

* More testing

* Cleanup code

* Disable caching of pages

* Remove query params

* Make disableCaching a flag in withAccessType
  • Loading branch information
alexzhang1618 authored Feb 15, 2024
1 parent ee792ac commit b60b9f9
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 54 deletions.
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
NEXT_PUBLIC_ACM_API_URL="https://api.acmucsd.com/api/v2"
NEXT_PUBLIC_KLEFKI_API_URL=""
NEXT_PUBLIC_TOTP_KEY=""
NEXT_PUBLIC_TOTP_KEY=""

# Set this environment variable to any string if you're connecting
# to the production API and want to use production file size limits.
# process.env.NEXT_PUBLIC_PRODUCTION
93 changes: 45 additions & 48 deletions src/components/admin/event/EventDetailsForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const EventDetailsForm = (props: IProps) => {
const [selectedCover, setSelectedCover] = useState<File | null>(null);
const [cover, setCover] = useState<File | null>(null);
const coverUrl = useObjectUrl(cover);
const eventCover = coverUrl || initialValues.cover;

const createEvent: SubmitHandler<FillInLater> = formData => {
if (!cover) {
Expand Down Expand Up @@ -138,6 +139,7 @@ const EventDetailsForm = (props: IProps) => {
start,
end,
},
cover: cover || undefined,
onSuccessCallback: event => {
setLoading(false);
showToast('Event Details Saved!', '', [
Expand Down Expand Up @@ -280,54 +282,49 @@ const EventDetailsForm = (props: IProps) => {
/>
</EventDetailsFormItem>

{/* Only show this in create mode */}
{editing ? null : (
<>
<label htmlFor="cover">Cover Image</label>
<EventDetailsFormItem>
{coverUrl && (
<Image src={coverUrl} alt="Selected cover image" width={480} height={270} />
)}
<input
type="file"
id="cover"
accept="image/*"
onChange={e => {
const file = e.currentTarget.files?.[0];
e.currentTarget.value = '';
if (file) {
setSelectedCover(file);
}
}}
/>
</EventDetailsFormItem>
<Cropper
file={selectedCover}
aspectRatio={1920 / 1080}
maxFileHeight={1080}
maxSize={config.file.MAX_EVENT_COVER_SIZE_KB * 1024}
onCrop={async file => {
setCover(
new File([file], selectedCover?.name ?? 'image', {
type: file.type,
})
);
setSelectedCover(null);
}}
onClose={reason => {
setSelectedCover(null);
if (reason === 'cannot-compress') {
showToast(
'Your image has too much detail and cannot be compressed.',
'Try shrinking your image.'
);
} else if (reason !== null) {
showToast('This image format is not supported.');
}
}}
/>
</>
)}
<label htmlFor="cover">Cover Image</label>
<EventDetailsFormItem>
{eventCover && (
<Image src={eventCover} alt="Selected cover image" width={480} height={270} />
)}
<input
type="file"
id="cover"
accept="image/*"
onChange={e => {
const file = e.currentTarget.files?.[0];
e.currentTarget.value = '';
if (file) {
setSelectedCover(file);
}
}}
/>
</EventDetailsFormItem>
<Cropper
file={selectedCover}
aspectRatio={1920 / 1080}
maxFileHeight={1080}
maxSize={config.file.MAX_EVENT_COVER_SIZE_KB * 1024}
onCrop={async file => {
setCover(
new File([file], selectedCover?.name ?? 'image', {
type: file.type,
})
);
setSelectedCover(null);
}}
onClose={reason => {
setSelectedCover(null);
if (reason === 'cannot-compress') {
showToast(
'Your image has too much detail and cannot be compressed.',
'Try shrinking your image.'
);
} else if (reason !== null) {
showToast('This image format is not supported.');
}
}}
/>
</div>

<div className={style.submitButtons}>
Expand Down
3 changes: 1 addition & 2 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Cat from '@/public/assets/graphics/cat404.png';

const env = process.env.NODE_ENV;
const isDevelopment = env !== 'production';
const isDevelopment = process.env.NEXT_PUBLIC_PRODUCTION === undefined;

const config = {
api: {
Expand Down
8 changes: 7 additions & 1 deletion src/lib/hoc/withAccessType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import type { GetServerSideProps, GetServerSidePropsContext } from 'next';
* @param gssp Server-side props function to run afterwards
* @param validAccessTypes Access types that can see this page
* @param redirectTo URL to send users without valid access level
* @param disableCaching Flag to disable page caching and so SSR props are refetched on redirects.
* @returns
*/
export default function withAccessType(
gssp: GetServerSideProps,
validAccessTypes: UserAccessType[],
redirectTo?: URL
redirectTo?: URL,
disableCaching?: boolean
): GetServerSideProps {
// Generate a new getServerSideProps function by taking the return value of the original function and appending the user prop onto it if the user cookie exists, otherwise force user to login page
const modified: GetServerSideProps = async (context: GetServerSidePropsContext) => {
Expand Down Expand Up @@ -86,6 +88,10 @@ export default function withAccessType(
}
}

if (disableCaching) {
res.setHeader('Cache-Control', 'no-store');
}

return originalReturnValue;
};
return modified;
Expand Down
10 changes: 10 additions & 0 deletions src/lib/managers/AdminEventManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,24 @@ export const createNewEvent = async (

interface EditEventRequest {
event: Partial<Event>;
cover?: File;
uuid: UUID;
}

export const editEvent = async (data: EditEventRequest & AuthAPIHandlerProps) => {
const { onSuccessCallback, onFailCallback, token, event, uuid } = data;
if (data.cover && data.cover.size > config.file.MAX_EVENT_COVER_SIZE_KB * 1024) {
onFailCallback?.(new Error('Cover size too large'));
return;
}

try {
const modifiedEvent = await EventAPI.editEvent(token, uuid, event);
if (data.cover) {
// There's some weird behavior that happens when we editEvent after uploading a new
// event image, so I've kept the API calls in the same order as createNewEvent
await EventAPI.uploadEventImage(token, uuid, data.cover);
}

onSuccessCallback?.(modifiedEvent);
} catch (e: any) {
Expand Down
3 changes: 2 additions & 1 deletion src/pages/admin/event/edit/[uuid].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res })
export const getServerSideProps = withAccessType(
getServerSidePropsFunc,
PermissionService.canManageEvents,
config.admin.homeRoute
config.admin.homeRoute,
true
);
3 changes: 2 additions & 1 deletion src/pages/admin/event/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,6 @@ const getServerSidePropsFunc: GetServerSideProps = async () => {
export const getServerSideProps = withAccessType(
getServerSidePropsFunc,
PermissionService.canManageEvents,
config.admin.homeRoute
config.admin.homeRoute,
true
);

0 comments on commit b60b9f9

Please sign in to comment.