Skip to content
Merged
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
Binary file added src/assets/google-play-badge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/instagram-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/pin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 78 additions & 0 deletions src/components/auth/CitySelectView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import instagramLogo from '../../assets/instagram-logo.svg'
import pinIcon from '../../assets/pin.png'
import { CITIES } from '../../constants/cities'

interface CitySelectViewProps {
country: string
onLoginClick: () => void
onSignupClick: () => void
}

const CitySelectView = ({
country,
onLoginClick,
onSignupClick,
}: CitySelectViewProps) => {
return (
<div className="flex w-full flex-col items-center">
<div className="fixed top-0 left-0 z-50 flex h-[60px] w-full justify-center border-b border-gray-300 bg-white px-4">
<div className="flex w-full max-w-[935px] items-center justify-between">
<img
src={instagramLogo}
alt="Instagram"
className="w-[103px] cursor-pointer"
onClick={onLoginClick}
/>
<div className="flex items-center gap-4">
<button
onClick={onLoginClick}
className="rounded-[8px] bg-[#4a5df9] px-4 py-1.5 text-sm font-semibold text-white"
>
로그인
</button>
<button
onClick={onSignupClick}
className="text-sm font-semibold text-[#4a5df9]"
>
가입하기
</button>
</div>
</div>
</div>

<div className="mt-32 mb-12 flex w-11/24 flex-col">
<div className="mb-10 flex items-center gap-2">
<img
src={pinIcon}
alt="Location"
className="h-4 w-4 object-contain"
/>
<span className="text-[16px] leading-none font-bold text-black">
{country}
</span>
</div>
<div className="mb-5 text-left">
<span className="text-[12px] font-bold text-[#737373] uppercase">
{country}의 도시
</span>
</div>
<div className="flex flex-col rounded-[3px] border border-gray-300 bg-white shadow-sm">
<div className="p-8">
<div className="grid grid-cols-4 gap-x-8 gap-y-5">
{CITIES.map((city, index) => (
<div
key={`${city}-${index}`}
className="cursor-pointer truncate text-[14px] text-black active:text-[#4a5df9]"
>
{city}
</div>
))}
</div>
</div>
</div>
</div>
</div>
)
}

export default CitySelectView
57 changes: 57 additions & 0 deletions src/components/auth/LiteDownloadView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import instagramIcon from '../../assets/instagram-icon.png'
import googlePlayBadge from '../../assets/google-play-badge.png'

interface LiteDownloadViewProps {
onBack: () => void
}

const LiteDownloadView = ({ onBack }: LiteDownloadViewProps) => {
return (
<div className="flex w-full flex-col items-center justify-center px-4 pt-40 text-center tracking-tighter">
<img
src={instagramIcon}
alt="Instagram"
className="mb-4 h-16 w-16 cursor-pointer object-contain"
onClick={onBack}
/>

<h1 className="mb-[6px] text-[22px] font-bold text-black">
Instagram Lite 다운로드
</h1>

<p className="mb-[20px] text-[16px] text-black">
더 가볍고 빠르며 데이터도 절약할 수 있습니다
</p>

<div className="flex w-full flex-col items-center">
<button className="mb-[7px] flex w-full max-w-[130px] items-center justify-center gap-2 rounded-[8px] bg-[#4a5df9] py-3 text-[13px] font-semibold tracking-tighter text-white transition-opacity hover:opacity-90">
<svg
width="15"
height="15"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="7 10 12 15 17 10" />
<line x1="12" y1="15" x2="12" y2="3" />
</svg>
APK 다운로드
</button>
</div>

<div className="cursor-pointer">
<img
src={googlePlayBadge}
alt="Google Play"
className="h-12 object-contain"
/>
</div>
</div>
)
}

export default LiteDownloadView
66 changes: 66 additions & 0 deletions src/components/auth/LocationSelectView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import instagramLogo from '../../assets/instagram-logo.svg'
import { COUNTRIES } from '../../constants/countries'

interface LocationSelectViewProps {
onLoginClick: () => void
onSignupClick: () => void
onSelect: (country: string) => void
}

const LocationSelectView = ({
onLoginClick,
onSignupClick,
onSelect,
}: LocationSelectViewProps) => {
return (
<div className="flex w-full flex-col items-center">
<div className="fixed top-0 left-0 z-50 flex h-[60px] w-full justify-center border-b border-gray-300 bg-white px-4">
<div className="flex w-full max-w-[935px] items-center justify-between">
<img
src={instagramLogo}
alt="Instagram"
className="w-[103px] cursor-pointer"
onClick={onLoginClick}
/>
<div className="flex items-center gap-4">
<button
onClick={onLoginClick}
className="rounded-[8px] bg-[#4a5df9] px-4 py-1.5 text-sm font-semibold text-white"
>
로그인
</button>
<button
onClick={onSignupClick}
className="text-sm font-semibold text-[#4a5df9]"
>
가입하기
</button>
</div>
</div>
</div>

<div className="mt-32 mb-12 flex w-11/24 flex-col">
<div className="mb-2 ml-1 text-left">
<span className="text-[14px] font-bold text-[#737373]">국가</span>
</div>
<div className="flex flex-col rounded-[3px] border border-gray-300 bg-white shadow-sm">
<div className="p-8">
<div className="grid grid-cols-4 gap-x-8 gap-y-5">
{COUNTRIES.map((country) => (
<div
key={country}
onClick={() => onSelect(country)}
className="cursor-pointer truncate text-[14px] text-black active:text-[#4a5df9]"
>
{country}
</div>
))}
</div>
</div>
</div>
</div>
</div>
)
}

