Skip to content

Commit

Permalink
feat: add gallery, fix preview
Browse files Browse the repository at this point in the history
  • Loading branch information
IanKrieger committed Aug 31, 2023
1 parent 67bc879 commit 00caefa
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 115 deletions.
93 changes: 51 additions & 42 deletions src/components/Assets/AdvertiserAssets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,72 @@ import {
useAdvertiserImagesQuery,
} from "graphql/advertiser.generated";
import { useAdvertiser } from "auth/hooks/queries/useAdvertiser";
import {
ColumnDescriptor,
EnhancedTable,
StandardRenderers,
} from "components/EnhancedTable";
import { ErrorDetail } from "components/Error/ErrorDetail";
import { CardContainer } from "components/Card/CardContainer";
import { Box, Skeleton } from "@mui/material";
import { Grid, LinearProgress, Typography } from "@mui/material";
import MiniSideBar from "components/Drawer/MiniSideBar";
import { UploadImage } from "components/Assets/UploadImage";
import { ImagePreview } from "components/Assets/ImagePreview";
import { CampaignFormat } from "graphql/types";
import moment from "moment/moment";

export function AdvertiserAssets() {
const { advertiser } = useAdvertiser();
const { data, loading, error } = useAdvertiserImagesQuery({
variables: { id: advertiser.id },
});

const options: ColumnDescriptor<AdvertiserImageFragment>[] = [
{
title: "Created",
value: (c) => c.createdAt,
renderer: StandardRenderers.date,
},
{
title: "Name",
value: (c) => c.name,
},
{
title: "Image",
value: (c) => c.imageUrl,
extendedRenderer: (r) => <ImagePreview url={r.imageUrl} />,
},
];
if (loading) {
return (
<MiniSideBar>
<LinearProgress sx={{ mt: 1, flexGrow: 1 }} />
</MiniSideBar>
);
}

const images = (data?.advertiser?.images ?? []).sort(
(a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
);
return (
<MiniSideBar>
<CardContainer header="Assets" sx={{ minWidth: "50%" }}>
{loading && (
<Box m={3}>
<Skeleton variant="rounded" height={500} />
</Box>
)}
{error && (
<ErrorDetail
error={error}
additionalDetails="Unable to retrieve images"
/>
)}
{!loading && !error && (
<EnhancedTable
rows={data?.advertiser?.images ?? []}
columns={options}
/>
)}
</CardContainer>
<UploadImage />
{error && (
<ErrorDetail
error={error}
additionalDetails="Unable to retrieve images"
/>
)}
{!loading && !error && (
<Grid container spacing={2}>
{images.map((i, idx) => (
<Grid item xs="auto" key={idx}>
<GalleryItem image={i} />
</Grid>
))}
<Grid item xs="auto">
<UploadImage />
</Grid>
</Grid>
)}
</MiniSideBar>
);
}

const GalleryItem = (props: { image: AdvertiserImageFragment }) => {
const { name, imageUrl, createdAt, format } = props.image;
const height = format === CampaignFormat.NewsDisplayAd ? 400 : undefined;
const width = format === CampaignFormat.NewsDisplayAd ? 500 : undefined;

return (
<CardContainer header={name}>
<ImagePreview url={imageUrl} height={height} width={width} />
<Typography
variant="caption"
color="text.primary"
textAlign="right"
fontWeight={500}
>
created {moment(createdAt).fromNow()}
</Typography>
</CardContainer>
);
};
63 changes: 37 additions & 26 deletions src/components/Assets/ImageAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { useAdvertiser } from "auth/hooks/queries/useAdvertiser";
import { Autocomplete, createFilterOptions, TextField } from "@mui/material";
import { useState } from "react";
import { useField } from "formik";
import { UploadImage } from "components/Assets/UploadImage";

type ImageOption = { label: string; image?: string };

const filter = createFilterOptions<ImageOption>();

export function ImageAutocomplete() {
const [createImage, setCreateImage] = useState(false);
const [, meta, imageUrl] = useField<string | undefined>(
`newCreative.payloadInlineContent.imageUrl`,
);
Expand All @@ -30,31 +32,40 @@ export function ImageAutocomplete() {
});

return (
<Autocomplete
disablePortal
loading={!options || loading}
options={options ?? []}
renderInput={(params) => (
<TextField
{...params}
label="Image"
helperText={showError ? meta.error : undefined}
error={showError}
margin="normal"
/>
)}
value={{
label: options?.find((o) => o.image === meta.value)?.label ?? "",
image: meta.value,
}}
getOptionLabel={(o) => o.label}
filterOptions={(options, params) => {
const filtered = filter(options, params);
return [...filtered, { image: undefined, label: `Upload new image` }];
}}
onChange={(e, nv) => {
imageUrl.setValue(nv ? nv.image : undefined);
}}
/>
<>
<Autocomplete
disablePortal
loading={!options || loading}
options={options ?? []}
renderInput={(params) => (
<TextField
{...params}
label="Image"
helperText={showError ? meta.error : undefined}
error={showError}
margin="normal"
/>
)}
value={{
label: options?.find((o) => o.image === meta.value)?.label ?? "",
image: meta.value,
}}
getOptionLabel={(o) => o.label}
filterOptions={(options, params) => {
const filtered = filter(options, params);
return [...filtered, { image: undefined, label: `Upload new image` }];
}}
onChange={(e, nv) => {
imageUrl.setValue(nv ? nv.image : undefined);
setCreateImage(nv != null && nv.image === undefined);
}}
/>

<UploadImage
useInlineCreation={createImage}
onClose={() => setCreateImage(false)}
onComplete={(url) => imageUrl.setValue(url)}
/>
</>
);
}
21 changes: 18 additions & 3 deletions src/components/Assets/ImagePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ interface Props {
url: string;
height?: number;
width?: number;
selected?: boolean;
}

