Skip to content

Commit f1a2a8e

Browse files
Merge pull request #20 from Eric-Zhang-Developer/refactor/conditional-render-components
Refactor/conditional render components
2 parents 25102d7 + 7b815fd commit f1a2a8e

File tree

4 files changed

+130
-74
lines changed

4 files changed

+130
-74
lines changed

src/app/page.tsx

Lines changed: 8 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
import { useEffect, useState } from "react";
44
import { FollowerListSchema, FollowingListSchema } from "@/lib/types";
5-
import { Github, RotateCw, Upload } from "lucide-react";
6-
import Link from "next/link";
7-
import Dropzone from "react-dropzone";
5+
import { Github } from "lucide-react";
86
import ExtractNamesFromJson from "@/lib/extractNamesFromJson";
97
import Compare from "@/lib/comparison";
8+
import HeroSection from "@/components/HeroSection";
9+
import ResultsSection from "@/components/ResultsSection";
1010
export default function Home() {
1111
const [followers, setFollowers] = useState<string[]>([]);
1212
const [following, setFollowing] = useState<string[]>([]);
@@ -26,6 +26,8 @@ export default function Home() {
2626
setUserDifference([]);
2727
}
2828

29+
// On Drop reads files then checks them against the schemas and throws an error if broke
30+
// Extremely Robust for wrong json
2931
function onDrop(acceptedFiles: File[]) {
3032
acceptedFiles.forEach((file) => {
3133
const reader = new FileReader();
@@ -72,81 +74,14 @@ export default function Home() {
7274
Curious Who Doesn&apos;t Follow You Back?
7375
</h1>
7476

75-
{/* TODO: Refactor this conditional rendering into separate components to improve readability. For now, leaving it in-line to focus on styling and theming. */}
7677
{!hasProcessedDifference ? (
77-
<section className="flex flex-col container items-center gap-4">
78-
<h2 className="text-lg md:text-xl text-center">
79-
Safely see your non-followers using your official Instagram data. Your files are
80-
processed right here in your browser and are never uploaded anywhere.
81-
</h2>
82-
<Dropzone onDrop={onDrop} accept={{ "application/json": [".json"] }}>
83-
{({ getRootProps, getInputProps }) => (
84-
<div
85-
{...getRootProps()}
86-
className="border-2 px-2 md:px-10 py-10 md:py-30 flex flex-col gap-4 md:gap-8 items-center justify-center w-full md:w-3/5 border-dashed rounded-3xl mt-6 md:mt-12 text-lg hover:cursor-pointer"
87-
>
88-
<input {...getInputProps()} data-testid="file-input"></input>
89-
90-
{/* Conditional rendering to show if a user has uploaded single files*/}
91-
{hasProcessedFollowers && <div className="bg-slate-200 px-5 md:px-20 py-2 border-2 rounded-2xl" aria-label="Followers List Uploaded!">Followers List Uploaded!</div>}
92-
93-
{hasProcessedFollowing && <div className="bg-slate-200 px-5 md:px-20 py-2 border-2 rounded-2xl">Following List Uploaded!</div>}
94-
95-
<Upload size={50}></Upload>
96-
<p className="text-center whitespace-normal break-words">
97-
Drag&nbsp;&amp;&nbsp;drop&nbsp;your<br />
98-
<span className="bg-slate-100 text-sm font-mono mx-1 px-2 py-1 rounded break-all">
99-
followers.json
100-
</span>
101-
&amp;
102-
<span className="bg-slate-100 text-sm font-mono mx-1 px-2 py-1 rounded break-all">
103-
following.json
104-
</span><br />
105-
files&nbsp;here
106-
</p>
107-
<span>or</span>
108-
<button className="border-1 px-4 py-2 text-lg rounded-xl hover:cursor-pointer transition hover:scale-105">
109-
Select Files
110-
</button>
111-
</div>
112-
)}
113-
</Dropzone>
114-
<Link href="/tutorial" className="underline">
115-
{" "}
116-
Don&apos;t have your files? Here&apos;s how to get them.
117-
</Link>
118-
</section>
78+
<HeroSection onDrop={onDrop} hasProcessedFollowers={hasProcessedFollowers} hasProcessedFollowing={hasProcessedFollowing}></HeroSection>
11979
) : (
120-
<section className="flex flex-col items-center">
121-
<button
122-
onClick={handleReset}
123-
aria-label="Reset Button"
124-
className="py-4 transform transition hover:rotate-90 cursor-pointer "
125-
>
126-
<RotateCw size={40}></RotateCw>
127-
</button>
128-
<p className="text-2xl mb-6 text-center">
129-
Accounts that don&apos;t follow you back - {userDifference.length}
130-
</p>
131-
132-
<ol className="flex flex-row flex-wrap container mx-auto gap-1.5 sm:gap-2.5 justify-center">
133-
{userDifference.map((userName) => (
134-
<li key={userName} aria-label={userName}>
135-
<a
136-
className="text-s m-1 inline-block rounded-full border-2 border-l-8 bg-slate-100 px-3 py-1 text-border cursor-pointer transition transform hover:scale-110"
137-
href={`https://www.instagram.com/${userName}/`}
138-
target="_blank"
139-
rel="noopener noreferrer"
140-
>
141-
{userName}
142-
</a>
143-
</li>
144-
))}
145-
</ol>
146-
</section>
80+
<ResultsSection handleReset={handleReset} userDifference={userDifference}></ResultsSection>
14781
)}
14882
</main>
14983

84+
{/* Disclaimer Footer */}
15085
<a
15186
href="https://github.com/Eric-Zhang-Developer/follow-diff"
15287
target="_blank"

src/components/HeroSection.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// components/FileUploadZone.tsx
2+
import React from 'react';
3+
import { Upload } from "lucide-react";
4+
import Link from "next/link";
5+
import Dropzone from "react-dropzone";
6+
import {HeroSectionProps} from "../lib/types"
7+
8+
export default function HeroSection({hasProcessedFollowers, hasProcessedFollowing, onDrop} : HeroSectionProps) {
9+
return (
10+
<section className="flex flex-col container items-center gap-4">
11+
<h2 className="text-lg md:text-xl text-center">
12+
Safely see your non-followers using your official Instagram data. Your files are processed
13+
right here in your browser and are never uploaded anywhere.
14+
</h2>
15+
<Dropzone onDrop={onDrop} accept={{ "application/json": [".json"] }}>
16+
{({ getRootProps, getInputProps }) => (
17+
<div
18+
{...getRootProps()}
19+
className="border-2 px-2 md:px-10 py-10 md:py-30 flex flex-col gap-4 md:gap-8 items-center justify-center w-full md:w-3/5 border-dashed rounded-3xl mt-6 md:mt-12 text-lg hover:cursor-pointer"
20+
>
21+
<input {...getInputProps()} data-testid="file-input"></input>
22+
23+
{/* Conditional rendering to show if a user has uploaded single files*/}
24+
{hasProcessedFollowers && (
25+
<div
26+
className="bg-slate-200 px-5 md:px-20 py-2 border-2 rounded-2xl"
27+
aria-label="Followers List Uploaded!"
28+
>
29+
Followers List Uploaded!
30+
</div>
31+
)}
32+
33+
{hasProcessedFollowing && (
34+
<div className="bg-slate-200 px-5 md:px-20 py-2 border-2 rounded-2xl">
35+
Following List Uploaded!
36+
</div>
37+
)}
38+
39+
<Upload size={50}></Upload>
40+
<p className="text-center whitespace-normal break-words">
41+
Drag&nbsp;&amp;&nbsp;drop&nbsp;your
42+
<br />
43+
<span className="bg-slate-100 text-sm font-mono mx-1 px-2 py-1 rounded break-all">
44+
followers.json
45+
</span>
46+
&amp;
47+
<span className="bg-slate-100 text-sm font-mono mx-1 px-2 py-1 rounded break-all">
48+
following.json
49+
</span>
50+
<br />
51+
files&nbsp;here
52+
</p>
53+
<span>or</span>
54+
<button className="border-1 px-4 py-2 text-lg rounded-xl hover:cursor-pointer transition hover:scale-105">
55+
Select Files
56+
</button>
57+
</div>
58+
)}
59+
</Dropzone>
60+
<Link href="/tutorial" className="underline">
61+
{" "}
62+
Don&apos;t have your files? Here&apos;s how to get them.
63+
</Link>
64+
</section>
65+
);
66+
}

src/components/ResultsSection.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from "react";
2+
import { RotateCw } from "lucide-react";
3+
import { ResultsSectionProps } from "@/lib/types";
4+
5+
export default function ResultsSection({handleReset, userDifference} : ResultsSectionProps) {
6+
return (
7+
<section className="flex flex-col items-center">
8+
<button
9+
onClick={handleReset}
10+
aria-label="Reset Button"
11+
className="py-4 transform transition hover:rotate-90 cursor-pointer "
12+
>
13+
<RotateCw size={40}></RotateCw>
14+
</button>
15+
<p className="text-2xl mb-6 text-center">
16+
Accounts that don&apos;t follow you back - {userDifference.length}
17+
</p>
18+
19+
<ol className="flex flex-row flex-wrap container mx-auto gap-1.5 sm:gap-2.5 justify-center">
20+
{userDifference.map((userName) => (
21+
<li key={userName} aria-label={userName}>
22+
<a
23+
className="text-s m-1 inline-block rounded-full border-2 border-l-8 bg-slate-100 px-3 py-1 text-border cursor-pointer transition transform hover:scale-110"
24+
href={`https://www.instagram.com/${userName}/`}
25+
target="_blank"
26+
rel="noopener noreferrer"
27+
>
28+
{userName}
29+
</a>
30+
</li>
31+
))}
32+
</ol>
33+
</section>
34+
);
35+
}

src/lib/types.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { z } from "zod";
22

3+
// =========== //
4+
// Backend interfaces and schemas
5+
// =========== //
6+
37
// Using Zod
48
// The reason for these interfaces is so that typescript neatly throws an error when a user
59
// adds a json that is not a followers.json or following.json format
@@ -27,4 +31,20 @@ export const FollowerListSchema = z.array(ExpectedJSONSchema);
2731

2832
// Types for extractNamesFromJson
2933
export type FollowingList = z.infer<typeof FollowingListSchema>['relationships_following'];
30-
export type FollowerList = z.infer<typeof FollowerListSchema>;
34+
export type FollowerList = z.infer<typeof FollowerListSchema>;
35+
36+
37+
// =========== //
38+
// Component Interfaces
39+
// =========== //
40+
41+
export interface HeroSectionProps{
42+
hasProcessedFollowers : boolean;
43+
hasProcessedFollowing : boolean;
44+
onDrop: (acceptedFiles: File[]) => void;
45+
}
46+
47+
export interface ResultsSectionProps{
48+
handleReset: () => void;
49+
userDifference: string[];
50+
}

0 commit comments

Comments
 (0)