Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions package-lock.json

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

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
"dom-to-image": "^2.6.0",
"file-saver": "^2.0.5",
"react": "^18.2.0",
"react-curved-text": "^3.0.1",
"react-dom": "^18.2.0",
"react-nice-avatar": "^1.5.0",
"react-router-dom": "^6.23.1",
"react-scripts": "5.0.1",
"sass": "^1.77.1",
"typescript": "^4.9.5",
Expand Down Expand Up @@ -53,6 +55,7 @@
"@types/chroma-js": "^2.4.4",
"@types/dom-to-image": "^2.6.7",
"@types/file-saver": "^2.0.7",
"@types/react-curved-text": "^2.0.3",
"tailwindcss": "^3.4.3"
}
}
132 changes: 100 additions & 32 deletions src/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { Component } from "react";
import { Routes, Route } from "react-router-dom";
import AvatarList from "./components/AvatarList/index";
import AvatarEditor from "./components/AvatarEditor/index";
import Footer from "./components/Footer";
Expand All @@ -7,11 +8,15 @@ import { saveAs } from "file-saver";
import ReactNiceAvatar, { genConfig } from "./config/index";
import Header from "./components/Header";
import "./index.scss";
import Form from "./components/Form";
import Arrow from "./components/Arrow";
import AboutUs from "./components/AboutUs";

interface AppState {
config: { [key: string]: any };
shape: AvatarShape;
avatarId: string;
avatarImageDataUrl: string | null;
}

type AvatarShape = "circle" | "rounded" | "square";
Expand All @@ -24,30 +29,41 @@ class App extends Component<{}, AppState> {
isGradient: Boolean(Math.round(Math.random())),
}),
shape: "circle",
avatarId: "myAvatar", // Declare avatarId here
avatarId: "myAvatar",
avatarImageDataUrl: null,
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.captureAvatarImage = this.captureAvatarImage.bind(this);
}

selectConfig(config: { [key: string]: any }) {
this.setState({ config });
}

updateConfig(key: string, value: any) {
// Specify type for value
const { config } = this.state;
config[key] = value;
this.setState({ config });
}

updateShape(shape: AvatarShape) {
// Specify type for shape
this.setState({ shape });
}

async download() {
const scale = 2;
const node = document.getElementById(this.state.avatarId);
if (node) {

if (!node) {
console.error(
"Element with ID",
this.state.avatarId,
"not found in the DOM."
);
return;
}

try {
const blob = await domtoimage.toBlob(node, {
height: node.offsetHeight * scale,
style: {
Expand All @@ -59,7 +75,13 @@ class App extends Component<{}, AppState> {
width: node.offsetWidth * scale,
});

saveAs(blob, "avatar.png");
if (blob) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good error handling

saveAs(blob, "avatar.png");
} else {
console.error("Blob is null or undefined.");
}
} catch (error) {
console.error("Error generating image blob:", error);
}
}

Expand All @@ -69,38 +91,84 @@ class App extends Component<{}, AppState> {
});
}

async captureAvatarImage() {
const node = document.getElementById(this.state.avatarId);
if (node) {
const dataUrl = await domtoimage.toPng(node);
console.log(dataUrl);
this.setState({ avatarImageDataUrl: dataUrl }, () => {
console.log("State updated:", this.state.avatarImageDataUrl);
});
} else console.log("node is not exist");
}

handleFormSubmit(
formData: { name: string; region: string; role: string },
imageDataUrl: string
) {
console.log("Form submitted", formData, imageDataUrl);
}

render() {
const { config, shape } = this.state;
const { config, shape, avatarImageDataUrl } = this.state;
return (
<div className="App flex flex-col min-h-screen">
<Header title="Avatar Generator" />
<div className="App flex flex-col min-h-screen overflow-x-hidden">
<Header title="AVANART" />
<main className="flex-1 flex flex-col items-center justify-center">
<div id={this.state.avatarId} className="mb-10">
<ReactNiceAvatar
className="w-64 h-64 highres:w-80 highres:h-80"
hairColorRandom
shape={this.state.shape}
{...config}
<Routes>
<Route path="/about-us" element={<AboutUs />} />
<Route
path="/"
element={
<>
<div id={this.state.avatarId} className="mb-10">
<ReactNiceAvatar
className="w-64 h-64 highres:w-80 highres:h-80"
hairColorRandom
shape={this.state.shape}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On line 113 you did destructuring, so you could directly access all of it without "this.state"

Copy link
Collaborator Author

@Shadi38 Shadi38 Jun 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are talking about app component, this structure comes from react nice avatar and we didn't change it, but I worked on form page and tried to stick role and region on top of image after clicking the submit btn.
#33

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Form looks all right to me, thank you Shadi

{...config}
/>
</div>
<AvatarEditor
config={config}
shape={shape}
updateConfig={this.updateConfig.bind(this)}
updateShape={this.updateShape.bind(this)}
download={this.download.bind(this)}
/>
<input
className="inputField w-64 h-10 p-2 rounded-full mt-10 text-center outline-none"
placeholder="input name or email ..."
onKeyUp={(e: React.KeyboardEvent<HTMLInputElement>) =>
this.onInputKeyUp(e)
}
/>
<AvatarList selectConfig={this.selectConfig.bind(this)} />
<button onClick={this.captureAvatarImage} className="mt-4">
Capture Avatar Image
</button>
<div className="absolute top-2/3 right-0">
<Arrow
fillColor="red"
onCaptureAvatar={this.captureAvatarImage}
/>
</div>
</>
}
/>
</div>
<AvatarEditor
config={config}
shape={shape}
updateConfig={this.updateConfig.bind(this)}
updateShape={this.updateShape.bind(this)}
download={this.download.bind(this)}
/>
<input
className="inputField w-64 h-10 p-2 rounded-full mt-10 text-center outline-none"
placeholder="input name or email ..."
onKeyUp={(e: React.KeyboardEvent<HTMLInputElement>) =>
this.onInputKeyUp(e)
}
/>
<Route
path="/form"
element={
<Form
onSubmit={this.handleFormSubmit}
setSelectedRegion={() => {}}
setSelectedRole={() => {}}
avatarImageDataUrl={avatarImageDataUrl}
/>
}
/>
</Routes>
</main>

<AvatarList selectConfig={this.selectConfig.bind(this)} />

<Footer />
</div>
);
Expand Down
35 changes: 35 additions & 0 deletions src/App/components/AboutUs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";

const AboutUs: React.FC = () => {
return (
<div className="container mx-auto px-4 py-8 relative">
<div className="absolute inset-0 flex justify-center items-center"></div>
<h1 className="text-3xl text-pink-500 font-bold mb-4">About Us</h1>
<p className="text-lg mb-4">
Welcome to our avatar generation website! This project was initiated as
part of our learning journey in software development at CodeYourFuture.
</p>
<p className="text-md mb-2">
The primary objective of this website is to provide a platform for
individuals who cannot use their original pictures to generate avatars.
Throughout this project, We have been utilizing TypeScript to ensure
cleaner and more understandable code, facilitating easier maintenance
and scalability.
</p>
<p className="text-md mb-2 ">
Initially, we worked on refactoring the existing codebase, which was
written in legacy code. This process allowed us to gain in-depth
understanding of the project, fix bugs, and apply necessary updates.
Now, we are actively working on building new features to enhance the
website and achieve its objectives.
</p>
<p className="text-md mb-2">
This experience has been invaluable in teaching us how to approach new
projects written by others, identify and resolve issues effectively, and
seamlessly integrate new features into existing codebases.
</p>
</div>
);
};

export default AboutUs;
Loading