diff --git a/.gitignore b/.gitignore index 713d500..c55fc74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules/ .env + +# Jetbrains files +.idea/ diff --git a/client/components/instant/instant.tsx b/client/components/instant/instant.tsx index 6a44d80..2e667c2 100644 --- a/client/components/instant/instant.tsx +++ b/client/components/instant/instant.tsx @@ -9,7 +9,7 @@ import axios from "axios"; import { useRouter } from "next/router"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faAdd, faArrowCircleRight, faArrowLeft, faArrowRight, faCaretDown, faCaretLeft, faCaretRight, faCaretUp, faCheck, faCross, faDownload, faFaceFrown, faFaceSadCry, faFaceSmile, faPlusCircle, faTrashCan, faPlayCircle } from "@fortawesome/free-solid-svg-icons"; +import { faCaretDown, faCaretLeft, faCaretRight, faCaretUp, faCheck, faCross, faDownload, faFaceFrown, faFaceSmile, faTrashCan, faPlayCircle } from "@fortawesome/free-solid-svg-icons"; import Carousel from 'react-multi-carousel'; import 'react-multi-carousel/lib/styles.css'; @@ -33,9 +33,9 @@ export default function Instant({ instance, mymojis }: _Instant) { setCommentLoading(true); let token = localStorage.getItem("token"); - let body = JSON.stringify({ - "token": token, - "instance_id": instance.instanceid, + let body = JSON.stringify({ + "token": token, + "instance_id": instance.instanceid, "poster_user_id": instance.user.uid, "comment": comment }); @@ -113,7 +113,7 @@ export default function Instant({ instance, mymojis }: _Instant) { setLocation("No location data"); } } - + let [reactionSuccess, setReactionSuccess] = useState(false); let [reactionFailure, setReactionFailure] = useState(false); let [addingmoji, setAddingmoji] = useState(false); @@ -142,10 +142,10 @@ export default function Instant({ instance, mymojis }: _Instant) { setReactionSuccess(true); setTimeout(() => { setReactionSuccess(false); router.reload() }, 2000); } - ).catch((error) => { - console.log(error); - setReactionLoading(false); - setReactionFailure(true); + ).catch((error) => { + console.log(error); + setReactionLoading(false); + setReactionFailure(true); setTimeout(() => { setReactionFailure(false) }, 2000); }) } @@ -177,7 +177,7 @@ export default function Instant({ instance, mymojis }: _Instant) { link.click(); link2.click(); - + document.body.removeChild(link); document.body.removeChild(link2); } @@ -215,7 +215,7 @@ export default function Instant({ instance, mymojis }: _Instant) { } { - instance.btsMedia != undefined ? + instance.btsMedia != undefined ?
@@ -337,7 +337,7 @@ export default function Instant({ instance, mymojis }: _Instant) {
setExpanded(!expanded)}> { - !expanded ? + !expanded ? <> expand comments : <> collapse comments} @@ -368,4 +368,4 @@ export default function Instant({ instance, mymojis }: _Instant) {
) -} \ No newline at end of file +} diff --git a/client/components/memoire/memoire.tsx b/client/components/memoire/memoire.tsx index 741c820..31378f4 100644 --- a/client/components/memoire/memoire.tsx +++ b/client/components/memoire/memoire.tsx @@ -1,7 +1,7 @@ import Memory from "@/models/memory" import s from "./memoire.module.scss" import Draggable from "react-draggable" -import { useEffect, useState } from "react"; +import { useState } from "react"; import axios from "axios"; @@ -64,4 +64,4 @@ export default function Memoire({ memory }: { memory: Memory }) { ) -} \ No newline at end of file +} diff --git a/client/components/realmoji/realmoji.tsx b/client/components/realmoji/realmoji.tsx index a3a37b8..5e07b3d 100644 --- a/client/components/realmoji/realmoji.tsx +++ b/client/components/realmoji/realmoji.tsx @@ -1,8 +1,6 @@ - import s from "./realmoji.module.scss" import l from "@/styles/loader.module.scss"; -import { useEffect, useState } from "react"; -import axios from "axios"; +import { useState } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheck, faUpload } from "@fortawesome/free-solid-svg-icons"; import { useRouter } from "next/router"; @@ -119,9 +117,9 @@ export default function Realmoji({ emoji, realmoji }: RealmojiProperties) { (realmoji[emoji] != undefined) ? ( - isFilePicked ? - - : + isFilePicked ? + + : ) : @@ -142,10 +140,10 @@ export default function Realmoji({ emoji, realmoji }: RealmojiProperties) { isFilePicked ? - - - : -
-
- enter the one time passcode -
-
- {setInputOTP(event.target.value);}} placeholder={'000111'}> - -
-
- } -
- { - failed != "" ? - - {failed} - : - } - { - help != "" ? -
- {help} -
: - } -
- -
-

