[nextjs]SNS Server-16(myapp26)

πŸ‘‰πŸ» myapp26μ—λŠ” μŠ€ν‚¨κΈ°λŠ₯을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.
Skin functionality is implemented in myapp26.

πŸ‘‰πŸ» μ½”λ“œ 양이 λ§Žμ•„μ„œ μ£Όμš” λΆ€λΆ„λ§Œ μ„€λͺ…ν•©λ‹ˆλ‹€ .
Due to the large volume of code, I will explain only the key parts.

πŸ‘‰πŸ» μ „μ²΄μ½”λ“œλŠ” κΉƒν—ˆλΈŒ μ½”λ“œλ₯Ό μ°Έμ‘°ν•˜μ„Έμš”
Please refer to the GitHub repository for the full code.

https://github.com/gideonslife01/flm-nextjs

πŸ“ 전체 ν”„λ‘œμ νŠΈ ꡬ쑰 / Overall Project Structure

myapp26/  
β”œβ”€β”€ app/  (Next.js App Router)
β”‚   β”œβ”€β”€ .well-known/webfinger/route.ts  -> webfinger
β”‚   β”œβ”€β”€ api/follow/route.ts    -> Follow API(temporary)
β”‚   β”œβ”€β”€ api/announce/route.ts  -> Boost(Announcement)
β”‚   β”œβ”€β”€ api/like/route.ts.     -> Like API
β”‚   β”œβ”€β”€ api/posts/route.ts     -> Writing API
β”‚   β”œβ”€β”€ api/timeline/route.ts  -> Timeline API
β”‚   β”œβ”€β”€ users/[username]/
β”‚   β”‚   β”œβ”€β”€ statuses/[id]/route.ts -> Indivisual Post
β”‚   β”‚   β”œβ”€β”€ route.ts               -> Acotr Information
β”‚   β”‚   β”œβ”€β”€ followers/route.ts     -> Followers List
β”‚   β”‚   β”œβ”€β”€ following/route.ts     -> Following List
β”‚   β”‚   β”œβ”€β”€ inbox/route.ts         -> Inbox
β”‚   β”‚   └── outbox/route.ts        -> outbox
β”‚   β”œβ”€β”€ usersui/[username]/
β”‚   β”‚   β”œβ”€β”€ page.tsx               -> Timeline UI
β”‚   β”‚   └── _components/themes/
β”‚   β”‚       β”œβ”€β”€ pinafore/Pinaforetheme.tsx  -> Theme 1
β”‚   β”‚       β”œβ”€β”€ mastodon/Mastodontheme.tsx  -> Theme 2
β”‚   β”‚       └── minimal/Minimaltheme.tsx    -> Theme 3
β”‚   β”œβ”€β”€ layout.tsx, page.tsx, globals.css
β”‚   └── favicon.ico
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ theme.tsx              -> Theme Provider
β”‚   β”œβ”€β”€ ap.ts                  -> Follow Accept
β”‚   └── db.ts                  -> DB connection
β”œβ”€β”€ data/
β”‚   └── keys/                  -> private.pem, public.pem
β”œβ”€β”€ data.sqlite                -> Database
β”œβ”€β”€ Caddyfile                  -> https 
└── package.json

πŸ“ ν”„λ‘œμ νŠΈ μ‹œμž‘(myapp26)
Project Start (myapp26)

npx create-next-app@latest

πŸ“ SQLiteμ„€μΉ˜ / Installing SQLite

cd ~/myapp26
npm install better-sqlite3
npm install -D @types/better-sqlite3

πŸ“ DDNS,httpsμ„€μ • / DDNS,https settings

πŸ“ λ„λ©”μΈν—ˆμš©, @userid μ‚¬μš©ν•˜κΈ°
Allowing domains, using @userid

— next.config.ts

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* allow domain*/
  allowedDevOrigins: ['aloy-horizon.duckdns.org', '*.duckdns.org'],

  /* host.domain.org/@userid */
  async rewrites() {
    return [
      {
        source: '/@:username',
        destination: '/usersui/:username',
      },
    ]
  }
};


export default nextConfig;

πŸ“ μ½”λ“œμˆ˜μ • / Code Modification

βœ”οΈ myapp26/lib/theme.tsxνŒŒμΌμ„ λΆˆλŸ¬μ™€ μ»¨ν…μŠ€νŠΈ ν”„λ‘œλ°”μ΄λ”λ₯Ό μ μš©ν•©λ‹ˆλ‹€.
Import the myapp26/lib/theme.tsx file and apply the context provider.

πŸ’‘ μ»¨ν…μŠ€νŠΈ ν”„λ‘œλ°”μ΄λ”λŠ” ν•˜λ‚˜μ˜ 이벀트둜 전체 μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” κΈ°λŠ₯으둜 ν…Œλ§ˆμ μš©μ΄λ‚˜ μ–Έμ–΄ 적용 섀정에 많이 μ‚¬μš©λ©λ‹ˆλ‹€.
A Context Provider is a feature that allows the entire state to be updated via a single event; it is commonly used for settings such as applying themes or languages.

— myapp26/app/usersui/[username]/Layout.tsx

import { ThemeProvider } from "@/lib/theme"; // βœ… myapp26

... ...

