first working state

This commit is contained in:
jane 2022-04-23 22:58:54 -04:00
parent 0a9b76f9c7
commit 4a0db151d4
23 changed files with 512 additions and 74 deletions

129
components/alert.js Normal file
View 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)}>
&times;
</a>
<span>{alert.message}</span>
</div>
))}
</div>
);
}

37
components/borderImage.js Normal file
View 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>
);
}

View 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>
);
}

View file

@ -1,3 +0,0 @@
export default function BorderSelect() {
}

View file

@ -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";
const pageSize = 48;
export default function Select(props) {
const { data, onSelect, selected = {} } = props;
console.log("data", data, "selected", selected);
const { data, total, onSelect, selected = {} } = props;
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 (
<div className={styles.select}>
{data &&
data.map((border, index) => {
<InfiniteScroll
dataLength={scrollData?.length || 0}
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 (
<div
className={[
styles.borderKeeper,
selected == border.id ? styles.selected : undefined,
].join(" ")}
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>
<BorderImage
key={border.id}
border={border}
selected={selected}
onSelect={onSelect}
/>
);
})}
</div>
</InfiniteScroll>
);
}