TooFake is currently working (I think??) but needs your help!!

-

BeReal continues to beef up its security making it much harder to reverse engineer. If you are well versed in reverse engineering, please check out the github and help us keep the befake project working!

- {/*

You can login using your phone number, view bereals and post custom images.

+export default function Home(){ + useCheck(); + + let router = useRouter(); + + let [vonageId, setVonageId] = useState(""); + let [firebaseSession, setFirebaseSession] = useState(""); + let [inputNumber, setInputNumber] = useState(""); + let [inputOTP, setInputOTP] = useState(""); + let [requestedOtp, setRequestedOtp] = useState(false); + + let [failed, setFailed] = useState(""); + let [help, setHelp] = useState(""); + + const handleFailure = (text: string) => { + console.log(text); + setFailed(`ERROR: ${text}`); + setTimeout(() => setFailed(""), 4000); + } + + const handleHelp = (text: string) => { + setHelp(text); + setTimeout(() => setHelp(""), 4000); + } + + const verifyOTP = async (otp: string, provider: 'vonage' | 'firebase') => { + const url = provider === 'vonage' ? "/api/otp/vonage/verify" : "/api/otp/fire/verify"; + const body = provider === 'vonage' ? { code: otp, vonageRequestId: vonageId } : { + code: otp, + session_info: firebaseSession + }; + + try { + const response = await axios.post(url, body, { headers: { 'Content-Type': 'application/json' } }); + const data = response.data; + localStorage.setItem("token", data.bereal_access_token); + localStorage.setItem("firebase_refresh_token", data.firebase_refresh_token); + localStorage.setItem("firebase_id_token", data.firebase_id_token); + localStorage.setItem("expiration", data.expiration); + localStorage.setItem("uid", data.uid); + localStorage.setItem("is_new_user", data.is_new_user); + localStorage.setItem("token_type", data.token_type); + await myself(); + router.push("/feed"); + } catch (error: any) { + handleFailure(`${provider.toUpperCase()} VERIFY ERROR: ${error.response?.data.error || "unknown error, please try re-logging in"}`); + if (provider === 'firebase') { + handleHelp("Failed with Firebase login provider, re-trying to login with Vonage..."); + await requestOTP(inputNumber, 'vonage'); + } + } + } + + const requestOTP = async (number: string, provider: 'vonage' | 'firebase') => { + const url = provider === 'vonage' ? "/api/otp/vonage/send" : "/api/otp/fire/send"; + const body = { number }; + + try { + const response = await axios.post(url, body, { headers: { 'Content-Type': 'application/json' } }); + if (provider === 'vonage') { + setVonageId(response.data.vonageRequestId); + } else { + setFirebaseSession(response.data.session_info); + } + setRequestedOtp(true); + + } catch (error: any) { + handleFailure(`${provider.toUpperCase()} OTP REQUEST ERROR: ${JSON.stringify(error.response?.data.error || error)}`); + if (provider === 'firebase') { + handleHelp("Re-trying to login with Vonage..."); + + // try to login automatically with vonage if firebase fails + await requestOTP(number, 'vonage'); + } + } + }; + + return ( +
+
+ {!requestedOtp ? ( +
+
+ login using your phone number +
+
+ setInputNumber('+' + phone)} + inputClass={s.digits} + dropdownClass={s.dropdown} + searchClass={s.search} + buttonClass={s.button} + containerClass={s.cont} + /> + +
+
+ ) : ( +
+
+ enter the one time passcode +
+
+ { + setInputOTP(event.target.value); + }} + placeholder={'000111'} + /> + +
+
+ )} +
+ {failed && {failed}} + {help &&
{help}
} +
+
+
+

TooFake is currently working (I think??) but needs your help!!

+

BeReal continues to beef up its security making it much harder to reverse engineer. If you are well versed in reverse engineering, please check out the + github and help us keep + the befake project working! +

+ {/*

You can login using your phone number, view bereals and post custom images.

Please report any bugs or issues on the github theres probably a bunch!

More features coming soon!

*/} - {/*

*/} - {/*

- There has been increased reports of login not working in the UK & other countries

*/} -
-
- ) + {/*

*/} + {/*

- There has been increased reports of login not working in the UK & other countries

*/} +
+ + ); } diff --git a/client/pages/layout.tsx b/client/pages/layout.tsx index 4fb4391..cf87d99 100644 --- a/client/pages/layout.tsx +++ b/client/pages/layout.tsx @@ -1,6 +1,5 @@ import Divider from "@/components/divider/divider" import Navbar from "@/components/navbar/navbar" -import useCheck from "@/utils/check" export default function Layout({ children }: any) { return ( @@ -10,4 +9,4 @@ export default function Layout({ children }: any) {
{children}
) -} \ No newline at end of file +} diff --git a/client/pages/memories/index.tsx b/client/pages/memories/index.tsx index 818c571..164ca9c 100644 --- a/client/pages/memories/index.tsx +++ b/client/pages/memories/index.tsx @@ -1,89 +1,77 @@ - import React, { useState } from 'react' import { useEffect } from 'react' import axios from 'axios' import useCheck from '@/utils/check'; -import myself from '@/utils/myself'; import s from './memories.module.scss' import l from '@/styles/loader.module.scss'; -import User from '@/models/user'; -import Link from 'next/link'; import Memory from '@/models/memory'; -import Draggable from 'react-draggable'; import Memoire from '@/components/memoire/memoire'; import JSZip from 'jszip'; import FileSaver from 'file-saver'; - -// Made memories global for downloading (kinda ugly) -let newmemories: Memory[] = []; - -export default function Memories() { +export default function Memories(){ useCheck(); let [memories, setMemories] = useState([]); let [loading, setLoading] = useState(true); - useEffect(() => { - - let token = localStorage.getItem("token"); - let body = JSON.stringify({ "token": token }); - let options = { - url: "/api/memories", - method: "POST", - headers: { 'Content-Type': 'application/json' }, - data: body, - } - - axios.request(options).then( - async (response) => { - console.log(response.data); - let memorydata = response.data.data; - - async function createMemory(data: any) { - let newmemory = await Memory.create(data); - newmemories.push(newmemory); - return newmemory; - } - - for (let i = 0; i < memorydata.length; i++) { - try { - await createMemory(memorydata[i]); - setLoading(false); - setMemories([...newmemories]); - } catch (error) { - console.log("COULDN'T MAKE MEMORY WITH DATA: ", memorydata[i]) - console.log(error); - } + const createMemories = async (memoryData: any[]): Promise => { + return Promise.all(memoryData.map(async (data) => { + try { + return await Memory.create(data); + } catch (error) { + console.log("COULDN'T MAKE MEMORY WITH DATA: ", data); + console.log(error); + throw error; // Let the error propagate to the Promise.all level + } + })); + }; - } - console.log("newmemories"); - console.log(newmemories); + useEffect(() => { + const fetchMemories = async () => { + try { + const token = localStorage.getItem("token"); + const body = JSON.stringify({ token }); + const options = { + url: "/api/memories", + method: "POST", + headers: { 'Content-Type': 'application/json' }, + data: body, + }; + + const response = await axios.request(options); + const memoryData = response.data.data; + + const memories: Memory[] = await createMemories(memoryData); + + setMemories(memories); + } catch (error) { + console.log(error); + } finally { + setLoading(false); } - ).catch((error) => { console.log(error); }) - }, []); - + }; + fetchMemories(); + }, []); return ( -
{ loading ?
: memories.map((memory, index) => { return ( - + ) }) }
-
- +