export default function RootLayout({ children }: LayoutProps<"/">) {
  return (
    <html
      lang="en"
      className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
    >
      <body className="min-h-full flex flex-col">
        {/* βœ… myapp26 - Add ThemeProvide */}
        <ThemeProvider>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

βœ”οΈ μ—¬κΈ°μ„œ ν…Œλ§ˆνŒŒμΌμ„ 뢈러였고 ν…Œλ§ˆ(μŠ€ν‚¨)μ„μ μš©ν•©λ‹ˆλ‹€.
Here, you load the theme file and apply the theme (skin).

— myapp26/app/paget.tsx

... ...
import { PinaforeTheme } from './_components/themes/pinafore/Pinaforetheme';
import { MastodonTheme } from './_components/themes/mastodon/Mastodontheme';
import { MinimalTheme } from './_components/themes/minimal/Minimaltheme';
... ...

  // βœ… timeline fetch
  useEffect(() => {
    params.then(p => {
      setUsername(p.username);
      fetch(`/api/timeline?username=${p.username}`)
        .then(r => r.json())
        .then(data => {
          setTimeline(data);
          setLoading(false);
        });
    });
  }, [params]);
... ...
  if (loading) return <div style={{ padding: 20 }}>Loading...</div>;

  // βœ… theme λΆ„κΈ° + props 전달 / Theme branching + props passing
  const props = { timeline, username, onBoost: handleBoost, onLike: handleLike };

  // apply theme
  if (theme === 'pinafore') return <PinaforeTheme {...props} />
  if (theme === 'mastodon') return <MastodonTheme {...props} />
  if (theme === 'minimal') return <MinimalTheme {...props} />
  
  return <PinaforeTheme {...props} />;

πŸ“ 디렉토리 및 파일 μΆ”κ°€ / Add directories and files

βœ”οΈ νƒ€μž„λΌμΈ μŠ€ν‚¨(ν…Œλ§ˆ) UI / Timeline Skin (Theme) UI

— μ•„λž˜μ˜ νŒŒμΌμ€ λͺ¨λ‘ ν…Œλ§ˆ λ””μžμΈμœΌλ‘œ μ’‹μ•„μš”,λΆ€μŠ€νŠΈκΈ°λŠ₯은 λΉ μ ΈμžˆμŠ΅λ‹ˆλ‹€.
The files below are all theme designs; the boost function is not included.

myapp26/app/usersui/[username]/_components/themes/pinafore/Pinaforetheme.tsx

myapp26/app/usersui/[username]/_components/themes/mastodon/Mastodontheme.tsx

myapp26/app/usersui/[username]/_components/themes/minimal/Minimaltheme.tsx

— μ„Έ 파일 λͺ¨λ‘ λ™μΌν•˜λ©° ν…Œλ§ˆμ˜ ꡬ뢄이 κ°€λŠ₯ν•˜κ²Œ ν…Œλ§ˆ μ΄λ¦„λ§Œ ν‘œμ‹œμ€¬μŠ΅λ‹ˆλ‹€.
All three files are identical; only the theme names are displayed to distinguish between them.

<h2>🐘 {username}/ Pinafore</h2> {/* ν…Œλ§ˆμ΄λ¦„ / Theme Name */}
Pinafore theme
Mastodon theme

— ν…Œλ§ˆ λ³€μˆ˜μ˜ 값이 λ³€ν•˜λ©΄ μ»¨ν…μŠ€νŠΈ ν”„λ‘œλ°”μ΄λ”μ—μ„œ 둜컬 μŠ€ν† λ¦¬μ§€μ˜ 값을 λ³€κ²½ν•©λ‹ˆλ‹€.
When the value of the theme variable changes, the context provider updates the value in local storage.

... ...
const [theme, setTheme] = useState<ThemeName>('pinafore');
... ...         
<select 
            value={theme} //  ν˜„μž¬ λ‘œμ»¬μŠ€ν† λ¦¬μ§€ κ°’ ν‘œμ‹œ / Display current local storage value
            onChange={(e) => {
            setTheme(e.target.value as any); 
         }}>
            <option value="pinafore">Pinafore</option>
            <option value="mastodon">Mastodon</option>
            <option value="minimal">Minimal</option>
          </select>
Minimal
Browser Local storage

βœ”οΈ μ»¨ν…μŠ€νŠΈ ν”„λ‘œλ°”μ΄λ” / Context Provider

— myapp26/lib/theme.tsx

... ...
  useEffect(() => {
    const saved = localStorage.getItem('theme') as ThemeName;
    if (saved) setTheme(saved);
  }, []);
... ...  

return (
    <ThemeContext.Provider value={{ theme, setTheme: updateTheme }}>
      <div data-theme={theme} style={{ minHeight: '100%', display: 'flex', flexDirection: 'column', flex: 1 }}>
        {children}
      </div>
    </ThemeContext.Provider>
  );

1)μ»¨ν…μŠ€νŠΈ ν”„λ‘œλ°”μ΄λ”μ— themeλ³€μˆ˜λ₯Ό νŒŒλΌλ©”ν„°λ‘œ μ μš©ν•©λ‹ˆλ‹€.
Apply the theme variable as a parameter to the context provider.

2)theme 값이 λ³€κ²½λ˜λ©΄ λ‹€λ₯Έ ν…Œλ§ˆνŒŒμΌμ„ 뢈러였기 μœ„ν•΄μ„œ μ‚¬μš©λ©λ‹ˆλ‹€.
It is used to load a different theme file when the theme value changes.

3)ν…Œλ§ˆμ΄λ¦„μ€ λΈŒλΌμš°μ €μ˜ 둜컬 μŠ€ν† λ¦¬μ§€μ— μ €μž₯λ©λ‹ˆλ‹€.
The theme name is stored in the browser’s local storage.

μš”ν•œλ³΅μŒ 8μž₯ 32절 / John 8:32

“그리고 λ„ˆν¬λŠ” 진리λ₯Ό μ•Œκ²Œ 될 것이며, 진리가 λ„ˆν¬λ₯Ό 자유둭게 ν•  것이닀.”

“Then you will know the truth ,and the truth will set you free”

Leave a Reply