Skip to content

Commit

Permalink
feat: add components for upload
Browse files Browse the repository at this point in the history
  • Loading branch information
IanKrieger committed Aug 29, 2023
1 parent 0f4ab33 commit 6c87a01
Show file tree
Hide file tree
Showing 11 changed files with 466 additions and 10 deletions.
14 changes: 6 additions & 8 deletions src/components/Drawer/MiniSideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,21 @@ export default function MiniSideBar({ children }: PropsWithChildren) {
/>
),
},
// Possible future enhancements, not visible to user but help keep spacing
{
label: "Creatives",
href: "/user/main/creatives",
label: "Assets",
href: "/user/main/assets",
icon: (
<LightbulbOutlinedIcon
<InsertPhotoOutlinedIcon
fontSize="large"
sx={{ color: "text.secondary" }}
/>
),
disabled: true,
},
{
label: "Assets",
href: "/user/main/assets",
label: "Creatives",
href: "/user/main/creatives",
icon: (
<InsertPhotoOutlinedIcon
<LightbulbOutlinedIcon
fontSize="large"
sx={{ color: "text.secondary" }}
/>
Expand Down
164 changes: 164 additions & 0 deletions src/graphql/advertiser.generated.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions src/graphql/advertiser.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,25 @@ query advertiserCampaigns($id: String!, $filter: AdvertiserCampaignFilter) {
...AdvertiserCampaigns
}
}

fragment AdvertiserImage on AdvertiserImage {
name
imageUrl
format
id
createdAt
}

query advertiserImages($id: String!) {
advertiser(id: $id) {
images {
...AdvertiserImage
}
}
}

mutation uploadAdvertiserImage($input: CreateAdvertiserImageInput!) {
createAdvertiserImage(createImageInput: $input) {
...AdvertiserImage
}
}
1 change: 1 addition & 0 deletions src/graphql/types.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/user/User.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ export function User() {
unauthedComponent={AdvertiserAgreed}
/>

<ProtectedRoute
path="/user/main/assets"
authedComponent={CampaignView}
/>

{/* default */}
<Redirect to="/user/main/campaign" />
</Switch>
Expand Down
127 changes: 127 additions & 0 deletions src/user/hooks/useUploadFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { buildAdServerEndpoint, getEnvConfig } from "util/environment";
import { useCallback, useState } from "react";
import _ from "lodash";
import { useUploadAdvertiserImageMutation } from "graphql/advertiser.generated";
import { useAdvertiser } from "auth/hooks/queries/useAdvertiser";
import { CampaignFormat } from "graphql/types";
import { UploadConfig } from "user/views/advertiser/UploadImage";

interface PutUploadResponse {
// the pre-signed url to which the file should be uploaded to
uploadUrl: string;
// the path on the cdn that this url will eventually have
destinationPath: string;
}

export const useUploadFile = () => {
const { advertiser } = useAdvertiser();
const [error, setError] = useState<string>();
const [step, setStep] = useState<number>(0);
const [state, setState] = useState<string>();
const [loading, setLoading] = useState(false);
const [mutate] = useUploadAdvertiserImageMutation({
onError(e) {
setError(e.message);
setLoading(false);
},
onCompleted(data) {
setStep(2);
setState(`File upload complete for ${data.createAdvertiserImage.name}!`);
setLoading(false);
},
});

const uploadFile = useCallback(async (file: File, format: CampaignFormat) => {
setError(undefined);
setLoading(true);
setState("Preparing file for upload...");

let upload: PutUploadResponse;
try {
const extension = _.last(file.name.split(".")) ?? "";
upload = await prepareForUpload(extension);
} catch (e: any) {
setError(e.message);
return;
}

try {
setState("Uploading file...");
await putFile(file, upload);
setStep(1);
} catch (e: any) {
setError(e.message);
return;
}

await mutate({
variables: {
input: {
format: format,
advertiserId: advertiser.id,
name: file.name,
imageUrl: `https://${configForFormat(format).targetHost}${
upload.destinationPath
}`,
},
},
});
}, []);

return [{ upload: uploadFile }, { state, step, loading, error }];
};

async function prepareForUpload(extension: string): Promise<PutUploadResponse> {
const resp = await fetch(
buildAdServerEndpoint(`/internal/image-upload/${extension}`),
{
method: "GET",
mode: "cors",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
},
);
if (!resp.ok) {
throw new Error(`Unable to upload image`);
}
const result = (await resp.json()) as PutUploadResponse;
if (!result.destinationPath || !result.uploadUrl) {
throw new Error(`Unable to upload image`);
}

return result;
}

async function putFile(file: File, uploadTarget: PutUploadResponse) {
try {
const resp = await fetch(uploadTarget.uploadUrl, {
method: "PUT",
mode: "cors",
body: file,
});

if (!resp.ok) {
await resp.text();
throw new Error(`Failed to upload image`);
}
} catch (e: any) {
if (e.message === "Failed to fetch") {
throw new Error(`Failed to upload image`);
}
throw e;
}
}

const configForFormat = (format: CampaignFormat): UploadConfig => {
if (format === CampaignFormat.NewsDisplayAd) {
return {
targetHost: () => getEnvConfig().pcdnHost,
requiresPublishStep: false,
endpoint: "internal/image-upload",
};
}

throw new Error("Invalid format");
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function CampaignSettings() {
<CampaignDateRange />
</CardContainer>

{isDraft && <FormatField />}
<FormatField />

{isDraft && <LocationField />}
</>
Expand Down
Loading

0 comments on commit 6c87a01

Please sign in to comment.