From 467d61f7d16d4788456b2127d96c50e85afe04e3 Mon Sep 17 00:00:00 2001 From: distributive Date: Mon, 12 Sep 2022 18:02:15 +0100 Subject: [PATCH 01/22] Started new general purpose js file --- web/js/api.js | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 web/js/api.js diff --git a/web/js/api.js b/web/js/api.js new file mode 100644 index 00000000..dab5aed4 --- /dev/null +++ b/web/js/api.js @@ -0,0 +1,59 @@ +// Fetches all cards from the v2 API +async function fetchCards() { + data = []; + json = await fetch('http://localhost:3000/api/v3/public/cards').then(data => data.json()); + data.push(...json.data); + while ('next' in json.links) { + json = await fetch(json.links.next).then(data => data.json()); + data.push(...json.data); + } + return data; +} + +// Remove all cards with any of the given subtypes from a list of cards +function removeCardsWithSubtypes(cards, subtypes) { + if (!subtypes || subtypes.length == 0) + return cards; + return cards.filter(c => subtypes.some(s => !(c.attributes.display_subtypes && c.attributes.display_subtypes.toLowerCase().split(' - ').includes(s)))); +} + +// Searches a list of restrictions for the one with the given ID +function getRestrictionById(restrictions, id) { + for (i = 0; i < restrictions.length; i++) { + if (restrictions[i].id == id) { + return restrictions[i]; + } + } + return null; +} + +// Filters a list of cards for the one with the given ID +function getCardsById(cards, ids) { + return cards.filter(card => ids.includes(card.id)); +} + +// Maps a _=>card_id object to a _=>card object +function getCardsByIdFromObj(cards, obj) { + return Object.keys(obj).reduce(function(newObj, key) { + newObj[key] = getCardsById(cards, obj[key]) + return newObj + }, {}); +} + +// Splits a list of cards into corp cards and runner cards +function splitBySide(cards) { + corp = []; + runner = []; + cards.forEach (card => { + if (card.attributes.side_id == 'corp') + corp.push(card); + else + runner.push(card); + }); + return [corp, runner]; +} + +// Generates a link to a card by creating a link to its most recent printing +function cardToLink(card) { + return Routing.generate('cards_zoom', {card_code:card.attributes.latest_printing_id}); +} From cc70d83f9b41ad48b71347f829d3f5b818a32068 Mon Sep 17 00:00:00 2001 From: distributive Date: Mon, 12 Sep 2022 18:02:43 +0100 Subject: [PATCH 02/22] Updated banlist page for v2 api --- .../views/Banlists/banlists.html.twig | 225 ++++++++++++------ web/css/style.css | 11 + 2 files changed, 161 insertions(+), 75 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index cd43236a..84e685c7 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -3,87 +3,162 @@ {% block title %}Ban Lists{% endblock %} {% block head %} - + {% endblock %} {% block body %} -
- +

{{ block('title') }}

+
+

There are currently three official formats supported by NISEI: Startup, Standard, and Eternal. This page displays the ban lists for each.

+

Explanation

+
    +
  • Banned: You cannot include any copies of a banned card in your deck.
  • +
  • Points: Including any number of copies of a card with points in Eternal adds points to your deck. Eternal decks must have 7 points or fewer.
  • +
  • Restricted (deprecated): You may include up to a full playset of only one restricted card.
  • +
  • Universal Influence (deprecated): Cards with universal influence cost additional influence to include in a deck.
  • +
  • Identity Influence Reduction (deprecated): These cards reduce your identity's influence limit by 1 for each copy (to a minimum of 1).
  • +
+

See NISEI's Supported Formats page for more information.

+
-

Startup Format

-
-
-
-

No cards are currently banned in Startup. Have a blast!

-
+
+ +
+ +
+ +
+
+
+
- -

Standard Format

-
- {% for banlist in banlists %} -
-
-

{{ banlist.name }} {% if banlist.active %}(active){% endif %}

-

{{ banlist.num_cards }} cards. Start Date {{ banlist.start_date|date("Y-m-d") }}

-
-
- -
-
-

Corp Cards

-
    - {% for verdict in banlist.cards|keys|sort %} -
  • {{ verdict }} -
      - {% if verdict == 'Banned' and banlist.all_currents_banned %} -
    • All cards with Current subtype.
    • - {% endif %} - {% for card in banlist.cards[verdict]|sort((a, b) => a.card.title <=> b.card.title) %} - {% if card.card.side.code == 'corp' %} - {% if banlist.all_currents_banned %} - {% if "Current" not in card.card.keywords %} -
    • {{ card.card.title }}
    • - {% endif %} - {% else %} -
    • {{ card.card.title }}
    • - {% endif %} - {% endif %} - {% endfor %} -
    -
  • - {% endfor %} -
-
- -
-

Runner Cards

-
    - {% for verdict in banlist.cards|keys|sort %} -
  • {{ verdict }} -
      - {% if verdict == 'Banned' and banlist.all_currents_banned %} -
    • All cards with Current subtype.
    • - {% endif %} - {% for card in banlist.cards[verdict]|sort((a, b) => a.card.title <=> b.card.title) %} - {% if card.card.side.code == 'runner' %} - {% if banlist.all_currents_banned %} - {% if "Current" not in card.card.keywords %} -
    • {{ card.card.title }}
    • - {% endif %} - {% else %} -
    • {{ card.card.title }}
    • - {% endif %} - {% endif %} - {% endfor %} -
    -
  • - {% endfor %} -