@@ -92,7 +80,7 @@ export default function Memories() {

- +
@@ -109,216 +97,135 @@ export default function Memories() {
- - ) - } -async function downloadMemories() { - - // Note: this is JS code not TS which is why it's throwing an error but runs fine - - // @ts-ignore: Object is possibly 'null'. - let separateImages = document.getElementById("separate").checked; - // @ts-ignore: Object is possibly 'null'. - let mergedImage = document.getElementById("merged").checked; - // @ts-ignore: Object is possibly 'null'. - let status = document.getElementById("downloadStatus"); - // @ts-ignore: Object is possibly 'null'. - let error = document.getElementById("error"); - // @ts-ignore: Object is possibly 'null'. - let downloadButton = document.getElementById("download"); - - // Reset text - // @ts-ignore: Object is possibly 'null'. - status.textContent = ""; - // @ts-ignore: Object is possibly 'null'. - error.textContent = ""; - - // Don't do anything if no boxes are checked - if (!(separateImages || mergedImage)) { - - // @ts-ignore: Object is possibly 'null'. - status.textContent = "No export option selected."; - return; - } - - - // Disable download button - // @ts-ignore: Object is possibly 'null'. - downloadButton.disabled = true; - - - let zip = new JSZip(); - - // Loop through each memory - for (let i = 0; i < newmemories.length; i++) { - - let memory = newmemories[i]; - - // Update memory status - // @ts-ignore: Object is possibly 'null'. - status.textContent = `Zipping, ${(((i + 1) / (newmemories.length)) * 100).toFixed(1)}% (Memory ${i + 1}/${(newmemories.length)})` - - - // Date strings for folder/file names - let memoryDate = new Date(memory.date); - memoryDate.setDate(memoryDate.getDate() + 1); // Memory date is one day off for some reason? - - // Month string for folder in the form: "yyyy-mm, Month Year" - let monthString = `${memoryDate.getFullYear()}-${memoryDate.toLocaleDateString("en-GB", { month: "2-digit" })}, ${memoryDate.toLocaleString('en-us', { month: 'long', year: 'numeric' })}` - monthString = monthString.replaceAll("/", "-"); // Slashes aren't allowed for filenames - - // Date string for files in the form: "Month Day, Year" - let dateString = memoryDate.toLocaleString('en-us', { dateStyle: 'long' }) - - - // An error can happen here, InvalidStateException - // Caused by the primary/secondary image fetch being corrupt, - // but only happens rarely on specific memories - try { - - // REPLACE WITH PROPER PROXY SETUP! - // Fetch image data - let primary = await fetch("https://toofake-cors-proxy-4fefd1186131.herokuapp.com/" + memory.primary) - .then((result) => result.blob()) - - let secondary = await fetch("https://toofake-cors-proxy-4fefd1186131.herokuapp.com/" + memory.secondary) - .then((result) => result.blob()) - - - // Create zip w/ image, adapted from https://stackoverflow.com/a/49836948/21809626 - // Zip (primary + secondary separate) - if (separateImages) { - zip.file(`${monthString}/${dateString} - primary.png`, primary) - zip.file(`${monthString}/${dateString} - secondary.png`, secondary) - } - - - - // Merging images for combined view - // (Must have canvas declaration here to be accessed by toBlob()) - var canvas = document.getElementById("myCanvas") as HTMLCanvasElement; - - if (mergedImage) { - - let primaryImage = await createImageBitmap(await primary); - let secondaryImage = await createImageBitmap(await secondary); - - canvas.width = primaryImage.width; - canvas.height = primaryImage.height; - - var ctx = canvas.getContext("2d"); - - // Check if ctx is null for dealing with TS error (not necessary) - // Bereal-style combined image - // NOTE: secondary image is bugged for custom-uploaded images through the site, - // that aren't phone-sized - if (ctx) { - ctx.drawImage(primaryImage, 0, 0) - - // Rounded secondary image, adapted from https://stackoverflow.com/a/19593950/21809626 - - // Values relative to image size - let width = secondaryImage.width * 0.3; - let height = secondaryImage.height * 0.3; - let x = primaryImage.width * 0.03; - let y = primaryImage.height * 0.03; - let radius = 70; - - - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - - ctx.closePath(); - - ctx.lineWidth = 20; - ctx.stroke(); - ctx.clip() +const fetchImage = async (url: string): Promise => { + const proxyUrl = "https://toofake-cors-proxy-4fefd1186131.herokuapp.com/" + url; + const response = await fetch(proxyUrl); + return await response.blob(); +} - ctx.drawImage(secondaryImage, x, y, width, height) - } +const mergeImages = async (primary: Blob, secondary: Blob, monthString: string, dateString: string, zip: JSZip): Promise => { + const canvas = document.getElementById("myCanvas") as HTMLCanvasElement; + const primaryImage = await createImageBitmap(primary); + const secondaryImage = await createImageBitmap(secondary); + + canvas.width = primaryImage.width; + canvas.height = primaryImage.height; + + const ctx = canvas.getContext("2d"); + if (ctx) { + ctx.drawImage(primaryImage, 0, 0); + let width = secondaryImage.width * 0.3; + let height = secondaryImage.height * 0.3; + let x = primaryImage.width * 0.03; + let y = primaryImage.height * 0.03; + let radius = 70; + + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + ctx.lineWidth = 20; + ctx.stroke(); + ctx.clip(); + ctx.drawImage(secondaryImage, x, y, width, height); + + canvas.toBlob((blob) => { + if (blob) { + zip.file(`${monthString}/${dateString}.png`, blob); } + }); + } +} +const processMemory = async (memory: Memory, zip: JSZip, separateImages: boolean, mergedImage: boolean): Promise => { + let memoryDate = new Date(memory.date); + memoryDate.setDate(memoryDate.getDate() + 1); + let monthString = `${memoryDate.getFullYear()}-${memoryDate.toLocaleDateString("en-GB", { month: "2-digit" })}, ${memoryDate.toLocaleString('en-us', { + month: 'long', + year: 'numeric' + })}`.replaceAll("/", "-"); + let dateString = memoryDate.toLocaleString('en-us', { dateStyle: 'long' }); - // Save as zip - // Async stuff: Must have generateAsync in toBlob function to run in proper order - - canvas.toBlob(async (blob) => { - - if (blob && mergedImage) { - zip.file(`${monthString}/${dateString}.png`, blob) - } - - // Only save if on last memory - if (i == newmemories.length-1) { - - // @ts-ignore: Object is possibly 'null'. - status.textContent += `, exporting .zip...` - - - // NOTE: Some toBlob() calls aren't done by the time we generate the zip, - // so instead it just waits for a second (probably change this) - setTimeout(() => { - - // Save w/ zip name of current date - zip.generateAsync({ type: 'blob' }).then(function (content: any) { - FileSaver.saveAs(content, `bereal-export-${new Date().toLocaleString("en-us", { year: "2-digit", month: "2-digit", day: "2-digit" }).replace(/\//g, '-')}.zip`); - }); - - // Reset status - // @ts-ignore: Object is possibly 'null'. - status.textContent = "Zip will download shortly..."; + try { + let primary = await fetchImage(memory.primary); + let secondary = await fetchImage(memory.secondary); - // Enable download button - // @ts-ignore: Object is possibly 'null'. - downloadButton.disabled = false; + if (separateImages) { + zip.file(`${monthString}/${dateString} - primary.png`, primary); + zip.file(`${monthString}/${dateString} - secondary.png`, secondary); + } - }, 1000) + if (mergedImage) { + await mergeImages(primary, secondary, monthString, dateString, zip); + } + } catch (e) { + console.log(`ERROR: Memory on ${memoryDate} could not be processed:\n${e}`); + throw e; + } +} +const generateAndSaveZip = async (zip: JSZip, downloadButton: HTMLButtonElement | null, status: HTMLElement | null): Promise => { + setTimeout(() => { + zip.generateAsync({ type: 'blob' }).then((content) => { + FileSaver.saveAs(content, `bereal-export-${new Date().toLocaleString("en-us", { + year: "2-digit", + month: "2-digit", + day: "2-digit" + }).replace(/\//g, '-')}.zip`); + }); + + if (status) status.textContent = "Zip will download shortly..."; + if (downloadButton) downloadButton.disabled = false; + }, 1000); +} - } - }); +const downloadMemories = async (memories: Memory[]): Promise => { + const separateImages = (document.getElementById("separate") as HTMLInputElement)?.checked; + const mergedImage = (document.getElementById("merged") as HTMLInputElement)?.checked; + const status = document.getElementById("downloadStatus"); + const error = document.getElementById("error"); + const downloadButton = document.getElementById("download") as HTMLButtonElement; - } catch (e) { + if (status) status.textContent = ""; + if (error) error.textContent = ""; - // @ts-ignore: Object is possibly 'null'. - error.textContent = "Errors found, check console." - console.log(`ERROR: Memory #${i} on ${memoryDate} could not be zipped:\n${e}`); + if (!(separateImages || mergedImage)) { + if (status) status.textContent = "No export option selected."; + return; + } - // Save zip if error was found on the last memory - if (i == newmemories.length-1) { + if (downloadButton) downloadButton.disabled = true; - setTimeout(() => { - zip.generateAsync({ type: 'blob' }).then(function (content: any) { - FileSaver.saveAs(content, `bereal-export-${new Date().toLocaleString("en-us", { year: "2-digit", month: "2-digit", day: "2-digit" }).replace(/\//g, '-')}.zip`); - }); + const zip = new JSZip(); - // @ts-ignore: Object is possibly 'null'. - status.textContent = "Zip will download shortly..."; + try { + for (let i = 0; i < memories.length; i++) { + let memory = memories[i]; + if (status) status.textContent = `Zipping, ${(((i + 1) / memories.length) * 100).toFixed(1)}% (Memory ${i + 1}/${memories.length})`; - // @ts-ignore: Object is possibly 'null'. - downloadButton.disabled = false; + await processMemory(memory, zip, separateImages, mergedImage); - }, 1000) - - } else { - continue; + if (i === memories.length - 1) { + if (status) status.textContent += `, exporting .zip...`; + await generateAndSaveZip(zip, downloadButton, status); } } - } + } catch (e) { + if (error) error.textContent = "Errors found, check console."; + console.log(e); + if (status) status.textContent += `, exporting .zip...`; + await generateAndSaveZip(zip, downloadButton, status); + } } - - - diff --git a/client/pages/post/index.tsx b/client/pages/post/index.tsx index 056d7ad..0a00c8b 100644 --- a/client/pages/post/index.tsx +++ b/client/pages/post/index.tsx @@ -1,6 +1,5 @@ -import { useState } from "react"; +import React, { useState } from "react"; import s from './post.module.scss' -import axios from "axios"; import useCheck from "@/utils/check"; import { useRouter } from "next/router"; @@ -15,175 +14,141 @@ export default function Post() { let [success, setSuccess] = useState(""); const [caption, setCaption] = useState(''); - const [selectedFileOne, setSelectedFileOne]: any = useState(); - const [selectedFileTwo, setSelectedFileTwo]: any = useState(); - const [isFirstFilePicked, setIsFirstFilePicked] = useState(false); - const [isSecondFilePicked, setIsSecondFilePicked] = useState(false); + const [selectedFileOne, setSelectedFileOne]: any = useState(null); + const [selectedFileTwo, setSelectedFileTwo]: any = useState(null); - const [primarybase64, setPrimaryBase64] = useState(''); - const [secondarybase64, setSecondaryBase64] = useState(''); + const [primaryBase64, setPrimaryBase64] = useState(''); + const [secondaryBase64, setSecondaryBase64] = useState(''); - function getBase64(file: any) { - return new Promise(resolve => { - let fileInfo; - let baseURL: any = ""; + const getBase64 = (file: File): Promise => { + return new Promise((resolve, reject) => { let reader = new FileReader(); reader.readAsDataURL(file); - reader.onload = () => { - baseURL = reader.result; - resolve(baseURL); - }; + reader.onload = () => resolve(reader.result as string); + reader.onerror = error => reject(error); }); - }; - - function fileOneHandler(event: any) { - setIsFirstFilePicked(true); - setSelectedFileOne(event.target.files[0]); - - getBase64(event.target.files[0]).then(result => { - setPrimaryBase64(result!.toString()); - }).catch(err => { - console.log(err); - }); - }; - - function fileTwoHandler(event: any) { - setIsSecondFilePicked(true); - setSelectedFileTwo(event.target.files[0]); - - getBase64(event.target.files[0]).then(result => { - setSecondaryBase64(result!.toString()); - }).catch(err => { - console.log(err); - }); - }; - - function handleSubmission() { - setLoading(true); - - let authorization_token = localStorage.getItem("token"); - - fetch("/api/add/post", { - method: "POST", - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - primaryb64: primarybase64, - secondaryb64: secondarybase64, - caption: caption, - token: authorization_token - }) - }).then( - (response) => { - console.log(response); - if (response.ok) { - setLoading(false); - setSuccess("Successfully posted!"); - setTimeout(() => { setSuccess(""); router.push("/feed")}, 3000); - } else { throw new Error("Error: " + response.statusText); } - } - ).catch((error) => { - console.log(error); - setLoading(false); - setFailure(error.message) - setTimeout(() => { setFailure("") }, 5000); - } - ) - - /* - const formData = new FormData(); - formData.append('primaryb64', primarybase64); - formData.append('secondaryb64', secondarybase64); - formData.append('caption', caption ? caption : ""); - formData.append('token', authorization_token!); - console.log(formData); + } - let options = { - url: "/api/add/post", - method: "POST", - headers: { 'Content-Type': "multipart/form-data" }, - data: { - primaryb64: primarybase64, - secondaryb64: secondarybase64, - caption: caption, - token: authorization_token + const fileHandler = async ( + event: React.ChangeEvent, + setFile: React.Dispatch>, + setBase64: React.Dispatch> + ) => { + const file = event.target.files?.[0]; + if (file) { + setFile(file); + try { + const base64 = await getBase64(file); + setBase64(base64); + } catch (error) { + console.error("Error converting file to base64:", error); } } + } - axios.request(options).then( - (response) => { - console.log(response.data); - setLoading(false); + const handleSubmission = async () => { + setLoading(true); + setFailure(""); + setSuccess(""); + + const authorizationToken = localStorage.getItem("token"); + + try { + const response = await fetch("/api/add/post", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + primaryb64: primaryBase64, + secondaryb64: secondaryBase64, + caption: caption, + token: authorizationToken + }) + }); + + if (response.ok) { setSuccess("Successfully posted!"); - setTimeout(() => { setSuccess(""); router.push("/feed")}, 3000); + setTimeout(() => { + setSuccess(""); + router.push("/feed"); + }, 3000); + } else { + throw new Error("Error: " + response.statusText); } - ).catch( - (error) => { - console.log(error); - setLoading(false); - setFailure(error.response.data.error) - setTimeout(() => { setFailure("") }, 5000); - } - ) */ - } + } catch (error: any) { + console.error("Error posting data:", error); + setFailure(error.message || "Unknown error"); + setTimeout(() => setFailure(""), 5000); + } finally { + setLoading(false); + } + }; return (
- - {isFirstFilePicked ? ( + fileHandler(e, setSelectedFileOne, setPrimaryBase64)} + /> + {selectedFileOne ? (
- + Selected back image
) : (<>)}
- - {isSecondFilePicked ? ( + fileHandler(e, setSelectedFileTwo, setSecondaryBase64)} + /> + {selectedFileTwo ? ( <>
- + Selected front image
) : (<>) }
- setCaption(txt.target.value)} + setCaption(txt.target.value)} disabled - > -
{ handleSubmission() }}> + /> +
submit
*some photos taken on an iphone (.heic) may not work. if there is an error try taking a screenshot of the image and uploading that instead.
*you might get a client-side exception if the image is too large. The maximum limit currently is 12mb for both images combined.
- {/* fix this nesting */} - { - failure != "" ? ( -
- {failure} -
- ) : ( - loading ? ( -
- loading... -
- ) : ( - success != "" ? ( -
- {success} -
- ) : (<>) - ) - ) - } + {failure && ( +
+ {failure} +
+ )} + {loading && ( +
+ Loading... +
+ )} + {success && ( +
+ {success} +
+ )}
) } diff --git a/client/pages/profile/[id].tsx b/client/pages/profile/[id].tsx index f94bb34..de0845c 100644 --- a/client/pages/profile/[id].tsx +++ b/client/pages/profile/[id].tsx @@ -4,74 +4,90 @@ import { useEffect, useState } from 'react' import s from './profile.module.scss' -export default function Profile() { +interface ProfileData { + username: string; + name: string; + bio: string; + pfp: string; + status: string; +} + +export default function Profile(){ const router = useRouter() - let [username, setUsername] = useState(""); - let [name, setName] = useState(""); - let [bio, setBio] = useState(""); - let [pfp, setPfp] = useState(""); - let [status, setStatus] = useState(""); + const [profile, setProfile] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); useEffect(() => { + const fetchProfile = async () => { + if (!router.isReady) return; - if (!router.isReady) return; + try { + const rid = router.query.id; + const token = localStorage.getItem("token"); - console.log("router.query.id") - console.log(router.query.id) + const response = await axios.post("/api/profile", { + token, + profile_id: rid, + }, { + headers: { 'Content-Type': 'application/json' } + }); - let rid = router.query.id; + const data = response.data; - let token = localStorage.getItem("token"); - let body = JSON.stringify({ "token": token, "profile_id": rid }); - let options = { - url: "/api/profile", - method: "POST", - headers: { 'Content-Type': 'application/json' }, - data: body, + setProfile({ + username: data.username, + name: data.fullname, + bio: data.biography || "", + pfp: data.profilePicture?.url || "", + status: data.relationship.status, + }); + } catch (error) { + console.error(error); + setError("An error occurred while fetching the profile."); + } finally { + setLoading(false); + } } - axios.request(options).then( - (response) => { - console.log(response.data); - setUsername(response.data.username); - setName(response.data.fullname); - setBio(response.data.biography != undefined ? response.data.biography : ""); - setPfp(response.data.profilePicture != undefined ? response.data.profilePicture.url : ""); - setStatus(response.data.relationship.status); - } - ).catch( - (error) => { - console.log(error); - } - ) - }, [router.isReady]) + fetchProfile(); + + }, [router.isReady]); + + if (loading) return
Loading...
; + + if (error) return
{error}
; + + if (!profile) return
Profile not found.
; + return (
- {pfp ? :
no profile picture
} + {profile.pfp ? ( + Profile picture + ) : ( +
No profile picture
+ )}
-
-
username
-
{username}
-
-
-
name
-
{name}
-
- { - bio.length > 0 ? -
-
biography
-
{bio}
-
: null - } -
-
relation
-
{status == "accepted" ? "friends" : "stranger"}
-
+ + + {profile.bio && } +
- ) -} \ No newline at end of file + ); +} + +interface ProfileDetailProps { + label: string; + value: string; +} + +const ProfileDetail = ({ label, value }: ProfileDetailProps) => ( +
+
{label}
+
{value}
+
+); diff --git a/client/pages/realmojis/index.tsx b/client/pages/realmojis/index.tsx index 41ddacc..2ea96bf 100644 --- a/client/pages/realmojis/index.tsx +++ b/client/pages/realmojis/index.tsx @@ -1,7 +1,5 @@ - import React, { useState } from 'react' import { useEffect } from 'react' -import axios from 'axios' import useCheck from '@/utils/check'; import myself from '@/utils/myself'; @@ -21,8 +19,8 @@ export default function RealMojis() { let emoji_lookup: {[key: string]: string} = { "😍": "heartEyes", "😂": "laughing", - "😲": "surprised", - "😃": "happy", + "😲": "surprised", + "😃": "happy", "👍": "up" } @@ -35,14 +33,14 @@ export default function RealMojis() { "👍": undefined } ); - + useEffect(() => { myself() if (!localStorage.getItem("myself")) { return; } - + let my_real_mojis = JSON.parse(localStorage.getItem("myself")!).realmojis; console.log("MY MOJIS"); console.log(my_real_mojis); @@ -82,4 +80,4 @@ export default function RealMojis() {
) -} \ No newline at end of file +}