export default LocationSelectView
120 changes: 104 additions & 16 deletions src/components/auth/LoginFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
const LoginFooter = () => {
import { useState, useEffect, useRef } from 'react'

interface LoginFooterProps {
onLocationClick?: () => void
onLiteClick?: () => void
}

const LoginFooter = ({ onLocationClick, onLiteClick }: LoginFooterProps) => {
const [isLangOpen, setIsLangOpen] = useState(false)
const [currentLang, setCurrentLang] = useState('한국어')
const dropdownRef = useRef<HTMLDivElement>(null)

const footerLinks = [
{ name: 'Meta', url: 'https://about.meta.com/' },
{ name: '소개', url: 'https://about.instagram.com/' },
Expand All @@ -8,12 +19,9 @@ const LoginFooter = () => {
{ name: 'API', url: 'https://developers.facebook.com/docs/instagram' },
{ name: '개인정보처리방침', url: 'https://privacycenter.instagram.com/' },
{ name: '약관', url: 'https://help.instagram.com/581066165581870' },
{ name: '위치', url: 'https://www.instagram.com/explore/locations/' },
{
name: 'Instagram Lite',
url: 'https://help.instagram.com/1559405347493117',
},
{ name: 'Meta AI', url: 'https://www.meta.ai/?utm_source=foa_web_footer' },
{ name: '위치', url: '#' },
{ name: 'Instagram Lite', url: '#' },
{ name: 'Meta AI', url: 'https://www.meta.ai/' },
{ name: 'Threads', url: 'https://www.threads.net/' },
{
name: '연락처 업로드 & 비사용자',
Expand All @@ -25,26 +33,106 @@ const LoginFooter = () => {
},
]

const languages = ['한국어']

useEffect(() => {
const handleClose = (e: MouseEvent | WheelEvent | TouchEvent) => {
if (!isLangOpen) return

if (
dropdownRef.current &&
!dropdownRef.current.contains(e.target as Node)
) {
setIsLangOpen(false)
} else if (e.type === 'wheel' || e.type === 'touchmove') {
setIsLangOpen(false)
}
}

if (isLangOpen) {
window.addEventListener('mousedown', handleClose)
window.addEventListener('wheel', handleClose, { passive: true })
window.addEventListener('touchmove', handleClose, { passive: true })
}

return () => {
window.removeEventListener('mousedown', handleClose)
window.removeEventListener('wheel', handleClose)
window.removeEventListener('touchmove', handleClose)
}
}, [isLangOpen])

return (
<footer className="flex flex-col items-center gap-4 px-4 py-10 text-xs text-[#737373]">
<div className="flex max-w-[1000px] flex-wrap justify-center gap-x-4 gap-y-2 text-center font-normal">
<footer className="flex flex-col items-center gap-4 px-4 py-10 text-xs tracking-tighter text-[#737373]">
<div className="flex max-w-[1000px] flex-wrap justify-center gap-x-4 gap-y-2 text-center">
{footerLinks.map((link) => (
<a
key={link.name}
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="transition-opacity hover:underline active:opacity-50"
target={
link.name === '위치' || link.name === 'Instagram Lite'
? undefined
: '_blank'
}
rel={
link.name === '위치' || link.name === 'Instagram Lite'
? undefined
: 'noopener noreferrer'
}
onClick={(e) => {
if (link.name === '위치') {
e.preventDefault()
onLocationClick?.()
} else if (link.name === 'Instagram Lite') {
e.preventDefault()
onLiteClick?.()
}
}}
className="cursor-pointer transition-opacity hover:underline active:opacity-50"
>
{link.name}
</a>
))}
</div>

<div className="mt-2 flex gap-4">
<div className="flex cursor-pointer items-center gap-1 transition-colors hover:text-black">
<span>한국어</span>
<span className="text-[10px] select-none">⌵</span>
<div className="mt-2 flex items-center gap-4">
<div
className="relative flex items-center justify-start"
ref={dropdownRef}
>
<div
onClick={() => setIsLangOpen(!isLangOpen)}
className="flex cursor-pointer items-center gap-1 transition-colors hover:text-black"
>
<span>{currentLang}</span>
<span
className={`text-[10px] transition-transform select-none ${isLangOpen ? 'rotate-180' : ''}`}
>
</span>
</div>

{isLangOpen && (
<div className="absolute bottom-full left-0 z-50 mb-2 w-[132px] overflow-hidden rounded-[3px] border border-gray-300 bg-white shadow-md">
{languages.map((lang) => (
<div
key={lang}
className={`cursor-pointer px-3 py-1.5 text-left text-[12px] transition-colors ${
currentLang === lang
? 'bg-[#737373] text-white'
: 'text-black hover:bg-[#737373] hover:text-white'
}`}
onClick={(e) => {
e.stopPropagation()
setCurrentLang(lang)
setIsLangOpen(false)
}}
>
{lang}
</div>
))}
</div>
)}
</div>
<span className="select-none">© 2026 Instagram from Meta</span>
</div>
Expand Down
Loading