-
-
- {% endfor %} -
+ + + {% endblock %} diff --git a/web/css/style.css b/web/css/style.css index d7c84d46..f83a4b04 100755 --- a/web/css/style.css +++ b/web/css/style.css @@ -712,6 +712,17 @@ ul.rulings-list blockquote { list-style-type: '- '; } + + +/* Corner buttons */ +.button-tr { + position: absolute; + top: 8px; + right: 8px; +} + + + /******** fix for twitter typehead + bootstrap input-group-btn ***********/ .input-group .twitter-typeahead { From 4bf66cf240e4e6a9060d7bdb16675f1d32697ec9 Mon Sep 17 00:00:00 2001 From: distributive Date: Mon, 12 Sep 2022 18:08:20 +0100 Subject: [PATCH 03/22] The banlist query (b:...) now accepts the inverse operand for showing only affected cards --- src/AppBundle/Service/CardsData.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AppBundle/Service/CardsData.php b/src/AppBundle/Service/CardsData.php index ef8b6b78..4134ba32 100755 --- a/src/AppBundle/Service/CardsData.php +++ b/src/AppBundle/Service/CardsData.php @@ -708,8 +708,8 @@ public function get_search_rows(array $conditions, string $sortorder, string $lo $mwl = $this->entityManager->getRepository(Mwl::class)->findOneBy(['code' => $condition[0]], ["dateStart" => "DESC"]); } if ($mwl) { - // Exclude any cards banned by this ban list. - $clauses[] = "(c.id NOT IN (SELECT mc.card_id FROM AppBundle:MwlCard mc WHERE mc.mwl_id = ?$i))"; + $cond = $operator == ":" ? "in" : "not in"; + $clauses[] = "(c.id $cond (SELECT mc.card_id FROM AppBundle:MwlCard mc WHERE mc.mwl_id = ?$i))"; $parameters[$i++] = $mwl->getId(); } $i++; From c6d81ebd02b70d1036a6563e3e75aec3dd42c44c Mon Sep 17 00:00:00 2001 From: distributive Date: Wed, 21 Sep 2022 18:19:38 +0100 Subject: [PATCH 04/22] Passed v3_api_url parameter to the JavaScript files --- app/Resources/views/layout.html.twig | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Resources/views/layout.html.twig b/app/Resources/views/layout.html.twig index 456e85f4..201abbcb 100755 --- a/app/Resources/views/layout.html.twig +++ b/app/Resources/views/layout.html.twig @@ -75,6 +75,7 @@ locale: '{{ app.request.locale }}' }; moment.locale(window.navigator.language); + var v3_api_url = '{{ v3_api_url }}'; {% if app.environment == 'prod' %} From 01a8644a746150e2715ea390881c0a67da134431 Mon Sep 17 00:00:00 2001 From: distributive Date: Wed, 21 Sep 2022 18:20:27 +0100 Subject: [PATCH 05/22] Improvements to layout and code --- .../views/Banlists/banlists.html.twig | 42 ++++++++++++------- web/css/style.css | 9 ---- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index 84e685c7..cc6284f5 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -62,17 +62,17 @@ function restrictionToText(cards, format, restriction) { // Get the affected cards by restriction type v = a.verdicts; - [corp, runner] = splitBySide (cards); - corpBanned = removeCardsWithSubtypes(getCardsById (corp, v.banned), a.banned_subtypes); + [corp, runner] = splitBySide(cards); + corpBanned = removeCardsWithSubtypes(getCardsById(corp, v.banned), a.banned_subtypes); corpRestricted = getCardsById (corp, v.restricted); - corpUniversalFactionCost = getCardsByIdFromObj (corp, v.universal_faction_cost); - corpGlobalPenalty = getCardsByIdFromObj (corp, v.global_penalty); - corpPoints = getCardsByIdFromObj (corp, v.points); - runnerBanned = removeCardsWithSubtypes(getCardsById (runner, v.banned), a.banned_subtypes); - runnerRestricted = getCardsById (runner, v.restricted); - runnerUniversalFactionCost = getCardsByIdFromObj (runner, v.universal_faction_cost); - runnerGlobalPenalty = getCardsByIdFromObj (runner, v.global_penalty); - runnerPoints = getCardsByIdFromObj (runner, v.points); + corpUniversalFactionCost = getCardsByIdFromObj(corp, v.universal_faction_cost); + corpGlobalPenalty = getCardsByIdFromObj(corp, v.global_penalty); + corpPoints = getCardsByIdFromObj(corp, v.points); + runnerBanned = removeCardsWithSubtypes(getCardsById(runner, v.banned), a.banned_subtypes); + runnerRestricted = getCardsById(runner, v.restricted); + runnerUniversalFactionCost = getCardsByIdFromObj(runner, v.universal_faction_cost); + runnerGlobalPenalty = getCardsByIdFromObj(runner, v.global_penalty); + runnerPoints = getCardsByIdFromObj(runner, v.points); // Generate corp restrictions corpBody = '

Corp Cards

    '; @@ -106,7 +106,10 @@ function restrictionToText(cards, format, restriction) { // Output search = Routing.generate('cards_find', {type:'find', 'view':'list', 'q':`b!${code.replaceAll('_', '-')}`}); - header = `

    ${name}${active ? " (active)" : ""}

    `; + header = `
    ` + + `

    ${name}${active ? " (active)" : ""}

    ` + + `` + + `
    `; subheader = `
    ${count} cards. Start Date: ${date_start}.
    `; body = `
    ${corpBody + runnerBody}
    `; return `
    ${header + subheader + body}
    `; @@ -120,11 +123,11 @@ async function buildBanlistsView() { // Load data from API const desiredFormats = ['startup', 'standard', 'eternal']; - const formats = await Promise.all(desiredFormats.map(f => fetch(`http://localhost:3000/api/v3/public/formats/${f}`).then(data => data.json()).then(json => json.data))) + const formats = await Promise.all(desiredFormats.map(f => fetch(`{{ v3_api_url }}/api/v3/public/formats/${f}`).then(data => data.json()).then(json => json.data))) const cards = await fetchCards(); - const rData = await fetch('http://localhost:3000/api/v3/public/restrictions').then(data => data.json()); + const rData = await fetch('{{ v3_api_url }}/api/v3/public/restrictions').then(data => data.json()); const restrictions = await Promise.all(formats.map(f => - fetch(`http://localhost:3000/api/v3/public/snapshots?filter[format_id]=${f.id}`) + fetch(`{{ v3_api_url }}/api/v3/public/snapshots?filter[format_id]=${f.id}`) .then(data => data.json()) .then(json => json.data.sort((a,b) => a.attributes.date_start < b.attributes.date_start) .map(snapshot => snapshot.attributes.restriction_id) @@ -139,7 +142,8 @@ async function buildBanlistsView() { formats.forEach((format, i) => { rs = restrictions[i].length == 0 ? `

    No cards are currently banned in ${format.attributes.name}. Have a blast!

    ` - : restrictions[i].map(r => restrictionToText (cards, format, r)).join(''); + : '

    ' + + restrictions[i].map(r => restrictionToText (cards, format, r)).join(''); $(`#tab-pane-${format.id}`).append( `
    ${rs}
    ` ); @@ -155,6 +159,14 @@ async function buildBanlistsView() { $(this).closest('.panel').children().show(250); } }); + $('#show-all').on('click', function (event) { + $('.panel').children().show(250); + $('.list-toggle').html('Hide'); + }); + $('#hide-all').on('click', function (event) { + $('.panel').children(':not(:first-child)').hide(250); + $('.list-toggle').html('Show'); + }); } // Create the banlists view on load diff --git a/web/css/style.css b/web/css/style.css index f83a4b04..8fd4c9f9 100755 --- a/web/css/style.css +++ b/web/css/style.css @@ -714,15 +714,6 @@ ul.rulings-list blockquote { -/* Corner buttons */ -.button-tr { - position: absolute; - top: 8px; - right: 8px; -} - - - /******** fix for twitter typehead + bootstrap input-group-btn ***********/ .input-group .twitter-typeahead { From 6be3f8aba074a3ba32a2fd7f173d6336d153d024 Mon Sep 17 00:00:00 2001 From: distributive Date: Wed, 21 Sep 2022 18:20:40 +0100 Subject: [PATCH 06/22] Used new v3_api_url parameter --- web/js/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/js/api.js b/web/js/api.js index dab5aed4..a5010b8d 100644 --- a/web/js/api.js +++ b/web/js/api.js @@ -1,7 +1,7 @@ // Fetches all cards from the v2 API async function fetchCards() { data = []; - json = await fetch('http://localhost:3000/api/v3/public/cards').then(data => data.json()); + json = await fetch(`${v3_api_url}/api/v3/public/cards`).then(data => data.json()); data.push(...json.data); while ('next' in json.links) { json = await fetch(json.links.next).then(data => data.json()); From 46d6a883904a09a1973cbe10535ca4db40610972 Mon Sep 17 00:00:00 2001 From: distributive Date: Wed, 21 Sep 2022 20:59:22 +0100 Subject: [PATCH 07/22] Made all pages load the helper api.js file automatically --- app/Resources/views/Banlists/banlists.html.twig | 4 ---- app/Resources/views/layout.html.twig | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index cc6284f5..90242a70 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -2,10 +2,6 @@ {% block title %}Ban Lists{% endblock %} -{% block head %} - -{% endblock %} - {% block body %}

    {{ block('title') }}

    diff --git a/app/Resources/views/layout.html.twig b/app/Resources/views/layout.html.twig index 201abbcb..9c389a3f 100755 --- a/app/Resources/views/layout.html.twig +++ b/app/Resources/views/layout.html.twig @@ -88,6 +88,7 @@ gtag('config', 'UA-131671930-1'); {% endif %} + From 2fa0969baa8c2be066bfdefaa5336418cd974893 Mon Sep 17 00:00:00 2001 From: distributive Date: Wed, 21 Sep 2022 22:05:04 +0100 Subject: [PATCH 08/22] Removed obsolete server code for banlists page --- .../Controller/BanlistsController.php | 71 ------------------- 1 file changed, 71 deletions(-) diff --git a/src/AppBundle/Controller/BanlistsController.php b/src/AppBundle/Controller/BanlistsController.php index 70203c82..01c6bfbd 100755 --- a/src/AppBundle/Controller/BanlistsController.php +++ b/src/AppBundle/Controller/BanlistsController.php @@ -12,79 +12,8 @@ class BanlistsController extends Controller */ public function getAction(EntityManagerInterface $entityManager) { - $mwls = $entityManager->getRepository('AppBundle:Mwl')->findBy([], ['dateStart' => 'DESC']); - $banlists = array(); - - // TODO(plural): Do some refactoring later for other places that need similar things and Move this to the CardData service. - $q = $entityManager->createQuery("select c from AppBundle:Card c where c.id IN (SELECT DISTINCT mc.card_id FROM AppBundle:MwlCard mc) ORDER BY c.code DESC"); - $cards = $q->getResult(); - $code_to_title = array(); - $title_to_most_recent_printing = array(); - $unique_cards = array(); - foreach ($cards as $card) { - $code_to_title[$card->getCode()] = $card->getTitle(); - if (!array_key_exists($card->getTitle(), $title_to_most_recent_printing)) { - // We haven't seen this card yet, so add it. We can do this simply since we are sorting the cards in reverse chronological order. - $title_to_most_recent_printing[$card->getTitle()] = $card->getCode(); - $unique_cards[$card->getCode()] = $card; - } - } - - // Until the MWL entity has some more metadata, keep track of this here. - $mwl_codes_all_currents_banned = [ - 'standard-ban-list-20-06' => true, - 'standard-ban-list-20-09' => true, - 'standard-ban-list-21-04' => true, - 'standard-ban-list-21-05' => true, - 'standard-ban-list-21-06' => true, - 'standard-ban-list-21-10' => true, - ]; - - foreach ($mwls as $mwl) { - $x = array(); - $x['name'] = $mwl->getName(); - $x['active'] = $mwl->getActive(); - $x['code'] = $mwl->getCode(); - $x['start_date'] = $mwl->getDateStart(); - $x['mwl_object_delete'] = $mwl; - $x['all_currents_banned'] = array_key_exists($mwl->getCode(), $mwl_codes_all_currents_banned); - $mwl_cards = $mwl->getCards(); - krsort($mwl_cards); - $x['cards'] = array(); - - $num_cards = 0; - foreach ($mwl_cards as $code => $mwl_entry) { - // Use the MWL/Ban List verdict as the key for the array of cards with that same verdict. - // This simplifies the template logic to have these grouped in the controller. - $verdict = ''; - if (array_key_exists('deck_limit', $mwl_entry)) { - $verdict = 'Banned'; - } elseif (array_key_exists('is_restricted', $mwl_entry)) { - $verdict = 'Restricted'; - } elseif (array_key_exists('global_penalty', $mwl_entry)) { - $verdict = 'Identity Influence Reduction'; - } elseif (array_key_exists('universal_faction_cost', $mwl_entry)) { - $verdict = '+' . $mwl_entry['universal_faction_cost'] . ' Universal Influence'; - } - if (!array_key_exists($verdict, $x['cards'])) { - $x['cards'][$verdict] = array(); - } - // Only add the most recent printing for each card. - $card = $unique_cards[$title_to_most_recent_printing[$code_to_title[$code]]]; - $x['cards'][$verdict][$card->getCode()]['card'] = $card; - $x['cards'][$verdict][$card->getCode()]['banlist_entry'] = $mwl_entry; - ++$num_cards; - } - // Keep track of the number of cards here so the template doesn't have to walk all the maps to count the cards. - $x['num_cards'] = $num_cards; - $banlists[] = $x; - } - return $this->render('/Banlists/banlists.html.twig', [ 'pagetitle' => "Ban Lists", - 'banlists' => $banlists, - 'unique_cards' => $unique_cards, - 'mwl_codes_all_currents_banned' => $mwl_codes_all_currents_banned, ]); } } From 3618ea53f04bec1b35b585a6a52489d7895172a5 Mon Sep 17 00:00:00 2001 From: distributive Date: Thu, 22 Sep 2022 05:24:43 +0100 Subject: [PATCH 09/22] Fixed show/hide all buttons --- .../views/Banlists/banlists.html.twig | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index 90242a70..27f93427 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -138,10 +138,10 @@ async function buildBanlistsView() { formats.forEach((format, i) => { rs = restrictions[i].length == 0 ? `

    No cards are currently banned in ${format.attributes.name}. Have a blast!

    ` - : '

    ' + + : '

    ' + restrictions[i].map(r => restrictionToText (cards, format, r)).join(''); $(`#tab-pane-${format.id}`).append( - `
    ${rs}
    ` + `
    ${rs}
    ` ); }); @@ -155,13 +155,15 @@ async function buildBanlistsView() { $(this).closest('.panel').children().show(250); } }); - $('#show-all').on('click', function (event) { - $('.panel').children().show(250); - $('.list-toggle').html('Hide'); + $('.show-all').on('click', function (event) { + let panels = $(this).closest('.list'); + panels.find('.panel').children().show(250); + panels.find('.list-toggle').html('Hide'); }); - $('#hide-all').on('click', function (event) { - $('.panel').children(':not(:first-child)').hide(250); - $('.list-toggle').html('Show'); + $('.hide-all').on('click', function (event) { + let panels = $(this).closest('.list'); + panels.find('.panel').children(':not(:first-child)').hide(250); + panels.find('.list-toggle').html('Show'); }); } From dc4e7546f99d9c58f3970efa2b27734d74efea53 Mon Sep 17 00:00:00 2001 From: distributive Date: Thu, 22 Sep 2022 05:25:59 +0100 Subject: [PATCH 10/22] Semicolon --- app/Resources/views/Banlists/banlists.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index 27f93427..d5818493 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -119,7 +119,7 @@ async function buildBanlistsView() { // Load data from API const desiredFormats = ['startup', 'standard', 'eternal']; - const formats = await Promise.all(desiredFormats.map(f => fetch(`{{ v3_api_url }}/api/v3/public/formats/${f}`).then(data => data.json()).then(json => json.data))) + const formats = await Promise.all(desiredFormats.map(f => fetch(`{{ v3_api_url }}/api/v3/public/formats/${f}`).then(data => data.json()).then(json => json.data))); const cards = await fetchCards(); const rData = await fetch('{{ v3_api_url }}/api/v3/public/restrictions').then(data => data.json()); const restrictions = await Promise.all(formats.map(f => From f22eedc75ba9670f8653a2c202863926cfbfcb07 Mon Sep 17 00:00:00 2001 From: distributive Date: Thu, 22 Sep 2022 05:51:11 +0100 Subject: [PATCH 11/22] Removed empty div --- app/Resources/views/Banlists/banlists.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index d5818493..eeb022cf 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -107,7 +107,7 @@ function restrictionToText(cards, format, restriction) { `` + `
    `; subheader = `
    ${count} cards. Start Date: ${date_start}.
    `; - body = `
    ${corpBody + runnerBody}
    `; + body = `
    ${corpBody + runnerBody}
    `; return `
    ${header + subheader + body}
    `; } From 33a04f8f18c48f5e3a0f2f864b7762543eb1a03a Mon Sep 17 00:00:00 2001 From: distributive Date: Thu, 22 Sep 2022 09:04:23 +0100 Subject: [PATCH 12/22] Removed extra line --- web/css/style.css | 1 - 1 file changed, 1 deletion(-) diff --git a/web/css/style.css b/web/css/style.css index 8fd4c9f9..09a7d749 100755 --- a/web/css/style.css +++ b/web/css/style.css @@ -713,7 +713,6 @@ ul.rulings-list blockquote { } - /******** fix for twitter typehead + bootstrap input-group-btn ***********/ .input-group .twitter-typeahead { From 9abc1ffb38d0402b83ec438453d4e393ac40b53f Mon Sep 17 00:00:00 2001 From: distributive Date: Sat, 24 Sep 2022 19:44:00 +0100 Subject: [PATCH 13/22] General improvements to banlist page --- .../views/Banlists/banlists.html.twig | 347 +++++++++--------- web/js/api.js | 49 ++- 2 files changed, 202 insertions(+), 194 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index eeb022cf..bef36832 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -1,174 +1,173 @@ -{% extends '/layout.html.twig' %} - -{% block title %}Ban Lists{% endblock %} - -{% block body %} -
    -

    {{ block('title') }}

    -
    -

    There are currently three official formats supported by NISEI: Startup, Standard, and Eternal. This page displays the ban lists for each.

    -

    Explanation

    -
      -
    • Banned: You cannot include any copies of a banned card in your deck.
    • -
    • Points: Including any number of copies of a card with points in Eternal adds points to your deck. Eternal decks must have 7 points or fewer.
    • -
    • Restricted (deprecated): You may include up to a full playset of only one restricted card.
    • -
    • Universal Influence (deprecated): Cards with universal influence cost additional influence to include in a deck.
    • -
    • Identity Influence Reduction (deprecated): These cards reduce your identity's influence limit by 1 for each copy (to a minimum of 1).
    • -
    -

    See NISEI's Supported Formats page for more information.

    -
    - -
    - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    - - - -{% endblock %} +{% extends '/layout.html.twig' %} + +{% block title %}Ban Lists{% endblock %} + +{% block body %} +
    +

    {{ block('title') }}

    +
    +

    There are currently three official formats supported by NISEI: Startup, Standard, and Eternal. This page displays the ban lists for each.

    +

    Explanation

    +
      +
    • Banned: You cannot include any copies of a banned card in your deck.
    • +
    • Points: Including any number of copies of a card with points in Eternal adds points to your deck. Eternal decks must have 7 points or fewer.
    • +
    • Restricted (deprecated): You may include up to a full playset of only one restricted card.
    • +
    • Universal Influence (deprecated): Cards with universal influence cost additional influence to include in a deck.
    • +
    • Identity Influence Reduction (deprecated): These cards reduce your identity's influence limit by 1 for each copy (to a minimum of 1).
    • +
    +

    See NISEI's Supported Formats page for more information.

    +
    + +
    + +
    + +
    + +
    +
    +
    +
    +
    +
    +
    + + + +{% endblock %} diff --git a/web/js/api.js b/web/js/api.js index a5010b8d..8f3b1993 100644 --- a/web/js/api.js +++ b/web/js/api.js @@ -1,7 +1,18 @@ -// Fetches all cards from the v2 API -async function fetchCards() { - data = []; - json = await fetch(`${v3_api_url}/api/v3/public/cards`).then(data => data.json()); +// Fetches the json from an api url +async function fetchJson(url) { + return await fetch(url).then(data => data.json()); +} + +// Fetches the data field from a url (not accounting for pagination) +async function fetchData(url) { + return await fetchJson(url).then(json => json.data); +} + +// Fetches all cards from the v2 API (accounting for pagination) +// Returns a list of cards +async function fetchCards(flags='') { + const data = []; + let json = await fetch(`${v3_api_url}/api/v3/public/cards/${flags}`).then(data => data.json()); data.push(...json.data); while ('next' in json.links) { json = await fetch(json.links.next).then(data => data.json()); @@ -10,21 +21,19 @@ async function fetchCards() { return data; } -// Remove all cards with any of the given subtypes from a list of cards -function removeCardsWithSubtypes(cards, subtypes) { - if (!subtypes || subtypes.length == 0) - return cards; - return cards.filter(c => subtypes.some(s => !(c.attributes.display_subtypes && c.attributes.display_subtypes.toLowerCase().split(' - ').includes(s)))); +// Removes currents from a list of cards +// Assumes the cards have their list of subtypes exposed +function removeCurrents(cards) { + return cards.filter(c => !c.relationships.card_subtypes.data.map(d => d.id).includes('current')); } -// Searches a list of restrictions for the one with the given ID -function getRestrictionById(restrictions, id) { - for (i = 0; i < restrictions.length; i++) { - if (restrictions[i].id == id) { - return restrictions[i]; - } - } - return null; +// Turns a json object containing a list of objects with an ID into a map from IDs to those objects +function makeIdMap(json) { + const out = new Map(); + json.data.forEach(d => { + out.set(d.id, d); + }); + return out; } // Filters a list of cards for the one with the given ID @@ -42,8 +51,8 @@ function getCardsByIdFromObj(cards, obj) { // Splits a list of cards into corp cards and runner cards function splitBySide(cards) { - corp = []; - runner = []; + const corp = []; + const runner = []; cards.forEach (card => { if (card.attributes.side_id == 'corp') corp.push(card); @@ -54,6 +63,6 @@ function splitBySide(cards) { } // Generates a link to a card by creating a link to its most recent printing -function cardToLink(card) { +function cardToLatestPrintingLink(card) { return Routing.generate('cards_zoom', {card_code:card.attributes.latest_printing_id}); } From 710bccc63776a2271113d44b249bff38708bba29 Mon Sep 17 00:00:00 2001 From: distributive Date: Sat, 24 Sep 2022 23:09:24 +0100 Subject: [PATCH 14/22] Moved api.js file to a twig container --- app/Resources/views/Banlists/banlists.html.twig | 1 + web/js/api.js => app/Resources/views/Scripts/api.html.twig | 4 ++++ 2 files changed, 5 insertions(+) rename web/js/api.js => app/Resources/views/Scripts/api.html.twig (94%) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index bef36832..4f4faba6 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -3,6 +3,7 @@ {% block title %}Ban Lists{% endblock %} {% block body %} +{% include '/Scripts/api.html.twig' %}

    {{ block('title') }}

    diff --git a/web/js/api.js b/app/Resources/views/Scripts/api.html.twig similarity index 94% rename from web/js/api.js rename to app/Resources/views/Scripts/api.html.twig index 8f3b1993..e28fc7fe 100644 --- a/web/js/api.js +++ b/app/Resources/views/Scripts/api.html.twig @@ -1,3 +1,5 @@ + From a8f487ce85308736d7af66e996eed85674f15f3c Mon Sep 17 00:00:00 2001 From: distributive Date: Sun, 25 Sep 2022 00:28:30 +0100 Subject: [PATCH 15/22] jquery optimisations feat. no longer loading the entire cardpool every time --- .../views/Banlists/banlists.html.twig | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index 4f4faba6..6e43da41 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -66,8 +66,6 @@ async function buildBanlistsView() { // Load data from API const desiredFormats = ['startup', 'standard', 'eternal']; const formats = await Promise.all(desiredFormats.map(f => fetch(`{{ v3_api_url }}/api/v3/public/formats/${f}`).then(data => data.json()).then(json => json.data))); - const cards = await fetchCards('?include=card_subtypes'); - const restrictionsById = makeIdMap(await fetch('{{ v3_api_url }}/api/v3/public/restrictions').then(data => data.json())); const restrictions = await Promise.all(formats.map(f => fetchData(f.relationships.restrictions.links.related).then(data => data.sort((a,b) => a.attributes.date_start < b.attributes.date_start)) )); @@ -78,19 +76,21 @@ async function buildBanlistsView() { // Add each format to the page for (const [i, f] of formats.entries()) { $(`#tab-pane-${f.id}`).append(`
    `); + const jqCol = $(`#${f.id}`); if (restrictions[i].length == 0) { - $(`#${f.id}`).append(`

    No cards are currently banned in ${f.attributes.name}. Have a blast!

    `); + jqCol.append(`

    No cards are currently banned in ${f.attributes.name}. Have a blast!

    `); } else { - $(`#${f.id}`).append('

    '); + jqCol.append('

    '); for (const r of restrictions[i]) { // Create panel - $(`#${f.id}`).append(`
    `); + jqCol.append(`
    `); + const jqPanel = $(`#restriction-${r.id}`); // Add header - $(`#restriction-${r.id}`).append(`
    `); - $(`#restriction-${r.id} > .panel-heading`).append(`

    ${r.attributes.name}${(r.id == f.attributes.active_restriction_id) ? " (active)" : ""}

    `); - $(`#restriction-${r.id} > .panel-heading`).append(``); + jqPanel.append(`
    `); + jqPanel.find(`.panel-heading`).append(`

    ${r.attributes.name}${(r.id == f.attributes.active_restriction_id) ? " (active)" : ""}

    `) + .append(``); // Add subheader (search link hyphenated for legacy IDs) - $(`#restriction-${r.id}`).append(`
    ${r.attributes.size} cards. Start Date: ${r.attributes.date_start}.
    `); + jqPanel.append(`
    ${r.attributes.size} cards. Start Date: ${r.attributes.date_start}.
    `); // Get the affected cards by restriction type let cards = await fetchCards(`?include=card_subtypes&filter[search]=in_restriction:true restriction_id:${r.id}`).then(cards => splitBySide(cards)); @@ -106,41 +106,43 @@ async function buildBanlistsView() { const [corpPts, runnerPts] = cards.map(cs => makeCardMap(cs, v.points)); // Add body - $(`#restriction-${r.id}`).append('
    '); + jqPanel.append('
    '); // Generate corp restrictions - $(`#restriction-${r.id} .row`).append(`

    Corp Cards

      `); + jqPanel.find(`.row`).append(`

      Corp Cards

        `); + const jqCorp = $(`#${r.id}-corp`); // Bans (banned subtypes (i.e. currents) are removed beforehand to reduce length) if (corpBan.length > 0) { if (r.attributes.banned_subtypes.length > 0) { // NOTE: currently hardcoded to only be currents const pre = `All cards with the Current subtype.`; - $(`#${r.id}-corp`).append(generateList('Banned', removeCurrents(corpBan), pre)); + jqCorp.append(generateList('Banned', removeCurrents(corpBan), pre)); } else { - $(`#${r.id}-corp`).append(generateList('Banned', corpBan)); + jqCorp.append(generateList('Banned', corpBan)); } } // The others - if (corpRes.length > 0) { $(`#${r.id}-corp`).append(generateList('Restricted', corpRes)); } - Object.keys(corpUFC).sort().reverse().forEach(p => { $(`#${r.id}-corp`).append(generateList(`+${p} Universal Influence`, corpUFC[p])); }); - if (corpPen.length > 0) { $(`#${r.id}-corp`).append(generateList('Identity Influence Reduction', corpPen)); } - Object.keys(corpPts).sort().reverse().forEach(p => { $(`#${r.id}-corp`).append(generateList(`${p} ${p == 1 ? 'Point' : 'Points'}`, corpPts[p])); }); + if (corpRes.length > 0) { jqCorp.append(generateList('Restricted', corpRes)); } + Object.keys(corpUFC).sort().reverse().forEach(p => { jqCorp.append(generateList(`+${p} Universal Influence`, corpUFC[p])); }); + if (corpPen.length > 0) { jqCorp.append(generateList('Identity Influence Reduction', corpPen)); } + Object.keys(corpPts).sort().reverse().forEach(p => { jqCorp.append(generateList(`${p} ${p == 1 ? 'Point' : 'Points'}`, corpPts[p])); }); // Generate runner restrictions $(`#restriction-${r.id} .row`).append(`

        Runner Cards

          `); + const jqRunner = $(`#${r.id}-runner`); // Bans (banned subtypes (i.e. currents) are removed beforehand to reduce length) if (runnerBan.length > 0) { if (r.attributes.banned_subtypes.length > 0) { // NOTE: currently hardcoded to only be currents const pre = `All cards with the Current subtype.`; - $(`#${r.id}-runner`).append(generateList('Banned', removeCurrents(runnerBan), pre)); + jqRunner.append(generateList('Banned', removeCurrents(runnerBan), pre)); } else { - $(`#${r.id}-runner`).append(generateList('Banned', runnerBan)); + jqRunner.append(generateList('Banned', runnerBan)); } } // The others - if (runnerRes.length > 0) { $(`#${r.id}-runner`).append(generateList('Restricted', runnerRes)); } - Object.keys(runnerUFC).sort().reverse().forEach(p => { $(`#${r.id}-runner`).append(generateList(`+${p} Universal Influence`, runnerUFC[p])); }); - if (runnerPen.length > 0) { $(`#${r.id}-runner`).append(generateList('Identity Influence Reduction', runnerPen)); } - Object.keys(runnerPts).sort().reverse().forEach(p => { $(`#${r.id}-runner`).append(generateList(`${p} ${p == 1 ? 'Point' : 'Points'}`, runnerPts[p])); }); + if (runnerRes.length > 0) { jqRunner.append(generateList('Restricted', runnerRes)); } + Object.keys(runnerUFC).sort().reverse().forEach(p => { jqRunner.append(generateList(`+${p} Universal Influence`, runnerUFC[p])); }); + if (runnerPen.length > 0) { jqRunner.append(generateList('Identity Influence Reduction', runnerPen)); } + Object.keys(runnerPts).sort().reverse().forEach(p => { jqRunner.append(generateList(`${p} ${p == 1 ? 'Point' : 'Points'}`, runnerPts[p])); }); } } } From 358bf42239a1091a38137b632fd25b1147909fdc Mon Sep 17 00:00:00 2001 From: distributive Date: Sun, 25 Sep 2022 00:49:55 +0100 Subject: [PATCH 16/22] Allow the user to interact with the text boxes before their content has loaded --- .../views/Banlists/banlists.html.twig | 113 ++++++++++-------- 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index 6e43da41..50c4071c 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -74,6 +74,7 @@ async function buildBanlistsView() { $('.temp-loading').remove(); // Add each format to the page + // Start by adding an empty text box for each restriction for (const [i, f] of formats.entries()) { $(`#tab-pane-${f.id}`).append(`
          `); const jqCol = $(`#${f.id}`); @@ -91,58 +92,6 @@ async function buildBanlistsView() { .append(``); // Add subheader (search link hyphenated for legacy IDs) jqPanel.append(`
          ${r.attributes.size} cards. Start Date: ${r.attributes.date_start}.
          `); - - // Get the affected cards by restriction type - let cards = await fetchCards(`?include=card_subtypes&filter[search]=in_restriction:true restriction_id:${r.id}`).then(cards => splitBySide(cards)); - const v = r.attributes.verdicts; - - // Lists (bans, restricted cards, global penalty cards) - const [corpBan, runnerBan] = cards.map(cs => cs.filter(card => v.banned?.includes(card.id))); - const [corpRes, runnerRes] = cards.map(cs => cs.filter(card => v.restricted?.includes(card.id))); - const [corpPen, runnerPen] = cards.map(cs => cs.filter(card => v.global_penalty?.includes(card.id))); - - // Mappings (points, universal faction costs) - const [corpUFC, runnerUFC] = cards.map(cs => makeCardMap(cs, v.universal_faction_cost)); - const [corpPts, runnerPts] = cards.map(cs => makeCardMap(cs, v.points)); - - // Add body - jqPanel.append('
          '); - - // Generate corp restrictions - jqPanel.find(`.row`).append(`

          Corp Cards

            `); - const jqCorp = $(`#${r.id}-corp`); - // Bans (banned subtypes (i.e. currents) are removed beforehand to reduce length) - if (corpBan.length > 0) { - if (r.attributes.banned_subtypes.length > 0) { // NOTE: currently hardcoded to only be currents - const pre = `All cards with the Current subtype.`; - jqCorp.append(generateList('Banned', removeCurrents(corpBan), pre)); - } else { - jqCorp.append(generateList('Banned', corpBan)); - } - } - // The others - if (corpRes.length > 0) { jqCorp.append(generateList('Restricted', corpRes)); } - Object.keys(corpUFC).sort().reverse().forEach(p => { jqCorp.append(generateList(`+${p} Universal Influence`, corpUFC[p])); }); - if (corpPen.length > 0) { jqCorp.append(generateList('Identity Influence Reduction', corpPen)); } - Object.keys(corpPts).sort().reverse().forEach(p => { jqCorp.append(generateList(`${p} ${p == 1 ? 'Point' : 'Points'}`, corpPts[p])); }); - - // Generate runner restrictions - $(`#restriction-${r.id} .row`).append(`

            Runner Cards

              `); - const jqRunner = $(`#${r.id}-runner`); - // Bans (banned subtypes (i.e. currents) are removed beforehand to reduce length) - if (runnerBan.length > 0) { - if (r.attributes.banned_subtypes.length > 0) { // NOTE: currently hardcoded to only be currents - const pre = `All cards with the Current subtype.`; - jqRunner.append(generateList('Banned', removeCurrents(runnerBan), pre)); - } else { - jqRunner.append(generateList('Banned', runnerBan)); - } - } - // The others - if (runnerRes.length > 0) { jqRunner.append(generateList('Restricted', runnerRes)); } - Object.keys(runnerUFC).sort().reverse().forEach(p => { jqRunner.append(generateList(`+${p} Universal Influence`, runnerUFC[p])); }); - if (runnerPen.length > 0) { jqRunner.append(generateList('Identity Influence Reduction', runnerPen)); } - Object.keys(runnerPts).sort().reverse().forEach(p => { jqRunner.append(generateList(`${p} ${p == 1 ? 'Point' : 'Points'}`, runnerPts[p])); }); } } } @@ -167,6 +116,66 @@ async function buildBanlistsView() { panels.find('.panel').children(':not(:first-child)').hide(); panels.find('.list-toggle').html('Show'); }); + + // Add the restriction data to each text box + for (const format of restrictions) { + for (const r of format) { + // Get the affected cards by restriction type + let cards = await fetchCards(`?include=card_subtypes&filter[search]=in_restriction:true restriction_id:${r.id}`).then(cards => splitBySide(cards)); + const v = r.attributes.verdicts; + + // Lists (bans, restricted cards, global penalty cards) + const [corpBan, runnerBan] = cards.map(cs => cs.filter(card => v.banned?.includes(card.id))); + const [corpRes, runnerRes] = cards.map(cs => cs.filter(card => v.restricted?.includes(card.id))); + const [corpPen, runnerPen] = cards.map(cs => cs.filter(card => v.global_penalty?.includes(card.id))); + + // Mappings (points, universal faction costs) + const [corpUFC, runnerUFC] = cards.map(cs => makeCardMap(cs, v.universal_faction_cost)); + const [corpPts, runnerPts] = cards.map(cs => makeCardMap(cs, v.points)); + + // Get the panel DOM object + const jqPanel = $(`#restriction-${r.id}`); + + // Add body + jqPanel.append(`
              `); + + // Generate corp restrictions + jqPanel.find(`.row`).append(`

              Corp Cards

                `); + const jqCorp = $(`#${r.id}-corp`); + // Bans (banned subtypes (i.e. currents) are removed beforehand to reduce length) + if (corpBan.length > 0) { + if (r.attributes.banned_subtypes.length > 0) { // NOTE: currently hardcoded to only be currents + const pre = `All cards with the Current subtype.`; + jqCorp.append(generateList('Banned', removeCurrents(corpBan), pre)); + } else { + jqCorp.append(generateList('Banned', corpBan)); + } + } + // The others + if (corpRes.length > 0) { jqCorp.append(generateList('Restricted', corpRes)); } + Object.keys(corpUFC).sort().reverse().forEach(p => { jqCorp.append(generateList(`+${p} Universal Influence`, corpUFC[p])); }); + if (corpPen.length > 0) { jqCorp.append(generateList('Identity Influence Reduction', corpPen)); } + Object.keys(corpPts).sort().reverse().forEach(p => { jqCorp.append(generateList(`${p} ${p == 1 ? 'Point' : 'Points'}`, corpPts[p])); }); + + // Generate runner restrictions + jqPanel.find('.row').append(`

                Runner Cards

                  `); + const jqRunner = $(`#${r.id}-runner`); + // Bans (banned subtypes (i.e. currents) are removed beforehand to reduce length) + if (runnerBan.length > 0) { + if (r.attributes.banned_subtypes.length > 0) { // NOTE: currently hardcoded to only be currents + const pre = `All cards with the Current subtype.`; + jqRunner.append(generateList('Banned', removeCurrents(runnerBan), pre)); + } else { + jqRunner.append(generateList('Banned', runnerBan)); + } + } + // The others + if (runnerRes.length > 0) { jqRunner.append(generateList('Restricted', runnerRes)); } + Object.keys(runnerUFC).sort().reverse().forEach(p => { jqRunner.append(generateList(`+${p} Universal Influence`, runnerUFC[p])); }); + if (runnerPen.length > 0) { jqRunner.append(generateList('Identity Influence Reduction', runnerPen)); } + Object.keys(runnerPts).sort().reverse().forEach(p => { jqRunner.append(generateList(`${p} ${p == 1 ? 'Point' : 'Points'}`, runnerPts[p])); }); + } + } } // Create the banlists view on load From 57dc45e33a80b397498cb22bbee83ac3fff9bac8 Mon Sep 17 00:00:00 2001 From: distributive Date: Sun, 25 Sep 2022 09:44:08 +0100 Subject: [PATCH 17/22] Removed obsolete js import --- app/Resources/views/layout.html.twig | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Resources/views/layout.html.twig b/app/Resources/views/layout.html.twig index 9c389a3f..201abbcb 100755 --- a/app/Resources/views/layout.html.twig +++ b/app/Resources/views/layout.html.twig @@ -88,7 +88,6 @@ gtag('config', 'UA-131671930-1'); {% endif %} - From c3367aadc1a9fd544644957211bfd1cdb4abeaa7 Mon Sep 17 00:00:00 2001 From: distributive Date: Sun, 25 Sep 2022 14:04:56 +0100 Subject: [PATCH 18/22] More improvements and significant optimisations --- app/Resources/views/Banlists/banlists.html.twig | 5 ++--- app/Resources/views/Scripts/api.html.twig | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index 50c4071c..afa42867 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -66,8 +66,9 @@ async function buildBanlistsView() { // Load data from API const desiredFormats = ['startup', 'standard', 'eternal']; const formats = await Promise.all(desiredFormats.map(f => fetch(`{{ v3_api_url }}/api/v3/public/formats/${f}`).then(data => data.json()).then(json => json.data))); + const cards = await fetchCards(`?include=card_subtypes&filter[search]=in_restriction:true`, 250).then(cards => splitBySide(cards)); const restrictions = await Promise.all(formats.map(f => - fetchData(f.relationships.restrictions.links.related).then(data => data.sort((a,b) => a.attributes.date_start < b.attributes.date_start)) + fetchData(`{{ v3_api_url }}/api/v3/public/formats/${f.id}/restrictions?sort=-date_start`) )); // Remove the loading indicator @@ -120,8 +121,6 @@ async function buildBanlistsView() { // Add the restriction data to each text box for (const format of restrictions) { for (const r of format) { - // Get the affected cards by restriction type - let cards = await fetchCards(`?include=card_subtypes&filter[search]=in_restriction:true restriction_id:${r.id}`).then(cards => splitBySide(cards)); const v = r.attributes.verdicts; // Lists (bans, restricted cards, global penalty cards) diff --git a/app/Resources/views/Scripts/api.html.twig b/app/Resources/views/Scripts/api.html.twig index e28fc7fe..20568440 100644 --- a/app/Resources/views/Scripts/api.html.twig +++ b/app/Resources/views/Scripts/api.html.twig @@ -12,9 +12,10 @@ async function fetchData(url) { // Fetches all cards from the v2 API (accounting for pagination) // Returns a list of cards -async function fetchCards(flags='') { +async function fetchCards(flags='', pageLimit) { const data = []; - let json = await fetch(`${v3_api_url}/api/v3/public/cards/${flags}`).then(data => data.json()); + pageLimit = (pageLimit != undefined) ? `&page[limit]=${pageLimit}` : ''; + let json = await fetch(`${v3_api_url}/api/v3/public/cards/${flags}${pageLimit}`).then(data => data.json()); data.push(...json.data); while ('next' in json.links) { json = await fetch(json.links.next).then(data => data.json()); From 6055c76c2e2a274dc56f9035a8315d96787f59ec Mon Sep 17 00:00:00 2001 From: distributive Date: Sun, 25 Sep 2022 14:05:39 +0100 Subject: [PATCH 19/22] All entries other than the active and latest restrictions for each format are hidden by default --- app/Resources/views/Banlists/banlists.html.twig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index afa42867..fd760185 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -84,15 +84,17 @@ async function buildBanlistsView() { } else { jqCol.append('

                  '); for (const r of restrictions[i]) { + const active = r.id == f.attributes.active_restriction_id; + const visible = active || r.id == restrictions[i][0].id; // Create panel jqCol.append(`
                  `); const jqPanel = $(`#restriction-${r.id}`); // Add header jqPanel.append(`
                  `); - jqPanel.find(`.panel-heading`).append(`

                  ${r.attributes.name}${(r.id == f.attributes.active_restriction_id) ? " (active)" : ""}

                  `) - .append(``); + jqPanel.find(`.panel-heading`).append(`

                  ${r.attributes.name}${active ? " (active)" : ""}

                  `) + .append(``); // Add subheader (search link hyphenated for legacy IDs) - jqPanel.append(`
                  ${r.attributes.size} cards. Start Date: ${r.attributes.date_start}.
                  `); + jqPanel.append(`
                  ${r.attributes.size} cards. Start Date: ${r.attributes.date_start}.
                  `); } } } From 22a3c19ce6dc3281d9834a9fc21a53a241db9ee9 Mon Sep 17 00:00:00 2001 From: distributive Date: Sun, 25 Sep 2022 20:55:49 +0100 Subject: [PATCH 20/22] Added animated waiting icon --- .../views/Banlists/banlists.html.twig | 4 +- web/css/style.css | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index fd760185..6bd945dd 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -59,9 +59,7 @@ function generateList(header, cards, pre='') { async function buildBanlistsView() { // Add a temporary loading indicator - $('.tab-pane').append( - '

                  Loading...

                  ' - ); + $('.tab-pane').append('

                  Loading...

                  '); // Load data from API const desiredFormats = ['startup', 'standard', 'eternal']; diff --git a/web/css/style.css b/web/css/style.css index 09a7d749..43546707 100755 --- a/web/css/style.css +++ b/web/css/style.css @@ -712,6 +712,52 @@ ul.rulings-list blockquote { list-style-type: '- '; } +/* Loading icon */ +.loading-icon { + display: inline-block; + position: relative; + width: 1em; + height: 1em; + margin-right: 0.75em; +} +.loading-icon * { + display: inline-block; + position: absolute; +} + +.loading-icon-red, .loading-icon-blue, .loading-icon-overlap { + border: 0.2em solid; /* 20% */ + left: 0; + + width: 100%; + height: 100%; + + animation-name: loading-icon; + animation-duration: 2s; + animation-iteration-count: infinite; + animation-fill-mode: both; +} +.loading-icon-red, .loading-icon-overlap { + border-color: red; + transform: translate(-20%, -20%); +} +.loading-icon-blue { + border-color: blue; + transform: translate(20%, 20%); + animation-delay: -1s; +} +.loading-icon-overlap { + border-top: 0; + border-bottom: 0; +} + +@keyframes loading-icon { + 0% {transform: translate(-20%, -20%);} + 25% {transform: translate( 20%, -20%);} + 50% {transform: translate( 20%, 20%);} + 75% {transform: translate(-20%, 20%);} + 100% {transform: translate(-20%, -20%);} +} /******** fix for twitter typehead + bootstrap input-group-btn ***********/ From 5defcc149fb83fc903abf05623c8764398494599 Mon Sep 17 00:00:00 2001 From: distributive Date: Sun, 25 Sep 2022 21:26:14 +0100 Subject: [PATCH 21/22] Provide an error message for browsers with JavaScript disabled --- app/Resources/views/Banlists/banlists.html.twig | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/Resources/views/Banlists/banlists.html.twig b/app/Resources/views/Banlists/banlists.html.twig index 6bd945dd..a2fd53e9 100644 --- a/app/Resources/views/Banlists/banlists.html.twig +++ b/app/Resources/views/Banlists/banlists.html.twig @@ -18,10 +18,9 @@

                See NISEI's Supported Formats page for more information.

                - -
                +
                +
                + +