first working state
This commit is contained in:
		
							parent
							
								
									0a9b76f9c7
								
							
						
					
					
						commit
						4a0db151d4
					
				
					 23 changed files with 512 additions and 74 deletions
				
			
		
							
								
								
									
										129
									
								
								components/alert.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								components/alert.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,129 @@ | ||||||
|  | import { useState, useEffect, useRef } from "react"; | ||||||
|  | import { useRouter } from "next/router"; | ||||||
|  | import PropTypes from "prop-types"; | ||||||
|  | 
 | ||||||
|  | import { alertService, AlertType } from "../services"; | ||||||
|  | 
 | ||||||
|  | export { Alert }; | ||||||
|  | 
 | ||||||
|  | Alert.propTypes = { | ||||||
|  |   id: PropTypes.string, | ||||||
|  |   fade: PropTypes.bool, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Alert.defaultProps = { | ||||||
|  |   id: "default-alert", | ||||||
|  |   fade: true, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function Alert({ id, fade }) { | ||||||
|  |   const mounted = useRef(false); | ||||||
|  |   const router = useRouter(); | ||||||
|  |   const [alerts, setAlerts] = useState([]); | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     mounted.current = true; | ||||||
|  | 
 | ||||||
|  |     // subscribe to new alert notifications
 | ||||||
|  |     const subscription = alertService.onAlert(id).subscribe((alert) => { | ||||||
|  |       // clear alerts when an empty alert is received
 | ||||||
|  |       if (!alert.message) { | ||||||
|  |         setAlerts((alerts) => { | ||||||
|  |           // filter out alerts without 'keepAfterRouteChange' flag
 | ||||||
|  |           const filteredAlerts = alerts.filter((x) => x.keepAfterRouteChange); | ||||||
|  | 
 | ||||||
|  |           // remove 'keepAfterRouteChange' flag on the rest
 | ||||||
|  |           return omit(filteredAlerts, "keepAfterRouteChange"); | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         // add alert to array with unique id
 | ||||||
|  |         alert.itemId = Math.random(); | ||||||
|  |         setAlerts((alerts) => [...alerts, alert]); | ||||||
|  | 
 | ||||||
|  |         // auto close alert if required
 | ||||||
|  |         if (alert.autoClose) { | ||||||
|  |           setTimeout(() => removeAlert(alert), 3000); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // clear alerts on location change
 | ||||||
|  |     const clearAlerts = () => alertService.clear(id); | ||||||
|  |     router.events.on("routeChangeStart", clearAlerts); | ||||||
|  | 
 | ||||||
|  |     // clean up function that runs when the component unmounts
 | ||||||
|  |     return () => { | ||||||
|  |       mounted.current = false; | ||||||
|  | 
 | ||||||
|  |       // unsubscribe to avoid memory leaks
 | ||||||
|  |       subscription.unsubscribe(); | ||||||
|  |       router.events.off("routeChangeStart", clearAlerts); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // eslint-disable-next-line react-hooks/exhaustive-deps
 | ||||||
|  |   }, []); | ||||||
|  | 
 | ||||||
|  |   function omit(arr, key) { | ||||||
|  |     return arr.map((obj) => { | ||||||
|  |       const { [key]: omitted, ...rest } = obj; | ||||||
|  |       return rest; | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function removeAlert(alert) { | ||||||
|  |     if (!mounted.current) return; | ||||||
|  | 
 | ||||||
|  |     if (fade) { | ||||||
|  |       // fade out alert
 | ||||||
|  |       setAlerts((alerts) => | ||||||
|  |         alerts.map((x) => | ||||||
|  |           x.itemId === alert.itemId ? { ...x, fade: true } : x | ||||||
|  |         ) | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       // remove alert after faded out
 | ||||||
|  |       setTimeout(() => { | ||||||
|  |         setAlerts((alerts) => alerts.filter((x) => x.itemId !== alert.itemId)); | ||||||
|  |       }, 250); | ||||||
|  |     } else { | ||||||
|  |       // remove alert
 | ||||||
|  |       setAlerts((alerts) => alerts.filter((x) => x.itemId !== alert.itemId)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function cssClasses(alert) { | ||||||
|  |     if (!alert) return; | ||||||
|  | 
 | ||||||
|  |     const classes = ["alert", "alert-dismissable"]; | ||||||
|  | 
 | ||||||
|  |     const alertTypeClass = { | ||||||
|  |       [AlertType.Success]: "alert-success", | ||||||
|  |       [AlertType.Error]: "alert-danger", | ||||||
|  |       [AlertType.Info]: "alert-info", | ||||||
|  |       [AlertType.Warning]: "alert-warning", | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     classes.push(alertTypeClass[alert.type]); | ||||||
|  | 
 | ||||||
|  |     if (alert.fade) { | ||||||
|  |       classes.push("fade"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return classes.join(" "); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!alerts.length) return null; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div> | ||||||
|  |       {alerts.map((alert, index) => ( | ||||||
|  |         <div key={index} className={cssClasses(alert)}> | ||||||
|  |           <a className="close" onClick={() => removeAlert(alert)}> | ||||||
|  |             × | ||||||
|  |           </a> | ||||||
|  |           <span>{alert.message}</span> | ||||||
|  |         </div> | ||||||
|  |       ))} | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								components/borderImage.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								components/borderImage.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import Image from "next/image"; | ||||||
|  | import styles from "../styles/Components.module.css"; | ||||||
|  | 
 | ||||||
|  | export default function BorderImage(props) { | ||||||
|  |   const { key, border, selected, size, onSelect, customImage } = props; | ||||||
|  | 
 | ||||||
|  |   const imageSize = size || 128; | ||||||
|  | 
 | ||||||
|  |   const classNames = [ | ||||||
|  |     styles.borderKeeper, | ||||||
|  |     selected == border.id ? styles.selected : undefined, | ||||||
|  |   ].join(" "); | ||||||
|  |   return ( | ||||||
|  |     <div className={classNames} key={key}> | ||||||
|  |       <Image | ||||||
|  |         className={styles.userImage} | ||||||
|  |         src={customImage || "/user.png"} | ||||||
|  |         alt="user image" | ||||||
|  |         width={imageSize} | ||||||
|  |         height={imageSize} | ||||||
|  |         onClick={(ev) => { | ||||||
|  |           onSelect && onSelect(border.id); | ||||||
|  |         }} | ||||||
|  |       /> | ||||||
|  |       <Image | ||||||
|  |         className={styles.borderImage} | ||||||
|  |         src={`/images/${border.imageName}`} | ||||||
|  |         alt={`border with id ${border.id}`} | ||||||
|  |         width={Math.floor(imageSize * 1.03125)} | ||||||
|  |         height={Math.floor(imageSize * 1.03125)} | ||||||
|  |         onClick={(ev) => { | ||||||
|  |           onSelect && onSelect(border.id); | ||||||
|  |         }} | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								components/borderPreview.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								components/borderPreview.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | import BorderImage from "./borderImage"; | ||||||
|  | import styles from "../styles/Components.module.css"; | ||||||
|  | import { useSession } from "next-auth/react"; | ||||||
|  | 
 | ||||||
|  | export default function Preview(props) { | ||||||
|  |   const { data: session } = useSession(); | ||||||
|  |   const { data, current, selected, apply } = props; | ||||||
|  | 
 | ||||||
|  |   const currentItem = data?.filter( | ||||||
|  |     (item) => parseInt(item.id) === (current != null ? parseInt(current) : 0) | ||||||
|  |   )?.[0]; | ||||||
|  | 
 | ||||||
|  |   const selectedItem = data?.filter( | ||||||
|  |     (item) => parseInt(item.id) === parseInt(selected) | ||||||
|  |   )?.[0]; | ||||||
|  | 
 | ||||||
|  |   console.log(currentItem, selectedItem, session); | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className={styles.preview}> | ||||||
|  |       current | ||||||
|  |       <br /> | ||||||
|  |       {currentItem && ( | ||||||
|  |         <BorderImage | ||||||
|  |           border={currentItem} | ||||||
|  |           selected={-1} | ||||||
|  |           size={256} | ||||||
|  |           customImage={session?.user?.image} | ||||||
|  |         /> | ||||||
|  |       )} | ||||||
|  |       <br /> | ||||||
|  |       new | ||||||
|  |       <br /> | ||||||
|  |       {selectedItem && ( | ||||||
|  |         <BorderImage | ||||||
|  |           border={selectedItem} | ||||||
|  |           selected={-1} | ||||||
|  |           size={256} | ||||||
|  |           customImage={session?.user?.image} | ||||||
|  |         /> | ||||||
|  |       )} | ||||||
|  |       <br /> | ||||||
|  |       <button onClick={apply}>Apply</button> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| export default function BorderSelect() { |  | ||||||
|      |  | ||||||
| } |  | ||||||
|  | @ -1,43 +1,47 @@ | ||||||
| import Image from "next/image"; | import BorderImage from "./borderImage"; | ||||||
|  | import InfiniteScroll from "react-infinite-scroll-component"; | ||||||
|  | import { useEffect, useState } from "react"; | ||||||
| import styles from "../styles/Components.module.css"; | import styles from "../styles/Components.module.css"; | ||||||
| 
 | 
 | ||||||
|  | const pageSize = 48; | ||||||
|  | 
 | ||||||
| export default function Select(props) { | export default function Select(props) { | ||||||
|   const { data, onSelect, selected = {} } = props; |   const { data, total, onSelect, selected = {} } = props; | ||||||
|   console.log("data", data, "selected", selected); |   console.log("data", data, "selected", selected, total); | ||||||
|  | 
 | ||||||
|  |   const [scrollIndex, setScrollIndex] = useState(36); | ||||||
|  |   const [scrollData, setScrollData] = useState([]); | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     setScrollData(data.slice(0, pageSize - 1)); | ||||||
|  |   }, [data]); | ||||||
|  | 
 | ||||||
|  |   const fetchMore = () => { | ||||||
|  |     let newData = data.slice(scrollIndex, scrollIndex + pageSize); | ||||||
|  |     setScrollData([...scrollData, ...newData]); | ||||||
|  |     setScrollIndex(scrollIndex + pageSize); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className={styles.select}> |     <InfiniteScroll | ||||||
|       {data && |       dataLength={scrollData?.length || 0} | ||||||
|         data.map((border, index) => { |       next={fetchMore} | ||||||
|  |       hasMore={total > scrollData?.length || 0} | ||||||
|  |       height={"75vh"} | ||||||
|  |       className={styles.select} | ||||||
|  |       loader={<h4>Loading...</h4>} | ||||||
|  |       endMessage={<h4>No more borders.</h4>}> | ||||||
|  |       {scrollData && | ||||||
|  |         scrollData.map((border, index) => { | ||||||
|           return ( |           return ( | ||||||
|             <div |             <BorderImage | ||||||
|               className={[ |               key={border.id} | ||||||
|                 styles.borderKeeper, |               border={border} | ||||||
|                 selected == border.id ? styles.selected : undefined, |               selected={selected} | ||||||
|               ].join(" ")} |               onSelect={onSelect} | ||||||
|               key={border.id}> |             /> | ||||||
|               <Image |  | ||||||
|                 className={styles.userImage} |  | ||||||
|                 src="/user.png" |  | ||||||
|                 alt="user image" |  | ||||||
|                 width={128} |  | ||||||
|                 height={128} |  | ||||||
|                 onClick={(ev) => { |  | ||||||
|                   onSelect(border.id); |  | ||||||
|                 }} |  | ||||||
|               /> |  | ||||||
|               <Image |  | ||||||
|                 className={styles.borderImage} |  | ||||||
|                 src={`/images/${border.imageName}`} |  | ||||||
|                 alt={`border with id ${border.id}`} |  | ||||||
|                 width={132} |  | ||||||
|                 height={132} |  | ||||||
|                 onClick={(ev) => { |  | ||||||
|                   onSelect(border.id); |  | ||||||
|                 }} |  | ||||||
|               /> |  | ||||||
|             </div> |  | ||||||
|           ); |           ); | ||||||
|         })} |         })} | ||||||
|     </div> |     </InfiniteScroll> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,10 +9,21 @@ export const getBorderById = async (id) => { | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export const countAllBorders = async () => { | ||||||
|  |   return await prisma.borderImage.count(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| export const getAllBorders = async (limit = undefined, cursor = undefined) => { | export const getAllBorders = async (limit = undefined, cursor = undefined) => { | ||||||
|  |   const sanitizedLimit = parseInt(limit) || undefined; | ||||||
|  |   const sanitizedCursor = parseInt(cursor) || 0; | ||||||
|   return await prisma.borderImage.findMany({ |   return await prisma.borderImage.findMany({ | ||||||
|     take: limit, |     take: sanitizedLimit, | ||||||
|     cursor: cursor, |     cursor: { | ||||||
|  |       id: sanitizedCursor, | ||||||
|  |     }, | ||||||
|  |     orderBy: { | ||||||
|  |       id: "asc", | ||||||
|  |     }, | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -63,10 +74,10 @@ export const setUserBorder = async (req, borderId) => { | ||||||
|     create: { |     create: { | ||||||
|       userId: session.user.id, |       userId: session.user.id, | ||||||
|       discordId: accountData.providerAccountId, |       discordId: accountData.providerAccountId, | ||||||
|       borderId, |       borderId: parseInt(borderId), | ||||||
|     }, |     }, | ||||||
|     data: { |     update: { | ||||||
|       borderId, |       borderId: parseInt(borderId), | ||||||
|     }, |     }, | ||||||
|     where: { |     where: { | ||||||
|       userId: session.user.id, |       userId: session.user.id, | ||||||
|  |  | ||||||
|  | @ -8,4 +8,7 @@ module.exports = { | ||||||
|     config.plugins.push(new webpack.EnvironmentPlugin(environment)); |     config.plugins.push(new webpack.EnvironmentPlugin(environment)); | ||||||
|     return config; |     return config; | ||||||
|   }, |   }, | ||||||
|  |   images: { | ||||||
|  |     domains: ["localhost", "borders.j4.pm", "cdn.discordapp.com"], | ||||||
|  |   }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -19,7 +19,9 @@ | ||||||
|     "node-fetch": "^3.2.3", |     "node-fetch": "^3.2.3", | ||||||
|     "prisma": "^3.12.0", |     "prisma": "^3.12.0", | ||||||
|     "react": "18.0.0", |     "react": "18.0.0", | ||||||
|     "react-dom": "18.0.0" |     "react-dom": "18.0.0", | ||||||
|  |     "react-infinite-scroll-component": "^6.1.0", | ||||||
|  |     "rxjs": "^7.5.5" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "eslint": "8.13.0", |     "eslint": "8.13.0", | ||||||
|  |  | ||||||
|  | @ -1,9 +1,11 @@ | ||||||
| import "../styles/globals.css"; | import "../styles/globals.css"; | ||||||
| import { SessionProvider } from "next-auth/react"; | import { SessionProvider } from "next-auth/react"; | ||||||
|  | import { Alert } from "../components/alert"; | ||||||
| 
 | 
 | ||||||
| function MyApp({ Component, pageProps: { session, ...pageProps } }) { | function MyApp({ Component, pageProps: { session, ...pageProps } }) { | ||||||
|   return ( |   return ( | ||||||
|     <SessionProvider session={session}> |     <SessionProvider session={session}> | ||||||
|  |       <Alert fade={true} /> | ||||||
|       <Component {...pageProps} /> |       <Component {...pageProps} /> | ||||||
|     </SessionProvider> |     </SessionProvider> | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  | @ -12,6 +12,20 @@ export default NextAuth({ | ||||||
|     }), |     }), | ||||||
|   ], |   ], | ||||||
|   callbacks: { |   callbacks: { | ||||||
|  |     async signIn({ user, account, profile, email, credentials }) { | ||||||
|  |       console.log(user, account, profile, email, credentials); | ||||||
|  |       if (user.image != profile.image_url) { | ||||||
|  |         await prisma.user.update({ | ||||||
|  |           data: { | ||||||
|  |             image: profile.image_url, | ||||||
|  |           }, | ||||||
|  |           where: { | ||||||
|  |             id: user.id, | ||||||
|  |           }, | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |       return true; | ||||||
|  |     }, | ||||||
|     async session({ session, token, user }) { |     async session({ session, token, user }) { | ||||||
|       session.user.id = user.id; |       session.user.id = user.id; | ||||||
|       // console.log(JSON.stringify(user));
 |       // console.log(JSON.stringify(user));
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,9 @@ | ||||||
| import { getAllBorders } from "../../../lib/borders"; | import { getAllBorders, countAllBorders } from "../../../lib/borders"; | ||||||
| 
 | 
 | ||||||
| export default function handler(req, res) { | export default function handler(req, res) { | ||||||
|   getAllBorders().then((result) => { |   getAllBorders(req.query?.limit, req.query?.cursor).then((result) => { | ||||||
|     return res.status(200).json(result); |     countAllBorders().then((count) => { | ||||||
|  |       return res.status(200).json({ data: result, count }); | ||||||
|  |     }); | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
 | ||||||
| 
 | 
 | ||||||
| export default function handler(req, res) { | export default function handler(req, res) { | ||||||
|   res.status(200).json({ name: 'John Doe' }) |   res.status(200).json({ | ||||||
|  |     ok: "ok", | ||||||
|  |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,11 +1,21 @@ | ||||||
| import { getUserBorders } from "../../../../lib/borders"; | import { getUserBorders, setUserBorder } from "../../../../lib/borders"; | ||||||
| 
 | 
 | ||||||
| export default function handler(req, res) { | export default function handler(req, res) { | ||||||
|   getUserBorders(req).then((result) => { |   if (req.method === "POST") { | ||||||
|     if (result) { |     setUserBorder(req, req.body).then((result) => { | ||||||
|       return res.status(200).json(result); |       if (result) { | ||||||
|     } else { |         return res.status(200).json(result); | ||||||
|       return res.status(404).json({ error: "Not Found" }); |       } else { | ||||||
|     } |         return res.status(500).json({ error: "could not update border" }); | ||||||
|   }); |       } | ||||||
|  |     }); | ||||||
|  |   } else { | ||||||
|  |     getUserBorders(req).then((result) => { | ||||||
|  |       if (result) { | ||||||
|  |         return res.status(200).json(result); | ||||||
|  |       } else { | ||||||
|  |         return res.status(404).json({ error: "Not Found" }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,35 +1,91 @@ | ||||||
| import Head from "next/head"; | import Head from "next/head"; | ||||||
| import Image from "next/image"; | import Image from "next/image"; | ||||||
|  | import Router from "next/router"; | ||||||
| import styles from "../styles/Home.module.css"; | import styles from "../styles/Home.module.css"; | ||||||
| import UserInfo from "../components/userInfo"; | import UserInfo from "../components/userInfo"; | ||||||
|  | import Preview from "../components/borderPreview"; | ||||||
| import Select from "../components/select"; | import Select from "../components/select"; | ||||||
|  | import { alertService } from "../services"; | ||||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||||
| 
 | 
 | ||||||
| export default function Home() { | export default function Home() { | ||||||
|   const [data, setData] = useState(null); |   const [data, setData] = useState([]); | ||||||
|  |   const [total, setTotal] = useState(0); | ||||||
|   const [borderData, setBorderData] = useState(null); |   const [borderData, setBorderData] = useState(null); | ||||||
|   const [selected, setSelected] = useState(0); |   const [selected, setSelected] = useState(0); | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   // const pageSize = 36;
 | ||||||
|     fetch("api/border/all") | 
 | ||||||
|  |   const [cursor, setCursor] = useState(0); | ||||||
|  | 
 | ||||||
|  |   // const fetchMore = () => {
 | ||||||
|  |   //   console.log("fetch more");
 | ||||||
|  |   //   fetch(`api/border/all?limit=${pageSize}&cursor=${cursor}`)
 | ||||||
|  |   //     .then((res) => res.json())
 | ||||||
|  |   //     .then((res) => {
 | ||||||
|  |   //       setData([...data, ...res.data]);
 | ||||||
|  |   //       setTotal(res.count);
 | ||||||
|  |   //     });
 | ||||||
|  |   // };
 | ||||||
|  | 
 | ||||||
|  |   const applyBorder = () => { | ||||||
|  |     console.log("apply"); | ||||||
|  |     fetch("api/user/border/@me", { method: "POST", body: selected }) | ||||||
|       .then((res) => res.json()) |       .then((res) => res.json()) | ||||||
|       .then((data) => setData(data)); |       .then((res) => { | ||||||
|   }, []); |         console.log(res); | ||||||
|  |         if (res.error) { | ||||||
|  |           alertService.error(`error: ${res.error}`); | ||||||
|  |         } else { | ||||||
|  |           setBorderData(res); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     fetch("api/user/border/@me") |     fetch("api/user/border/@me") | ||||||
|  |       .then((res) => res.json()) | ||||||
|  |       .then((dat) => { | ||||||
|  |         console.log("border data", dat); | ||||||
|  |         setBorderData(dat); | ||||||
|  |         setSelected(dat?.borderId || 0); | ||||||
|  |       }); | ||||||
|  |   }, [data]); | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     fetch(`api/border/all`) | ||||||
|       .then((res) => res.json()) |       .then((res) => res.json()) | ||||||
|       .then((data) => { |       .then((data) => { | ||||||
|         setBorderData(data); |         setData(data.data); | ||||||
|         setSelected(data?.borderId || 0); |         setTotal(data.count); | ||||||
|       }); |       }); | ||||||
|   }, []); |   }, []); | ||||||
| 
 | 
 | ||||||
|  |   // useEffect(() => {
 | ||||||
|  |   //   let final = data?.[data?.length - 1];
 | ||||||
|  |   //   if (final) {
 | ||||||
|  |   //     setCursor(parseInt(final.id) + 1);
 | ||||||
|  |   //     console.log(cursor);
 | ||||||
|  |   //   }
 | ||||||
|  |   // }, [data, cursor]);
 | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     const timer = setTimeout(() => { | ||||||
|  |       // if data has not loaded in 5 seconds
 | ||||||
|  |       if (!data || !borderData) { | ||||||
|  |         console.log(data, borderData); | ||||||
|  |         Router.reload(); | ||||||
|  |       } else { | ||||||
|  |         console.log("data loaded properly, not reloading."); | ||||||
|  |       } | ||||||
|  |     }, 5000); | ||||||
|  |     return () => clearTimeout(timer); | ||||||
|  |   }, [data, borderData]); | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className={styles.container}> |     <div className={styles.container}> | ||||||
|       <Head> |       <Head> | ||||||
|         <title>Create Next App</title> |         <title>Borders</title> | ||||||
|         <meta name="description" content="Generated by create next app" /> |  | ||||||
|         <link rel="icon" href="/favicon.ico" /> |         <link rel="icon" href="/favicon.ico" /> | ||||||
|       </Head> |       </Head> | ||||||
|       <header> |       <header> | ||||||
|  | @ -39,7 +95,18 @@ export default function Home() { | ||||||
|         </div> |         </div> | ||||||
|       </header> |       </header> | ||||||
|       <main> |       <main> | ||||||
|         <Select data={data} onSelect={setSelected} selected={selected} /> |         <Preview | ||||||
|  |           data={data} | ||||||
|  |           current={borderData?.borderId} | ||||||
|  |           selected={selected} | ||||||
|  |           apply={applyBorder} | ||||||
|  |         /> | ||||||
|  |         <Select | ||||||
|  |           data={data} | ||||||
|  |           total={total} | ||||||
|  |           onSelect={setSelected} | ||||||
|  |           selected={selected} | ||||||
|  |         /> | ||||||
|       </main> |       </main> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										28
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							|  | @ -12,6 +12,8 @@ specifiers: | ||||||
|   prisma: ^3.12.0 |   prisma: ^3.12.0 | ||||||
|   react: 18.0.0 |   react: 18.0.0 | ||||||
|   react-dom: 18.0.0 |   react-dom: 18.0.0 | ||||||
|  |   react-infinite-scroll-component: ^6.1.0 | ||||||
|  |   rxjs: ^7.5.5 | ||||||
| 
 | 
 | ||||||
| dependencies: | dependencies: | ||||||
|   '@next-auth/prisma-adapter': 1.0.3_2b535bbfb604d219371c9582b52b11b1 |   '@next-auth/prisma-adapter': 1.0.3_2b535bbfb604d219371c9582b52b11b1 | ||||||
|  | @ -23,6 +25,8 @@ dependencies: | ||||||
|   prisma: 3.12.0 |   prisma: 3.12.0 | ||||||
|   react: 18.0.0 |   react: 18.0.0 | ||||||
|   react-dom: 18.0.0_react@18.0.0 |   react-dom: 18.0.0_react@18.0.0 | ||||||
|  |   react-infinite-scroll-component: 6.1.0_react@18.0.0 | ||||||
|  |   rxjs: 7.5.5 | ||||||
| 
 | 
 | ||||||
| devDependencies: | devDependencies: | ||||||
|   eslint: 8.13.0 |   eslint: 8.13.0 | ||||||
|  | @ -1610,6 +1614,15 @@ packages: | ||||||
|       scheduler: 0.21.0 |       scheduler: 0.21.0 | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|  |   /react-infinite-scroll-component/6.1.0_react@18.0.0: | ||||||
|  |     resolution: {integrity: sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==} | ||||||
|  |     peerDependencies: | ||||||
|  |       react: '>=16.0.0' | ||||||
|  |     dependencies: | ||||||
|  |       react: 18.0.0 | ||||||
|  |       throttle-debounce: 2.3.0 | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /react-is/16.13.1: |   /react-is/16.13.1: | ||||||
|     resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} |     resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} | ||||||
|     dev: true |     dev: true | ||||||
|  | @ -1676,6 +1689,12 @@ packages: | ||||||
|       queue-microtask: 1.2.3 |       queue-microtask: 1.2.3 | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /rxjs/7.5.5: | ||||||
|  |     resolution: {integrity: sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==} | ||||||
|  |     dependencies: | ||||||
|  |       tslib: 2.4.0 | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /scheduler/0.21.0: |   /scheduler/0.21.0: | ||||||
|     resolution: {integrity: sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==} |     resolution: {integrity: sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==} | ||||||
|     dependencies: |     dependencies: | ||||||
|  | @ -1801,6 +1820,11 @@ packages: | ||||||
|     resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} |     resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /throttle-debounce/2.3.0: | ||||||
|  |     resolution: {integrity: sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==} | ||||||
|  |     engines: {node: '>=8'} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /to-regex-range/5.0.1: |   /to-regex-range/5.0.1: | ||||||
|     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} |     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} | ||||||
|     engines: {node: '>=8.0'} |     engines: {node: '>=8.0'} | ||||||
|  | @ -1821,6 +1845,10 @@ packages: | ||||||
|     resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} |     resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /tslib/2.4.0: | ||||||
|  |     resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /tsutils/3.21.0: |   /tsutils/3.21.0: | ||||||
|     resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} |     resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} | ||||||
|     engines: {node: '>= 6'} |     engines: {node: '>= 6'} | ||||||
|  |  | ||||||
|  | @ -71,4 +71,5 @@ model BorderImage { | ||||||
|   appId      Int? |   appId      Int? | ||||||
|   appName    String? |   appName    String? | ||||||
|   borderName String? |   borderName String? | ||||||
|  |   itemType   Int? | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										56
									
								
								services/alert.service.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								services/alert.service.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | import { Subject } from "rxjs"; | ||||||
|  | import { filter } from "rxjs/operators"; | ||||||
|  | 
 | ||||||
|  | export const alertService = { | ||||||
|  |   onAlert, | ||||||
|  |   success, | ||||||
|  |   error, | ||||||
|  |   info, | ||||||
|  |   warn, | ||||||
|  |   alert, | ||||||
|  |   clear, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const AlertType = { | ||||||
|  |   Success: "Success", | ||||||
|  |   Error: "Error", | ||||||
|  |   Info: "Info", | ||||||
|  |   Warning: "Warning", | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const alertSubject = new Subject(); | ||||||
|  | const defaultId = "default-alert"; | ||||||
|  | 
 | ||||||
|  | // enable subscribing to alerts observable
 | ||||||
|  | function onAlert(id = defaultId) { | ||||||
|  |   return alertSubject.asObservable().pipe(filter((x) => x && x.id === id)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // convenience methods
 | ||||||
|  | function success(message, options) { | ||||||
|  |   alert({ ...options, type: AlertType.Success, message }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function error(message, options) { | ||||||
|  |   alert({ ...options, type: AlertType.Error, message }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function info(message, options) { | ||||||
|  |   alert({ ...options, type: AlertType.Info, message }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function warn(message, options) { | ||||||
|  |   alert({ ...options, type: AlertType.Warning, message }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // core alert method
 | ||||||
|  | function alert(alert) { | ||||||
|  |   alert.id = alert.id || defaultId; | ||||||
|  |   alert.autoClose = alert.autoClose === undefined ? true : alert.autoClose; | ||||||
|  |   alertSubject.next(alert); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // clear alerts
 | ||||||
|  | function clear(id = defaultId) { | ||||||
|  |   alertSubject.next({ id }); | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								services/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/index.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | export * from "./alert.service"; | ||||||
|  | @ -2,27 +2,31 @@ | ||||||
|   min-height: 90vh; |   min-height: 90vh; | ||||||
|   max-height: 90vh; |   max-height: 90vh; | ||||||
|   max-width: 50vw; |   max-width: 50vw; | ||||||
|   left: 45vw; |   left: 40vw; | ||||||
|   position: relative; |   position: relative; | ||||||
|   overflow-y: scroll; |   overflow-y: scroll; | ||||||
|   display: flex; |   display: flex; | ||||||
|   flex-wrap: wrap; |   flex-wrap: wrap; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .preview { | ||||||
|  |   max-width: 50vw; | ||||||
|  |   left: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .borderKeeper { | .borderKeeper { | ||||||
|   width: 132px !important; |   width: 132px !important; | ||||||
|   height: 132px !important; |   height: 132px !important; | ||||||
|   padding: 4px; |   padding: 6px; | ||||||
|  |   margin: 12px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .borderKeeper > span { | .borderKeeper > span { | ||||||
|   display: inline-table !important; |   display: inline-table !important; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .userImage, | .borderKeeper :nth-child(2) { | ||||||
| .borderImage { |   top: -8em; | ||||||
|   position: relative; |  | ||||||
|   left: 0; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .userImage { | .userImage { | ||||||
|  | @ -32,9 +36,10 @@ | ||||||
| 
 | 
 | ||||||
| .borderImage { | .borderImage { | ||||||
|   z-index: 3; |   z-index: 3; | ||||||
|   top: -256px !important; |   transform: scale(1.22); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .selected { | .selected { | ||||||
|   border: 1px solid black; |   border: 3px solid black; | ||||||
|  |   z-index: 5; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   display: flex; |   display: flex; | ||||||
|   flex-direction: column; |   flex-direction: column; | ||||||
|   justify-content: end; |   justify-content: flex-end; | ||||||
|   align-items: flex-end; |   align-items: flex-end; | ||||||
|   text-align: right; |   text-align: right; | ||||||
|   width: 50vw; |   width: 50vw; | ||||||
|  |  | ||||||
|  | @ -14,3 +14,17 @@ a { | ||||||
| * { | * { | ||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .alert { | ||||||
|  |   border: 1px black solid; | ||||||
|  |   border-radius: 7%; | ||||||
|  |   max-width: min-content; | ||||||
|  |   padding: 4px; | ||||||
|  |   background-color: lightgray; | ||||||
|  |   position: relative; | ||||||
|  |   left: 45vw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | main { | ||||||
|  |   display: flex; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -70,6 +70,8 @@ async function main() { | ||||||
|       const appid = border.appid; |       const appid = border.appid; | ||||||
|       const name = border.community_item_data.item_name; |       const name = border.community_item_data.item_name; | ||||||
| 
 | 
 | ||||||
|  |       const community_item_type = border.community_item_type; | ||||||
|  | 
 | ||||||
|       const filename_large = border.community_item_data.item_image_large; |       const filename_large = border.community_item_data.item_image_large; | ||||||
|       if (filename_large == "c05c0155855b74c28e0f6e9417d4afa3c99d76ef.png") { |       if (filename_large == "c05c0155855b74c28e0f6e9417d4afa3c99d76ef.png") { | ||||||
|         console.log(border); |         console.log(border); | ||||||
|  | @ -84,6 +86,7 @@ async function main() { | ||||||
|         borders.push({ |         borders.push({ | ||||||
|           name: `${name}-LARGE`, |           name: `${name}-LARGE`, | ||||||
|           borderURL: `${borderURL}/${filename_large}`, |           borderURL: `${borderURL}/${filename_large}`, | ||||||
|  |           itemType: community_item_type, | ||||||
|           appInfo, |           appInfo, | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|  | @ -92,6 +95,7 @@ async function main() { | ||||||
|         borders.push({ |         borders.push({ | ||||||
|           name: `${name}-SMALL`, |           name: `${name}-SMALL`, | ||||||
|           borderURL: `${borderURL}/${filename_small}`, |           borderURL: `${borderURL}/${filename_small}`, | ||||||
|  |           itemType: community_item_type, | ||||||
|           appInfo, |           appInfo, | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ let catalogue = async () => { | ||||||
|           id: 0, |           id: 0, | ||||||
|           imageName: item, |           imageName: item, | ||||||
|           borderName: "Default", |           borderName: "Default", | ||||||
|  |           itemType: 54, | ||||||
|         }, |         }, | ||||||
|       }); |       }); | ||||||
|       numAdded++; |       numAdded++; | ||||||
|  | @ -95,12 +96,14 @@ let download = async () => { | ||||||
|           appId: value.appInfo.appid, |           appId: value.appInfo.appid, | ||||||
|           appName: value.appInfo.name, |           appName: value.appInfo.name, | ||||||
|           borderName: value.name, |           borderName: value.name, | ||||||
|  |           itemType: value.itemType, | ||||||
|         }, |         }, | ||||||
|         update: { |         update: { | ||||||
|           imageName: filename, |           imageName: filename, | ||||||
|           appId: value.appInfo.appid, |           appId: value.appInfo.appid, | ||||||
|           appName: value.appInfo.name, |           appName: value.appInfo.name, | ||||||
|           borderName: value.name, |           borderName: value.name, | ||||||
|  |           itemType: value.itemType, | ||||||
|         }, |         }, | ||||||
|         where: { |         where: { | ||||||
|           imageName: filename, |           imageName: filename, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue