FEAT Using a library for infinite scroll.
This is better. Always nice to let the professionals do all the hard lifting for ya when you're pressed for time. Also: Incorporated a pretty nice little modal pop-up. Styling sucks (at the moment), but I can fix that later.
This commit is contained in:
parent
642e7e9f2f
commit
73e310ea14
|
@ -3,6 +3,7 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@egjs/react-infinitegrid": "^3.0.5",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
|
@ -13,6 +14,7 @@
|
|||
"node-sass": "^4.14.1",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-modal": "^3.11.2",
|
||||
"react-scripts": "3.4.3",
|
||||
"sass": "^1.26.10",
|
||||
"typescript": "^3.9.3"
|
||||
|
@ -38,6 +40,7 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react-modal": "^3.10.6",
|
||||
"http-server": "^0.12.3"
|
||||
}
|
||||
}
|
||||
|
|
58
src/App.scss
58
src/App.scss
|
@ -9,6 +9,21 @@ body {
|
|||
font-family: "Marcellus";
|
||||
}
|
||||
|
||||
.current-card {
|
||||
background: repeat url("images/elder_scrolling_bg.png") #0c0c0c;
|
||||
color: #f8f8f8;
|
||||
|
||||
}
|
||||
|
||||
.fullcard tr td {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.fullcard tr td:nth-child(3) {
|
||||
padding-left: 8rem;
|
||||
}
|
||||
|
||||
|
||||
body * {
|
||||
font-family: "Marcellus";
|
||||
}
|
||||
|
@ -50,32 +65,29 @@ header {
|
|||
}
|
||||
}
|
||||
|
||||
.cardbox {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
font-size: 1.4rem;
|
||||
justify-content: space-between;
|
||||
align-content: space-between;
|
||||
.card {
|
||||
width: 192px;
|
||||
height: 540px;
|
||||
border: 1px solid #707c80;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem;
|
||||
margin: 0.5rem;
|
||||
|
||||
img {
|
||||
height: 290px;
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
line-height: 1.4;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 192px;
|
||||
border: 1px solid #707c80;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem;
|
||||
margin: 0.5rem;
|
||||
|
||||
img {
|
||||
height: 290px;
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
line-height: 1.4;
|
||||
}
|
||||
.content table td {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.lds-spinner-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
import * as React from "react";
|
||||
import { CardProps } from "./types";
|
||||
|
||||
export const Card = ({ card }: { card: CardProps }) => {
|
||||
export const Card = ({
|
||||
card,
|
||||
onClick
|
||||
}: {
|
||||
card: CardProps;
|
||||
onClick: Function | null;
|
||||
}) => {
|
||||
return (
|
||||
<div className="card" key="{idx}">
|
||||
<div
|
||||
className="card"
|
||||
key="{idx}"
|
||||
onClick={_event => onClick && onClick(card.id)}
|
||||
>
|
||||
<img src={card.imageUrl} alt={card.name} />
|
||||
<div className="content">
|
||||
<h4>{card.name}</h4>
|
||||
|
@ -28,4 +38,3 @@ export const Card = ({ card }: { card: CardProps }) => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@ import * as React from "react";
|
|||
import { useState, useEffect, useRef } from "react";
|
||||
import { Loading } from "./Loading";
|
||||
import { Card } from "./Card";
|
||||
import { useInfiniteScroll } from "./infiniteScroll";
|
||||
import { JustifiedLayout, GridLayout } from "@egjs/react-infinitegrid";
|
||||
import ReactModal from "react-modal";
|
||||
import { FullCard, fullcardStyles } from "./FullCard";
|
||||
import {
|
||||
CardProps,
|
||||
CardRequestProps,
|
||||
|
@ -16,54 +18,85 @@ const firstUrl = `https://api.elderscrollslegends.io/v1/cards?pageSize=${PAGE_SI
|
|||
|
||||
const emptyCards: CardState = {
|
||||
cards: [],
|
||||
next: firstUrl
|
||||
next: firstUrl,
|
||||
maxCards: -1
|
||||
};
|
||||
|
||||
export const Cards = () => {
|
||||
const [cards, setCards]: CardStateHandler = useState(emptyCards);
|
||||
const [cardPage, setCardPage]: [string | null, Function] = useState(null);
|
||||
const [loading, setLoading]: [boolean, Function] = useState(false);
|
||||
const [currentCard, setCurrentCard]: [CardProps | null, Function] = useState(
|
||||
null
|
||||
);
|
||||
|
||||
const getNextPage = () => {
|
||||
setCardPage(cards.next);
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
let pageBottomRef = useRef(null);
|
||||
useInfiniteScroll(pageBottomRef, getNextPage);
|
||||
|
||||
const fetchMoreCards = async () => {
|
||||
if (cards.next === null || cardPage === null) {
|
||||
return cards;
|
||||
const loadItems = () => {
|
||||
if (cards.next === null || cards.cards.length === cards.maxCards) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request = new Request(cardPage!);
|
||||
const response = await fetch(request);
|
||||
const data: CardRequestProps = await response.json();
|
||||
setCards({
|
||||
cards: cards.cards.concat(data.cards),
|
||||
next: data._links && data._links.next ? data._links.next : null
|
||||
});
|
||||
setLoading(false);
|
||||
setLoading(true);
|
||||
const request = new Request(cards.next);
|
||||
fetch(request)
|
||||
.then(response => response.json())
|
||||
.then((data: CardRequestProps) => {
|
||||
setCards({
|
||||
cards: [...cards.cards, ...data.cards],
|
||||
next: data._links && data._links.next ? data._links.next : null,
|
||||
maxcards: data._totalCount
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
if (cards.next && cardPage === null) {
|
||||
getNextPage();
|
||||
}
|
||||
const loadMoreCards = (options: any) => {
|
||||
if (options.startLoading !== null) {
|
||||
options.startLoading();
|
||||
}
|
||||
loadItems();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchMoreCards();
|
||||
}, [cardPage]);
|
||||
const onLayoutComplete = (options: any) => {
|
||||
!options.isLayout && options.endLoading();
|
||||
};
|
||||
|
||||
const handleCloseCard = () => {
|
||||
setCurrentCard(null);
|
||||
};
|
||||
|
||||
const handleOpenCard = (id: string) => {
|
||||
const card = cards.cards.find(card => card.id === id);
|
||||
if (card) {
|
||||
setCurrentCard(card);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="cardbox">
|
||||
<ReactModal
|
||||
isOpen={currentCard !== null}
|
||||
style={fullcardStyles}
|
||||
contentLabel=""
|
||||
onRequestClose={handleCloseCard}
|
||||
>
|
||||
<FullCard card={currentCard} />
|
||||
</ReactModal>
|
||||
<GridLayout
|
||||
options={{
|
||||
isConstantSize: true,
|
||||
isEqualSize: true,
|
||||
transitionDuration: 0.2
|
||||
}}
|
||||
layoutOptions={{
|
||||
margin: 10
|
||||
}}
|
||||
onAppend={loadMoreCards}
|
||||
onLayoutComplete={onLayoutComplete}
|
||||
>
|
||||
{cards.cards.map((card: CardProps, count: number) => (
|
||||
<Card card={card} key={count} />
|
||||
<Card card={card} key={count} onClick={handleOpenCard} />
|
||||
))}
|
||||
</div>
|
||||
{loading && <Loading />}
|
||||
<div id="page-bottom" ref={pageBottomRef} />
|
||||
</GridLayout>
|
||||
{loading ? <Loading /> : ""}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
import * as React from "react";
|
||||
import { CardProps } from "./types";
|
||||
|
||||
export const fullcardStyles = {
|
||||
content: {
|
||||
backgroundColor: "#0c0c0c",
|
||||
color: "#f8f8f8"
|
||||
}
|
||||
};
|
||||
|
||||
export const FullCard = ({ card }: { card: CardProps | null }) => {
|
||||
if (card === null) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fullcard">
|
||||
<img src={card.imageUrl} alt={card.name} />
|
||||
<div className="content">
|
||||
<h4>{card.name}</h4>
|
||||
<p className="card-text">{card.text}</p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Type</strong>
|
||||
</td>
|
||||
<td>{card.type}</td>
|
||||
<td>
|
||||
<strong>Cost</strong>
|
||||
</td>
|
||||
<td>{card.cost}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Subtypes</strong>
|
||||
</td>
|
||||
<td>{card.subtypes.join(", ")}</td>
|
||||
<td>
|
||||
<strong>Power</strong>
|
||||
</td>
|
||||
<td>{card.power}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Rarity</strong>
|
||||
</td>
|
||||
<td>{card.rarity}</td>
|
||||
<td>
|
||||
<strong>Health</strong>
|
||||
</td>
|
||||
<td>{card.health}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Set</strong>
|
||||
</td>
|
||||
<td>{card.set.name}</td>
|
||||
<td>
|
||||
<strong>Soul Summon</strong>
|
||||
</td>
|
||||
<td>{card.soulSummon}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Attributes</strong>
|
||||
</td>
|
||||
<td>{card.attributes.join(", ")}</td>
|
||||
<td>
|
||||
<strong>Soul Trap</strong>
|
||||
</td>
|
||||
<td>{card.soulTrap}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Keywords</strong>
|
||||
</td>
|
||||
<td>{card.keywords}</td>
|
||||
<td />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Set Name:</strong>
|
||||
</td>
|
||||
<td>{card.set.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Type:</strong>
|
||||
</td>
|
||||
<td>{card.type}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{card.collectible ? <p>This card is considered collectible.</p> : ""}
|
||||
{card.unique ? (
|
||||
<p>
|
||||
This card is unique. Unique cards may only be used once per deck.
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,33 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { useEffect, useCallback } from "react";
|
||||
|
||||
// From the Smashing Magazine article,
|
||||
// https://www.smashingmagazine.com/2020/03/infinite-scroll-lazy-image-loading-react/
|
||||
|
||||
// As this is new to me, I'm going to try to explain it;
|
||||
// IntersectionObserver takes a callback to be called when it's
|
||||
// triggred. It's trigged when the node it's been asked to observe
|
||||
// changes its relationship with another object or, without a
|
||||
// specified object, with the viewport. When the `intersectionRatio`
|
||||
// exceed zero, it is _intersecting_ the viewport, i.e. it is now
|
||||
// visible.
|
||||
|
||||
// useCallback is a memoizer; it only updates the contents of the
|
||||
// function its wrapped if the included function changes, which
|
||||
// it shouldn't.
|
||||
|
||||
export const useInfiniteScroll = (scrollRef: React.RefObject<HTMLElement>, stateUpdate: Function) => {
|
||||
const scrollObserver = (node: HTMLElement) => {
|
||||
new IntersectionObserver(entries => {
|
||||
if (entries.some(en => en.intersectionRatio > 0)) {
|
||||
stateUpdate();
|
||||
}
|
||||
}).observe(node);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollRef.current) {
|
||||
scrollObserver(scrollRef.current);
|
||||
}
|
||||
}, [scrollObserver, scrollRef]);
|
||||
}
|
|
@ -9,11 +9,14 @@ export interface CardProps {
|
|||
rarity: string;
|
||||
type: string;
|
||||
cost: number;
|
||||
power: number;
|
||||
health: number;
|
||||
set: SetProps;
|
||||
collectible: boolean;
|
||||
soulSummon: number;
|
||||
soulTrap: number;
|
||||
text: string;
|
||||
subtypes: string[];
|
||||
attributes: string[];
|
||||
keywords: string[];
|
||||
unique: boolean;
|
||||
|
@ -33,11 +36,10 @@ export interface CardRequestProps {
|
|||
_totalCount: number;
|
||||
}
|
||||
|
||||
|
||||
export interface CardState {
|
||||
cards: CardProps[];
|
||||
next: string | null;
|
||||
maxCards: number;
|
||||
}
|
||||
|
||||
export type CardStateHandler = [CardState, Function];
|
||||
|
||||
|
|
70
yarn.lock
70
yarn.lock
|
@ -1166,6 +1166,40 @@
|
|||
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
|
||||
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
|
||||
|
||||
"@egjs/component@^2.1.2":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@egjs/component/-/component-2.1.2.tgz#c466a0a6fc6ba2d479814dcbe6ee4643c25a2e5b"
|
||||
integrity sha512-7tnPiqxbSZ0porzlm0+/O3qZdanMj0zOq0sb17wQXuaRG49XKKKJaO+SacGnZDqf308N5hzJ0m9fZ4+j+VBvXA==
|
||||
|
||||
"@egjs/infinitegrid@^3.6.3":
|
||||
version "3.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@egjs/infinitegrid/-/infinitegrid-3.6.3.tgz#f12f60b4d7d983a6cc0371735df69991101618cc"
|
||||
integrity sha512-GELg3eeOjIvk6DkUB61sab2eyV8yJHB2npR7mHmp0rjGf3nJoEBR6A1FqjIrAtT+NjseBxTP2EqYRyI9CwhrIA==
|
||||
dependencies:
|
||||
"@egjs/component" "^2.1.2"
|
||||
"@egjs/lazyloaded" "0.0.2"
|
||||
"@egjs/list-differ" "^1.0.0"
|
||||
|
||||
"@egjs/lazyloaded@0.0.2":
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@egjs/lazyloaded/-/lazyloaded-0.0.2.tgz#1658a2188239e1c5e9dee71734830bcca6625388"
|
||||
integrity sha512-UWgJCDHxsEP/sF+ztwl8F79rP1RHmlwG5ur0fFUT6RgaqKN6aQTnuYm9G/MQB48mWpw/tsY80Ha7xhvhryl+FA==
|
||||
dependencies:
|
||||
"@egjs/component" "^2.1.2"
|
||||
|
||||
"@egjs/list-differ@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@egjs/list-differ/-/list-differ-1.0.0.tgz#2277aff52e3e4bd9318d5c30ffc3ba3b6216f05e"
|
||||
integrity sha512-HsbMKc0ZAQH+EUeCmI/2PvTYSybmkaWwakU8QGDYYgMVIg9BQ5sM0A0Nnombjxo2+JzXHxmH+jw//yGX+y6GYw==
|
||||
|
||||
"@egjs/react-infinitegrid@^3.0.5":
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@egjs/react-infinitegrid/-/react-infinitegrid-3.0.5.tgz#be3759b219c67d7f8e487a2857ccd7d453949b26"
|
||||
integrity sha512-Lf90xlCuThKeg9JfZEd9gdn4a5WgHaL/DmsiS/qWe3dUs7XmEolvfh3tzCP6j8APt6ezQsblr0q/DpUnG/7xkg==
|
||||
dependencies:
|
||||
"@egjs/infinitegrid" "^3.6.3"
|
||||
"@egjs/list-differ" "^1.0.0"
|
||||
|
||||
"@hapi/address@2.x.x":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
|
@ -1671,6 +1705,13 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-modal@^3.10.6":
|
||||
version "3.10.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-modal/-/react-modal-3.10.6.tgz#76717220f32bc72769190692147814a540053c7e"
|
||||
integrity sha512-XpshhwVYir1TRZ2HS5EfmNotJjB8UEC2IkT3omNtiQzROOXSzVLz5xsjwEpACP8U+PctkpfZepX+WT5oDf0a9g==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^16.9.49":
|
||||
version "16.9.49"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.49.tgz#09db021cf8089aba0cdb12a49f8021a69cce4872"
|
||||
|
@ -4528,6 +4569,11 @@ execa@^1.0.0:
|
|||
signal-exit "^3.0.0"
|
||||
strip-eof "^1.0.0"
|
||||
|
||||
exenv@^1.2.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
|
||||
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
|
||||
|
||||
exit@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
|
||||
|
@ -8856,7 +8902,7 @@ prompts@^2.0.1:
|
|||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.4"
|
||||
|
||||
prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
|
@ -9082,6 +9128,21 @@ react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4:
|
|||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-lifecycles-compat@^3.0.0:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
|
||||
react-modal@^3.11.2:
|
||||
version "3.11.2"
|
||||
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.11.2.tgz#bad911976d4add31aa30dba8a41d11e21c4ac8a4"
|
||||
integrity sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==
|
||||
dependencies:
|
||||
exenv "^1.2.0"
|
||||
prop-types "^15.5.10"
|
||||
react-lifecycles-compat "^3.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-scripts@3.4.3:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.4.3.tgz#21de5eb93de41ee92cd0b85b0e1298d0bb2e6c51"
|
||||
|
@ -10990,6 +11051,13 @@ walker@^1.0.7, walker@~1.0.5:
|
|||
dependencies:
|
||||
makeerror "1.0.x"
|
||||
|
||||
warning@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
|
||||
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
watchpack-chokidar2@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0"
|
||||
|
|
Loading…
Reference in New Issue