export const ImagePreview = ({ url, height = 300, width = 360 }: Props) => {
export const ImagePreview = ({
url,
height = 300,
width = 360,
selected,
}: Props) => {
const { data, loading, error } = useGetImagePreviewUrl({ url });

if (!data || loading) {
return <Skeleton variant="rectangular" height={height} />;
return <Skeleton variant="rectangular" height={height} width={width} />;
}

if (error) {
Expand All @@ -28,7 +34,16 @@ export const ImagePreview = ({ url, height = 300, width = 360 }: Props) => {
}}
>
{url?.endsWith(".pad") ? (
<img height={height} width={width} src={data} alt="preview" />
<img
height={height}
width={width}
src={data}
alt="preview"
style={{
opacity: selected === false ? 0.3 : 1,
filter: selected === false ? "grayscale(20%)" : "none",
}}
/>
) : (
<Link underline="none" href={url} target="_blank" rel="noreferrer">
<img
Expand Down
53 changes: 45 additions & 8 deletions src/components/Assets/UploadImage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import {
Alert,
Box,
Expand All @@ -16,21 +16,53 @@ import {
} from "@mui/material";
import { CampaignFormat } from "graphql/types";
import { useUploadFile } from "components/Assets/hooks/useUploadFile";
import { CardContainer } from "components/Card/CardContainer";
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";

export interface UploadConfig {
targetHost: () => string;
requiresPublishStep: boolean;
endpoint: string;
}

export function UploadImage() {
interface Props {
useInlineCreation?: boolean;
onComplete?: (url: string) => void;
onClose?: () => void;
}

export function UploadImage({ useInlineCreation, onClose, onComplete }: Props) {
const [open, setOpen] = useState(false);
const [file, setFile] = useState<File>();
const [{ upload, reset }, { step, error, loading, state }] = useUploadFile();
const [{ upload, reset }, { step, error, loading, state }] = useUploadFile({
onComplete(data) {
if (useInlineCreation && onComplete) {
onComplete(data);
}
},
});

useEffect(() => {
if (useInlineCreation) {
setOpen(true);
}
}, [useInlineCreation]);

return (
<Box>
<Button onClick={() => setOpen(true)}>Upload Image</Button>
<>
{useInlineCreation === undefined && (
<CardContainer header="Upload Image">
<Box
component={Button}
borderRadius="12px"
onClick={() => setOpen(true)}
width={200}
height={423}
>
<AddCircleOutlineIcon fontSize="large" />
</Box>
</CardContainer>
)}
<Dialog open={open} onClose={() => setOpen(false)}>
<DialogTitle>Upload Image</DialogTitle>
<DialogContent>
Expand Down Expand Up @@ -69,7 +101,11 @@ export function UploadImage() {
</Button>
)}
{step === 0 && !!file && (
<Chip onDelete={() => setFile(undefined)} label={file.name} />
<Chip
onDelete={() => setFile(undefined)}
label={file.name}
sx={{ marginBottom: 1 }}
/>
)}

{!error && state && (
Expand All @@ -85,14 +121,15 @@ export function UploadImage() {
setOpen(false);
setFile(undefined);
reset!();
if (onClose) onClose();
}}
variant="outlined"
>
{step === 2 ? "Close" : "Cancel"}
</Button>
{step !== 2 && (
<Button
disabled={file === undefined}
disabled={file === undefined || step !== 0}
onClick={() => {
upload!(file!, CampaignFormat.NewsDisplayAd);
}}
Expand All @@ -103,6 +140,6 @@ export function UploadImage() {
)}
</DialogActions>
</Dialog>
</Box>
</>
);
}
16 changes: 12 additions & 4 deletions src/components/Assets/hooks/useUploadFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ interface PutUploadResponse {
destinationPath: string;
}

export const useUploadFile = () => {
interface Props {
onComplete?: (data: string) => void;
}

export const useUploadFile = ({ onComplete }: Props = {}) => {
const { advertiser } = useAdvertiser();
const [url, setUrl] = useState<string>();
const [error, setError] = useState<string>();
const [step, setStep] = useState<number>(0);
const [state, setState] = useState<string>();
Expand All @@ -35,6 +40,7 @@ export const useUploadFile = () => {
setStep(2);
setState(`File upload complete for "${data.createAdvertiserImage.name}"`);
setLoading(false);
if (onComplete && url) onComplete(url);
},
});

Expand Down Expand Up @@ -64,15 +70,17 @@ export const useUploadFile = () => {
return;
}

const imageUrl = `https://${configForFormat(format).targetHost()}${
upload.destinationPath
}`;
setUrl(imageUrl);
await mutate({
variables: {
input: {
format: format,
advertiserId: advertiser.id,
name: file.name,
imageUrl: `https://${configForFormat(format).targetHost}${
upload.destinationPath
}`,
imageUrl,
},
},
});
Expand Down
Loading

0 comments on commit 00caefa

Please sign in to comment.