Skip to content

Commit

Permalink
Merge pull request #138 from Proof-Of-Humanity/testnets
Browse files Browse the repository at this point in the history
Testnets
  • Loading branch information
martillansky authored Nov 5, 2024
2 parents 2522acc + 669d215 commit c32ec80
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 30 deletions.
1 change: 1 addition & 0 deletions src/app/[pohid]/claim/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export default withClientConnected<FormProps & JSX.IntrinsicAttributes>(
advance={() => step$.set(Step.review)}
video$={media$.video}
isRenewal={!!renewal}
videoError={(ErrMsg) => toast.error(ErrMsg)}
/>
),
[Step.review]: () => (
Expand Down
53 changes: 38 additions & 15 deletions src/app/[pohid]/claim/Photo.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
"use client";

import { useRef, useState } from "react";
import Cropper from "react-easy-crop";
import type { Area, Point } from "react-easy-crop/types";
import { toast } from "react-toastify";
import ReactWebcam from "react-webcam";
import { ObservableObject } from "@legendapp/state";
import Previewed from "components/Previewed";
import Uploader from "components/Uploader";
import Webcam from "components/Webcam";
import useFullscreen from "hooks/useFullscreen";
import { useLoading } from "hooks/useLoading";
import { getCroppedPhoto, sanitizeImage } from "utils/media";
import { base64ToUint8Array } from "utils/misc";
import { ObservableObject } from "@legendapp/state";
import Image, { StaticImageData } from "next/image";
import { MediaState } from "./Form";
import Previewed from "components/Previewed";
import CameraIcon from "icons/camera.svg";
import CircleCancel from "icons/CircleCancelMinor.svg";
import CircleTick from "icons/CircleTickMinor.svg";
import CheckmarkIcon from "icons/MobileAcceptMajor.svg";
import ResetIcon from "icons/ResetMinor.svg";
import ZoomIcon from "icons/SearchMajor.svg";
import CameraIcon from "icons/camera.svg";
import UploadIcon from "icons/upload.svg";
import Image, { StaticImageData } from "next/image";
import { useRef, useState } from "react";
import Cropper from "react-easy-crop";
import type { Area, Point } from "react-easy-crop/types";
import { toast } from "react-toastify";
import ReactWebcam from "react-webcam";
import { getCroppedPhoto, sanitizeImage } from "utils/media";
import { base64ToUint8Array } from "utils/misc";
import { MediaState } from "./Form";

const MIN_DIMS = { width: 256, height: 256 }; // PXs
const MAX_SIZE = 3; // Megabytes
const MAX_SIZE_BYTES = 1024 * 1024 * MAX_SIZE; // Bytes
const ERROR_MSG = {
dimensions: `Photo dimensions are too small. Minimum dimensions are ${MIN_DIMS.width}px by ${MIN_DIMS.height}px`,
size: `Photo is oversized. Maximum allowed size is ${MAX_SIZE}mb`,
};

interface PhotoProps {
advance: () => void;
Expand Down Expand Up @@ -70,8 +78,13 @@ function Photo({ advance, photo$ }: PhotoProps) {

const onCrop = async () => {
if (!cropPixels || !originalPhoto) return;
if (cropPixels.width < 256 || cropPixels.height < 256)
return console.error("Size error");
if (
cropPixels.width < MIN_DIMS.width ||
cropPixels.height < MIN_DIMS.height
) {
toast.error(ERROR_MSG.dimensions);
return console.error("Dimensions error");
}

loading.start("Cropping photo");

Expand All @@ -82,6 +95,11 @@ function Photo({ advance, photo$ }: PhotoProps) {
const sanitized = await sanitizeImage(
Buffer.from(base64ToUint8Array(cropped.split(",")[1])),
);
if (sanitized.size > MAX_SIZE_BYTES) {
toast.error(ERROR_MSG.size);
//return console.error("Size error");
}

photo$.set({ content: sanitized, uri: URL.createObjectURL(sanitized) });
} catch (err: any) {
toast.error(err.message);
Expand Down Expand Up @@ -244,8 +262,13 @@ function Photo({ advance, photo$ }: PhotoProps) {
onCropChange={setCrop}
onCropComplete={(_area, croppedPixels) => {
setCropPixels(croppedPixels);
if (croppedPixels.width < 256 || croppedPixels.height < 256)
if (
croppedPixels.width < MIN_DIMS.width ||
croppedPixels.height < MIN_DIMS.height
) {
toast.error(ERROR_MSG.dimensions);
console.error("Size error");
}
}}
onZoomChange={setZoom}
onMediaLoaded={(media) => {
Expand Down
60 changes: 45 additions & 15 deletions src/app/[pohid]/claim/Video.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import getBlobDuration from "get-blob-duration";
import React, { useRef, useState } from "react";
import ReactWebcam from "react-webcam";
import { ObservableObject } from "@legendapp/state";
import Uploader from "components/Uploader";
import Webcam from "components/Webcam";
import { IS_IOS } from "utils/media";
import getBlobDuration from "get-blob-duration";
import useFullscreen from "hooks/useFullscreen";
import { useAccount } from "wagmi";
import { ObservableObject } from "@legendapp/state";
import { MediaState } from "./Form";
import CameraIcon from "icons/CameraMajor.svg";
import ResetIcon from "icons/ResetMinor.svg";
import UploadIcon from "icons/upload.svg";
import React, { useRef, useState } from "react";
import ReactWebcam from "react-webcam";
import { IS_IOS } from "utils/media";
import { useAccount } from "wagmi";
import { MediaState } from "./Form";

const MIN_DIMS = { width: 352, height: 352 };

const MIN_DIMS = { width: 352, height: 352 }; // PXs
//const MAX_DURATION = 20; // Seconds
//const MAX_SIZE = 7; // Megabytes
const MAX_DURATION = 60 * 2; // Seconds
const MAX_SIZE = undefined;
const MAX_SIZE_BYTES = MAX_SIZE ? 1024 * 1024 * MAX_SIZE : MAX_SIZE; // Bytes
const ERROR_MSG = {
duration: `Video is too long. Maximum allowed duration is ${MAX_DURATION} seconds long`,
dimensions: `Video dimensions are too small. Minimum dimensions are ${MIN_DIMS.width}px by ${MIN_DIMS.height}px`,
size: `Video is oversized. Maximum allowed size is ${MAX_SIZE}mb`,
};
interface PhotoProps {
advance: () => void;
video$: ObservableObject<MediaState["video"]>;
isRenewal: boolean;
videoError: (error: string) => void;
}

function VideoStep({ advance, video$, isRenewal }: PhotoProps) {
function VideoStep({ advance, video$, isRenewal, videoError }: PhotoProps) {
const video = video$.use();

const { address } = useAccount();
Expand All @@ -35,6 +45,21 @@ function VideoStep({ advance, video$, isRenewal }: PhotoProps) {

const [recorder, setRecorder] = useState<MediaRecorder | null>(null);

const checkVideoSize = (blob: Blob) => {
if (MAX_SIZE_BYTES && blob.size > MAX_SIZE_BYTES) {
videoError(ERROR_MSG.size);
return console.error(ERROR_MSG.size);
}
};

const checkVideoDuration = async (blob: Blob) => {
const duration = await getBlobDuration(blob);
if (duration > MAX_DURATION) {
videoError(ERROR_MSG.duration);
return console.error(ERROR_MSG.duration);
}
};

const startRecording = () => {
if (!camera || !camera.stream) return;
const mediaRecorder = new MediaRecorder(camera.stream, {
Expand All @@ -46,6 +71,10 @@ function VideoStep({ advance, video$, isRenewal }: PhotoProps) {
const blob = new Blob(newlyRecorded, {
type: `${IS_IOS ? 'video/mp4;codecs="h264"' : 'video/webm;codecs="vp8"'}`,
});

await checkVideoDuration(blob);
checkVideoSize(blob);

video$.set({ content: blob, uri: URL.createObjectURL(blob) });
setShowCamera(false);
};
Expand Down Expand Up @@ -115,9 +144,8 @@ function VideoStep({ advance, video$, isRenewal }: PhotoProps) {
const blob = new Blob([file], { type: file.type });
const uri = URL.createObjectURL(blob);

const duration = await getBlobDuration(blob);
if (duration > 60 * 2) return console.error("Video is too long");

await checkVideoDuration(blob);
checkVideoSize(blob);
const vid = document.createElement("video");
vid.crossOrigin = "anonymous";
vid.src = uri;
Expand All @@ -127,8 +155,10 @@ function VideoStep({ advance, video$, isRenewal }: PhotoProps) {
if (
vid.videoWidth < MIN_DIMS.width ||
vid.videoHeight < MIN_DIMS.height
)
return console.error("Video dimensions are too small");
) {
videoError(ERROR_MSG.dimensions);
return console.error(ERROR_MSG.dimensions);
}

setRecording(false);
video$.set({ uri, content: blob });
Expand Down

0 comments on commit c32ec80

Please sign in to comment.