diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index fe8398f..87b7952 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -14,8 +14,6 @@ jobs: uses: actions/checkout@v4 - name: Install pnpm uses: pnpm/action-setup@v4 - with: - version: 10 - name: Setup Node.js uses: actions/setup-node@v4 with: diff --git a/app/page.tsx b/app/page.tsx index 7b489be..aaf775f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -62,9 +62,9 @@ export default function Home() { useEffect(() => { if (!emblaApi) return; - onSelect(); emblaApi.on("select", onSelect); emblaApi.on("reInit", onSelect); + emblaApi.emit("select"); return () => { emblaApi.off("select", onSelect); diff --git a/components/mini-lt/ManageTalks.tsx b/components/mini-lt/ManageTalks.tsx index 7b8e5ca..8052b96 100644 --- a/components/mini-lt/ManageTalks.tsx +++ b/components/mini-lt/ManageTalks.tsx @@ -70,6 +70,7 @@ export function ManageTalks({ {/* 編集中または未登録の場合のみフォームを表示 */} {editingTalk || !hasRegistered ? ( { await onAction(data); diff --git a/components/mini-lt/SubmitForm.tsx b/components/mini-lt/SubmitForm.tsx index ca864f9..acb9e37 100644 --- a/components/mini-lt/SubmitForm.tsx +++ b/components/mini-lt/SubmitForm.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect } from "react"; +import { useState } from "react"; import { Button, Input, @@ -25,29 +25,22 @@ interface SubmitFormProps { } const DURATION_OPTIONS = [1, 3, 5, 7, 10, 15] as const; +const DEFAULT_DURATION = 5; export function SubmitForm({ onSubmit, editingTalk, onCancelEdit, }: SubmitFormProps) { - const [title, setTitle] = useState(""); - const [description, setDescription] = useState(""); - const [duration, setDuration] = useState(5); // Default 5 minutes + const [title, setTitle] = useState(editingTalk?.title ?? ""); + const [description, setDescription] = useState( + editingTalk?.description ?? "", + ); + const [duration, setDuration] = useState( + editingTalk?.duration ?? DEFAULT_DURATION, + ); const [loading, setLoading] = useState(false); - useEffect(() => { - if (editingTalk) { - setTitle(editingTalk.title); - setDescription(editingTalk.description); - setDuration(editingTalk.duration); - } else { - setTitle(""); - setDescription(""); - setDuration(5); // Reset to default - } - }, [editingTalk]); - const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); @@ -56,7 +49,7 @@ export function SubmitForm({ if (!editingTalk) { setTitle(""); setDescription(""); - setDuration(5); + setDuration(DEFAULT_DURATION); } } finally { setLoading(false); diff --git a/components/onboarding-form.tsx b/components/onboarding-form.tsx index e2dda52..dd48e8a 100644 --- a/components/onboarding-form.tsx +++ b/components/onboarding-form.tsx @@ -314,9 +314,9 @@ export default function OnboardingForm({ const stepParam = searchParams.get("step"); if (success === "line_linked") { - setLineLinked(true); fetchProfile() .then((data) => { + setLineLinked(true); if (data?.line) { setLineUsername(data.line); setLineAvatar(data.lineAvatar ?? ""); @@ -340,9 +340,9 @@ export default function OnboardingForm({ }) .catch(console.error); } else if (success === "github_linked") { - setGithubLinked(true); fetchProfile() .then((data) => { + setGithubLinked(true); if (data?.github) { setGithubUsername(data.github); setGithubAvatar(data.githubAvatar ?? ""); @@ -350,21 +350,27 @@ export default function OnboardingForm({ }) .catch(console.error); } else if (success === "x_linked") { - setXLinked(true); fetchProfile() .then((data) => { + setXLinked(true); if (data?.x) { setXUsername(data.x); setXAvatar(data.xAvatar ?? ""); } }) .catch(console.error); - } else if (error === "line_link_failed") { - setStep3Error("LINE連携に失敗しました。もう一度お試しください。"); - } else if (error === "github_link_failed") { - setStep3Error("GitHub連携に失敗しました。もう一度お試しください。"); - } else if (error === "x_link_failed") { - setStep3Error("X連携に失敗しました。もう一度お試しください。"); + } else if (error) { + const errorMessage = + error === "line_link_failed" + ? "LINE連携に失敗しました。もう一度お試しください。" + : error === "github_link_failed" + ? "GitHub連携に失敗しました。もう一度お試しください。" + : error === "x_link_failed" + ? "X連携に失敗しました。もう一度お試しください。" + : null; + if (errorMessage) { + queueMicrotask(() => setStep3Error(errorMessage)); + } } if (success || error) { diff --git a/components/ui/carousel.tsx b/components/ui/carousel.tsx index bbfa730..b6e59db 100644 --- a/components/ui/carousel.tsx +++ b/components/ui/carousel.tsx @@ -111,9 +111,9 @@ const Carousel = React.forwardRef< return; } - onSelect(api); api.on("reInit", onSelect); api.on("select", onSelect); + api.emit("select"); return () => { api?.off("select", onSelect); diff --git a/components/ui/use-mobile.tsx b/components/ui/use-mobile.tsx index a93d583..6cda291 100644 --- a/components/ui/use-mobile.tsx +++ b/components/ui/use-mobile.tsx @@ -2,20 +2,20 @@ import * as React from "react"; const MOBILE_BREAKPOINT = 768; -export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState( - undefined, - ); +function subscribe(callback: () => void) { + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); + mql.addEventListener("change", callback); + return () => mql.removeEventListener("change", callback); +} + +function getSnapshot() { + return window.innerWidth < MOBILE_BREAKPOINT; +} - React.useEffect(() => { - const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); - const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - }; - mql.addEventListener("change", onChange); - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - return () => mql.removeEventListener("change", onChange); - }, []); +function getServerSnapshot() { + return false; +} - return !!isMobile; +export function useIsMobile() { + return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); } diff --git a/hooks/use-mobile.tsx b/hooks/use-mobile.tsx index a93d583..6cda291 100644 --- a/hooks/use-mobile.tsx +++ b/hooks/use-mobile.tsx @@ -2,20 +2,20 @@ import * as React from "react"; const MOBILE_BREAKPOINT = 768; -export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState( - undefined, - ); +function subscribe(callback: () => void) { + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); + mql.addEventListener("change", callback); + return () => mql.removeEventListener("change", callback); +} + +function getSnapshot() { + return window.innerWidth < MOBILE_BREAKPOINT; +} - React.useEffect(() => { - const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); - const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - }; - mql.addEventListener("change", onChange); - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - return () => mql.removeEventListener("change", onChange); - }, []); +function getServerSnapshot() { + return false; +} - return !!isMobile; +export function useIsMobile() { + return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); } diff --git a/package.json b/package.json index 74308c1..70b3181 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "my-v0-project", "version": "0.1.0", "private": true, + "packageManager": "pnpm@10.33.0", "scripts": { "dev": "next dev", "build": "next build",