@@ -15,61 +15,66 @@ import PersonDetailedMenu from "./components/PersonDetailedMenu";
1515export default function Home ( ) {
1616 const { data, error } = useRecommended ( ) ;
1717 const controls = useAnimation ( ) ;
18+ const backCardControls = useAnimation ( ) ;
1819 const [ clickedButton , setClickedButton ] = useState < string > ( "" ) ;
20+
1921 const [ openDetailedMenu , setOpenDetailedMenu ] = useState ( false ) ;
2022 const {
2123 state : { data : currentUser } ,
2224 } = useAboutMe ( ) ;
2325
2426 const [ _ , rerender ] = useState ( { } ) ;
25- const [ recommended , setRecommended ] =
26- useState < Queue < UserWithCoursesAndSubjects > | null > ( null ) ;
27+ const [ recommended , setRecommended ] = useState <
28+ Queue < UserWithCoursesAndSubjects >
29+ > ( ( ) => new Queue ( [ ] ) ) ;
30+
2731 useEffect ( ( ) => {
2832 if ( data ) setRecommended ( new Queue ( data ) ) ;
2933 } , [ data ] ) ;
3034
31- const displayedUser = recommended ?. peek ( 0 ) ;
32- const nextUser = recommended ?. peek ( 1 ) ;
33- const reject = useCallback ( ( ) => {
34- const current = recommended ?. pop ( ) ;
35- if ( ! current ) return ;
36- recommended ?. push ( current ) ;
37- rerender ( { } ) ;
38- } , [ recommended ] ) ;
39- const accept = useCallback ( async ( ) => {
40- const current = recommended ?. pop ( ) ;
41- if ( ! current ) return ;
42- request . send ( current . id ) ;
43- rerender ( { } ) ;
44- } , [ recommended ] ) ;
45-
46- const onClickClose = useCallback ( ( ) => {
47- setClickedButton ( "cross" ) ;
48- controls
49- . start ( {
50- x : [ 0 , - 1000 ] ,
51- transition : { duration : 0.5 , times : [ 0 , 1 ] , delay : 0.2 } ,
52- } )
53- . then ( ( ) => {
54- reject ( ) ;
55- setClickedButton ( "" ) ;
56- controls . set ( { x : 0 } ) ;
57- } ) ;
58- } , [ controls , reject ] ) ;
59-
60- const onClickHeart = useCallback ( ( ) => {
61- setClickedButton ( "heart" ) ;
62- controls
63- . start ( {
64- x : [ 0 , 1000 ] ,
65- transition : { duration : 0.5 , times : [ 0 , 1 ] , delay : 0.2 } ,
66- } )
67- . then ( ( ) => {
68- accept ( ) ;
69- setClickedButton ( "" ) ;
70- controls . set ( { x : 0 } ) ;
71- } ) ;
72- } , [ controls , accept ] ) ;
35+ const displayedUser = recommended . peek ( 0 ) ;
36+ const nextUser = recommended . peek ( 1 ) ;
37+
38+ const handleAction = useCallback (
39+ async ( action : "accept" | "reject" ) => {
40+ const current = recommended . peek ( 0 ) ;
41+ if ( ! current ) return ;
42+
43+ setClickedButton ( action === "accept" ? "heart" : "cross" ) ;
44+
45+ // アニメーション開始前に BackCard の位置をリセット
46+ backCardControls . set ( { x : 0 , y : 0 } ) ;
47+
48+ // 移動アニメーションを実行
49+ await Promise . all ( [
50+ controls . start ( {
51+ x : action === "accept" ? 1000 : - 1000 ,
52+ transition : { duration : 0.5 , delay : 0.2 } ,
53+ } ) ,
54+ backCardControls . start ( {
55+ x : 10 ,
56+ y : 10 ,
57+ transition : { duration : 0.5 , delay : 0.2 } ,
58+ } ) ,
59+ ] ) ;
60+
61+ // 状態更新
62+ recommended . pop ( ) ;
63+ if ( action === "accept" ) {
64+ await request . send ( current . id ) ;
65+ } else if ( action === "reject" ) {
66+ recommended . push ( current ) ;
67+ }
68+ rerender ( { } ) ;
69+
70+ // 位置をリセット
71+ controls . set ( { x : 0 } ) ;
72+ backCardControls . set ( { x : 0 , y : 0 } ) ;
73+
74+ setClickedButton ( "" ) ;
75+ } ,
76+ [ recommended , controls , backCardControls ] ,
77+ ) ;
7378
7479 if ( recommended == null ) {
7580 return < FullScreenCircularProgress /> ;
@@ -85,40 +90,57 @@ export default function Home() {
8590 return (
8691 < div className = "flex h-full flex-col items-center justify-center p-4" >
8792 { displayedUser && (
88- < >
89- < div className = "flex h-full flex-col items-center justify-center" >
90- { nextUser && (
91- < div className = "relative h-full w-full" >
92- < div className = "-translate-x-4 -translate-y-4 inset-0 z-0 mt-4 transform" >
93- < Card displayedUser = { nextUser } currentUser = { currentUser } />
94- </ div >
95- < motion . div
96- animate = { controls }
97- className = "absolute inset-0 z-10 mt-4 flex items-center justify-center"
98- >
99- < DraggableCard
100- displayedUser = { displayedUser }
101- currentUser = { currentUser }
102- onSwipeLeft = { reject }
103- onSwipeRight = { accept }
104- clickedButton = { clickedButton }
105- />
106- </ motion . div >
107- </ div >
108- ) }
109- < button
110- type = "button"
111- onClick = { ( ) => setOpenDetailedMenu ( ! openDetailedMenu ) }
112- >
113- てすと
114- </ button >
115- < div className = "button-container mt-4 mb-4 flex w-full justify-center space-x-8" >
116- < CloseButton onclick = { onClickClose } icon = { < CloseIconStyled /> } />
117- < GoodButton
118- onclick = { onClickHeart }
119- icon = { < FavoriteIconStyled /> }
120- />
93+ < div className = "flex h-full flex-col items-center justify-center" >
94+ { nextUser && (
95+ < div className = "relative grid h-full w-full grid-cols-1 grid-rows-1" >
96+ < motion . div
97+ className = "z-0 col-start-1 row-start-1 mt-4"
98+ initial = { { x : 0 , y : 0 } } // 初期位置を (0, 0) に設定
99+ animate = { backCardControls }
100+ >
101+ < Card displayedUser = { nextUser } currentUser = { currentUser } />
102+ </ motion . div >
103+ < motion . div
104+ className = "z-10 col-start-1 row-start-1 mt-4 flex items-center justify-center"
105+ animate = { controls }
106+ >
107+ < DraggableCard
108+ displayedUser = { displayedUser }
109+ currentUser = { currentUser }
110+ onSwipeLeft = { ( ) => handleAction ( "reject" ) }
111+ onSwipeRight = { ( ) => handleAction ( "accept" ) }
112+ clickedButton = { clickedButton }
113+ setOpenDetailedMenu = { setOpenDetailedMenu }
114+ />
115+ </ motion . div >
116+ </ div >
117+ ) }
118+ { nextUser == null && (
119+ < div className = "relative grid h-full w-full grid-cols-1 grid-rows-1" >
120+ < motion . div
121+ className = "z-10 col-start-1 row-start-1 mt-4 flex items-center justify-center"
122+ animate = { controls }
123+ >
124+ < DraggableCard
125+ displayedUser = { displayedUser }
126+ currentUser = { currentUser }
127+ onSwipeLeft = { ( ) => handleAction ( "reject" ) }
128+ onSwipeRight = { ( ) => handleAction ( "accept" ) }
129+ clickedButton = { clickedButton }
130+ setOpenDetailedMenu = { setOpenDetailedMenu }
131+ />
132+ </ motion . div >
121133 </ div >
134+ ) }
135+ < div className = "button-container mt-4 mb-4 flex w-full justify-center space-x-8" >
136+ < CloseButton
137+ onclick = { ( ) => handleAction ( "reject" ) }
138+ icon = { < CloseIconStyled /> }
139+ />
140+ < GoodButton
141+ onclick = { ( ) => handleAction ( "accept" ) }
142+ icon = { < FavoriteIconStyled /> }
143+ />
122144 </ div >
123145 { openDetailedMenu && (
124146 < PersonDetailedMenu
@@ -129,7 +151,7 @@ export default function Home() {
129151 currentUser = { currentUser }
130152 />
131153 ) }
132- </ >
154+ </ div >
133155 ) }
134156 </ div >
135157 ) ;
@@ -179,8 +201,5 @@ class Queue<T> {
179201 }
180202 pop ( ) : T | undefined {
181203 return this . store . shift ( ) ;
182- // yes, I know what you want to say, it has O(n) time complexity.
183- // it doesn't really matter if there is only like 100 people in home queue at most.
184- // if you really care about performance, why don't you go and limit the amount of people to fetch? that probably has significantly more impact to the performance.
185204 }
186205}
0 commit comments