From 2992f5b941e741cdfe6a475bf054a4dae76017eb Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Fri, 2 Oct 2026 07:51:10 +0000 Subject: [PATCH] The whole-India poster ships on an India tab (66x66 by default); its list, packet and cards stay withheld countrySheet.js WITHHELD now says WHAT of a whole country is withheld: { IND: { poster: false, rows: true } }, with posterWithheld() and rowsWithheld() (sheetWithheld() keeps meaning the poster, the name Poster Design reads). - India's whole-country POSTER prints again, 66x66 by default (60x96 and 36x60 on request); largeSheet() names it. - Its ROWS (the prayer list, packet and cards: about a thousand pages) stay withheld with the same sentence, in the wall, the Regions list and the 'country' poster type. India's state posters are unchanged. - Country Posters gains an INDIA tab: the same wall filtered to India, labelled with India's own name in the reader's language (Intl), between the posters and the map. check:country-sheet-one-path arm G checks the split (poster live at 66x66, rows withheld, state cards never withheld, both callers read the right half), with poisons for un-withheld rows and a wall that prints them; check:poster-section arm C follows. The by-state, leaderless India layout follows as its own PR. --- .../poster/types/countryPosterType.js | 8 ++-- library/packet/countrySheet.js | 16 +++++--- .../country-posters/country-poster-wall.vue | 6 +-- .../country-posters/country-posters-mfe.vue | 7 +++- .../scripts/check-country-sheet-one-path.mjs | 40 +++++++++---------- .../scripts/check-poster-section.mjs | 8 ++-- .../regions-dashboard/regions-list.vue | 6 +-- 7 files changed, 50 insertions(+), 41 deletions(-) diff --git a/library/components/poster/types/countryPosterType.js b/library/components/poster/types/countryPosterType.js index 5c613bff8..bbe72d2b5 100644 --- a/library/components/poster/types/countryPosterType.js +++ b/library/components/poster/types/countryPosterType.js @@ -8,7 +8,7 @@ * a country with no region is 'other'. */ import data from '@map/data/country-posters.json' -import { makeCountrySheet, sheetWithheld } from '@map/packet/countrySheet.js' +import { makeCountrySheet, largeSheet, posterWithheld, rowsWithheld } from '@map/packet/countrySheet.js' import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js' import { keyedError } from '@map/i18n/keyedError.js' import { countryName } from '@map/utils/countryName.js' @@ -38,11 +38,11 @@ export default { const sheet = makeCountrySheet({ records: recordsFor, assetsBase, countries: data.countries }) const byId = new Map(data.countries.map((c) => [c.id, c])) return { - // a withheld whole-country poster withholds its list, packet and cards too (countrySheet.js WITHHELD) - records: (c) => (sheetWithheld(c) ? Promise.reject(keyedError('messages.countryPosterTooMany', TOO_MANY)) : recordsFor({ type: 'country', values: [c.iso3] })), + // a whole country's withheld rows (countrySheet.js WITHHELD: India's list, packet and cards) say so, unfetched + records: (c) => (rowsWithheld(c) ? Promise.reject(keyedError('messages.countryPosterTooMany', TOO_MANY)) : recordsFor({ type: 'country', values: [c.iso3] })), // the sheet gets the data's own record (English, the series index by id), as on the wall sheet: (card, format) => sheet(byId.get(card.id) || card, format), - unsupported: (c) => (sheetWithheld(c) || c.unsupportedReason ? t('messages.countryPosterTooMany') : null), + unsupported: (c) => (posterWithheld(c) || (c.unsupportedReason && !largeSheet(c)) ? t('messages.countryPosterTooMany') : null), unit: 'UPG', itemCountKey: 'regions.countryCount', eventName: 'doxa-country-posters:show-on-map', downloadPrefix: 'doxa-country', cardStocks: true, } }, diff --git a/library/packet/countrySheet.js b/library/packet/countrySheet.js index 73ae1d46e..b7432e9e7 100644 --- a/library/packet/countrySheet.js +++ b/library/packet/countrySheet.js @@ -79,7 +79,7 @@ export function seriesFor(countries, item) { * packet and cards are offered on a desktop or laptop only (a phone cannot draw a sheet that big). * The data (country-posters.json) still records why no PAPER sheet fits; this table is what prints instead. */ -export const LARGE_SHEETS = { IND: { format: '60x96', formats: ['60x96', '66x66', '36x60'], desktopOnly: true } } +export const LARGE_SHEETS = { IND: { format: '66x66', formats: ['66x66', '60x96', '36x60'], desktopOnly: true } } const largeOf = (item) => (item && LARGE_SHEETS[String(item.iso3 || '').toUpperCase()]) || null /** * ★ WITHHELD: a whole-country poster that is NOT shipped yet, though the renderer can draw it. India's 855 groups do @@ -89,9 +89,15 @@ const largeOf = (item) => (item && LARGE_SHEETS[String(item.iso3 || '').toUpperC * packet and cards with the same sentence. India's STATE and union-territory posters are separate cards * (`indiaState`) and are not affected. check:country-sheet-one-path fails if India's poster comes back unannounced. */ -export const WITHHELD = new Set(['IND']) -export function sheetWithheld(item) { return Boolean(item && !item.indiaState && WITHHELD.has(String(item.iso3 || '').toUpperCase())) } -export function largeSheet(item) { return sheetWithheld(item) ? null : largeOf(item) } +// WHAT of a whole country is withheld: its POSTER, and/or its ROWS (its prayer list, packet and cards). India's +// whole-country poster ships (66x66 by default); its list, packet and cards (about a thousand pages) stay withheld. +export const WITHHELD = { IND: { poster: false, rows: true } } +const withheldOf = (item) => (item && !item.indiaState && WITHHELD[String(item.iso3 || '').toUpperCase()]) || null +export function posterWithheld(item) { return Boolean(withheldOf(item)?.poster) } +export function rowsWithheld(item) { return Boolean(withheldOf(item)?.rows) } +/** The poster's withholding (the name Poster Design reads). */ +export function sheetWithheld(item) { return posterWithheld(item) } +export function largeSheet(item) { return posterWithheld(item) ? null : largeOf(item) } // The sheet format a request gets: a large-sheet country prints at the large size ASKED FOR when it has one (66x66, // 36x60), else at its default (60x96); a packet's page 1 stays a page; every other country gets exactly what it asked. export function sheetFormatFor(item, format) { @@ -115,7 +121,7 @@ export function makeCountrySheet({ records, assetsBase, countries = null }) { return async function countrySheet(item, format) { const iso3 = item && item.iso3 if (!iso3) return '' // a selection or a row without a country: no sheet - if (sheetWithheld(item)) return '' // withheld (above): nothing is fetched or drawn + if (posterWithheld(item)) return '' // a withheld poster (above): nothing is fetched or drawn // Lazy, like the zone sheet: the generator and the boundary loader are only pulled when a // reader actually prints, so they stay out of every wall's initial download. const { generateCountrySheet } = await import('@map/packet/posterSheets.js') diff --git a/plugin-designs/country-posters/country-poster-wall.vue b/plugin-designs/country-posters/country-poster-wall.vue index b86b94c6c..77617bbbc 100644 --- a/plugin-designs/country-posters/country-poster-wall.vue +++ b/plugin-designs/country-posters/country-poster-wall.vue @@ -44,7 +44,7 @@ import { getRuntimeLocale, detectLocale, tStatic } from '@map/i18n/index.js' // library/. Generated by library/scripts/posters/emit-countries.mjs, which now writes there. import data from '@map/data/country-posters.json' import { resolveAssetsBase } from '@map/utils/embedBase.js' -import { makeCountrySheet, largeSheet, sheetWithheld } from '@map/packet/countrySheet.js' +import { makeCountrySheet, largeSheet, posterWithheld, rowsWithheld } from '@map/packet/countrySheet.js' import { isPhonePrint } from '@map/composables/printInPlace.js' import { keyedError } from '@map/i18n/keyedError.js' // India's states and union territories: the shared table (fetched when the wall shows them) and their sheet. @@ -170,7 +170,7 @@ const desktopOnly = () => Promise.reject(keyedError('poster.phoneLimitAll', 'Thi // A WITHHELD whole-country poster (countrySheet.js WITHHELD: India) withholds its list, packet and cards too, with // the same sentence the card shows (about a thousand pages, offered when the poster is). const withheld = () => Promise.reject(keyedError('messages.countryPosterTooMany', 'Too many people groups to fit on one poster legibly — a solution is in progress.')) -const countryRecords = (c) => (sheetWithheld(c) ? withheld() : phoneBlocked(c) ? desktopOnly() : c.indiaState +const countryRecords = (c) => (rowsWithheld(c) ? withheld() : phoneBlocked(c) ? desktopOnly() : c.indiaState ? (c.slugs.length ? recordsFor({ type: 'pg_slugs', values: c.slugs }) : Promise.resolve([])) : recordsFor({ type: 'country', values: [c.iso3] })) // ★ GENERATED AT THE CLICK, through the ONE shared path (@map/packet/countrySheet.js, which the Regions @@ -244,7 +244,7 @@ const collapsedBy = (sec) => sec.total < 60 // unsupportedReason is an English note (and a flag), not display text. The card already states the // count above it, so the sentence needs no number and no plural agreement. // A requested country with no poster at all says so in regions-list.vue's words (the regions catalogue). -const unsupported = (c) => (c.noGroups ? t('countryPosters.indiaStateNoGroups') : c.noPoster ? tRegions('No poster has been generated for this country yet.') : sheetWithheld(c) ? t('messages.countryPosterTooMany') : phoneBlocked(c) ? t('poster.phoneLimitAll') : c.unsupportedReason && !largeSheet(c) ? t('messages.countryPosterTooMany') : null) +const unsupported = (c) => (c.noGroups ? t('countryPosters.indiaStateNoGroups') : c.noPoster ? tRegions('No poster has been generated for this country yet.') : posterWithheld(c) ? t('messages.countryPosterTooMany') : phoneBlocked(c) ? t('poster.phoneLimitAll') : c.unsupportedReason && !largeSheet(c) ? t('messages.countryPosterTooMany') : null) // ★ THROUGH THE CATALOGUE, like the UPG-100 wall: the heading, the tag, the intro and the counts read // in the page's language. The intro keeps its one emphasis as three plain keys (A, the emphasis, B) diff --git a/plugin-designs/country-posters/country-posters-mfe.vue b/plugin-designs/country-posters/country-posters-mfe.vue index ce0b2922c..edccaa7e4 100644 --- a/plugin-designs/country-posters/country-posters-mfe.vue +++ b/plugin-designs/country-posters/country-posters-mfe.vue @@ -34,6 +34,8 @@ import { ref, computed, inject, onMounted, onBeforeUnmount } from 'vue' import { buildPaneColumn, PANE_GRID_STYLES } from '@map/layouts/pane-wrapper/pane-wrapper.js' import { useShadowStyles } from '@map/composables/useShadowStyles.js' import ViewSelect from '@map/components/ViewSelect.vue' +import { countryName } from '@map/utils/countryName.js' +import { getRuntimeLocale, detectLocale } from '@map/i18n/index.js' import ShareButton from '@map/components/map-controls/ShareButton.vue' import { useI18n } from 'vue-i18n' import enTabs from '@map/i18n/locales/en/tabs.json' @@ -68,7 +70,10 @@ const spec = computed(() => ({ // strip desynchronises from the scroll column. panes: [ { id: 'wall', label: t('tabs.countryPosters'), at: [0, 0], config: { profile: 'country-poster-wall' } }, - { id: 'map', label: t('tabs.map'), at: [1, 0], config: { profile: 'countries-map' } }, + // INDIA: the whole-India poster (66x66 by default) on its own tab, the same wall filtered to India. Its label is + // India's own name in the reader's language (Intl), so no tab key is needed. + { id: 'india', label: countryName('IND', getRuntimeLocale() || detectLocale() || 'en') || 'India', at: [1, 0], config: { profile: 'country-poster-wall', countries: ['IND'] } }, + { id: 'map', label: t('tabs.map'), at: [2, 0], config: { profile: 'countries-map' } }, ], // The switcher lives in this element's own header, so the layout's "… below ↓" // pill would be a second, redundant control (same call upg-100-mfe makes). diff --git a/plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs b/plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs index d67c42897..2be3d764d 100644 --- a/plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs +++ b/plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs @@ -99,10 +99,10 @@ export function armB(seriesFor, countries, sheetFormatFor = null) { const by = (iso) => countries.find((c) => c.iso3 === iso) const ind = by('IND') const s = ind && seriesFor(countries, ind) - // THE LARGE-SHEET FORMAT RULE: India prints the large size ASKED FOR, else its 60x96 default; a packet page stays a + // THE LARGE-SHEET FORMAT RULE: India prints the large size ASKED FOR, else its 66x66 default; a packet page stays a // page; any other country gets exactly what it asked for. if (sheetFormatFor) { - const want = [[ind, '66x66', '66x66'], [ind, '36x60', '36x60'], [ind, '60x96', '60x96'], [ind, 'letter', '60x96'], [ind, '3x4', '60x96'], [ind, 'packet', 'packet'], + const want = [[ind, '66x66', '66x66'], [ind, '36x60', '36x60'], [ind, '60x96', '60x96'], [ind, 'letter', '66x66'], [ind, '3x4', '66x66'], [ind, 'packet', 'packet'], [by('NPL'), '3x3', '3x3'], [by('NPL'), 'letter', 'letter'], [by('CHN'), '3x4', '3x4']] for (const [item, ask, exp] of want) { const got = sheetFormatFor(item, ask); if (got !== exp) fails.push(`sheetFormatFor(${item?.iso3}, ${ask}) = ${got}, expected ${exp}`) } } @@ -179,26 +179,24 @@ export function armF(srcs) { // ARM E: India is its OWN section, FIRST, counted ONCE. The India country card, the state cards and the none-listed // cards group together; the section's total is India's own count (not the card sum, which counts 855 twice); on that // true total it outranks every regional section. PosterWall's default (no sectionTotal) must still be the card sum. -// ARM G — A WITHHELD WHOLE-COUNTRY POSTER stays withheld (India: not shipped until it prints legibly). The library -// draws NOTHING for it in any format and fetches no record (0 big jobs); a state card is never withheld; and each -// caller shows messages.countryPosterTooMany on the card and rejects the country's rows with it (no list/packet/cards). +// ARM G — WHAT OF A WHOLE COUNTRY IS WITHHELD (countrySheet.js WITHHELD). India: its whole-country POSTER ships (66x66 +// by default; largeSheet() names it), its ROWS (list, packet, cards: about a thousand pages) stay withheld with the +// sentence; a state card is never withheld. Each caller shows the sentence on the card only for a withheld POSTER and +// rejects the rows only for withheld ROWS. export async function armG(cs, srcs) { const fails = [] - if (!(cs.WITHHELD instanceof Set) || !cs.WITHHELD.has('IND')) fails.push("countrySheet.js WITHHELD no longer holds India: its whole-country poster would ship") - let fetched = 0 - const sheet = cs.makeCountrySheet({ records: () => { fetched++; return Promise.resolve([{ name: 'x', latitude: 20, longitude: 78 }]) }, assetsBase: () => '', countries: [] }) - for (const f of ['3x3', '3x4', 'letter', 'packet', '60x96', '66x66', '36x60']) { - let out - try { out = await sheet({ id: 'country-ind', iso3: 'IND', label: 'India' }, f) } catch (e) { out = 'THREW ' + e.message } - if (out !== '') fails.push(`makeCountrySheet drew something for withheld India at ${f}`) - } - if (fetched) fails.push(`makeCountrySheet fetched records ${fetched}x for withheld India (a big job started)`) - if (cs.sheetWithheld({ iso3: 'IND', indiaState: true, id: 'india-state-gujarat' })) fails.push('an India STATE card is withheld (only the whole-country poster may be)') - if (cs.largeSheet({ iso3: 'IND' })) fails.push('largeSheet(India) is not null while withheld, so callers would offer its poster') + const ind = { id: 'country-ind', iso3: 'IND', label: 'India' } + if (!cs.WITHHELD || !cs.WITHHELD.IND) fails.push('countrySheet.js WITHHELD no longer lists India') + if (cs.posterWithheld(ind)) fails.push("India's whole-country poster is withheld again (it ships)") + if (!cs.rowsWithheld(ind)) fails.push("India's rows are no longer withheld: its list/packet/cards (~1,000 pages) would print") + const big = cs.largeSheet(ind) + if (!big || big.format !== '66x66') fails.push(`India's whole-country poster is not offered at 66x66 by default (${big && big.format})`) + if (cs.sheetWithheld(ind) !== cs.posterWithheld(ind)) fails.push('sheetWithheld() no longer means the poster') + if (cs.rowsWithheld({ iso3: 'IND', indiaState: true, id: 'india-state-gujarat' })) fails.push('an India STATE card has withheld rows (only the whole country may)') for (const [name, src] of Object.entries(srcs)) { - if (!/sheetWithheld\((?:c|item)\) \? t(?:Key)?\('messages\.countryPosterTooMany'\)/.test(src)) fails.push(`${name}: a withheld country's card does not show messages.countryPosterTooMany`) - if (!/sheetWithheld\((?:c|item)\) \? (?:withheld\(\)|Promise\.reject\(keyedError\('messages\.countryPosterTooMany')/.test(src)) fails.push(`${name}: a withheld country's rows are not withheld (its list/packet/cards would print)`) - if (/const withheld = /.test(src) && !/const withheld = \(\) => Promise\.reject\(keyedError\('messages\.countryPosterTooMany'/.test(src)) fails.push(`${name}: withheld() does not reject with messages.countryPosterTooMany`) + if (!/posterWithheld\((?:c|item)\) \? t(?:Key)?\('messages\.countryPosterTooMany'\)/.test(src)) fails.push(`${name}: a withheld POSTER's card does not show messages.countryPosterTooMany`) + if (!/rowsWithheld\((?:c|item)\) \? (?:withheld\(\)|Promise\.reject\(keyedError\('messages\.countryPosterTooMany')/.test(src)) fails.push(`${name}: withheld ROWS are not withheld (its list/packet/cards would print)`) + if (/sheetWithheld\(/.test(src)) fails.push(`${name}: still reads sheetWithheld() (ambiguous now: poster or rows?)`) } return fails } @@ -261,8 +259,8 @@ async function main() { ['the Regions list resolves [] for a phone-blocked card', () => armF({ wall: files[wall], regions: REG.replace("phoneBlocked(item) ? Promise.reject(keyedError('poster.phoneLimitAll'", "phoneBlocked(item) ? Promise.resolve([]) || (keyedError('poster.phoneLimitAll'") })], ['a locale loses a state name', () => armD(states, { ...areas, ro: { ...areas.ro, stateNames: Object.fromEntries(Object.entries(areas.ro.stateNames).filter(([k]) => k !== 'IN-GJ')) } }, meta)], ['the names lose their licence line', () => armD(states, areas, { ...meta, licence: '' })], - ['the whole-India poster un-withheld', async () => armG({ ...CS, WITHHELD: new Set(), largeSheet: (it) => CS.LARGE_SHEETS[it?.iso3] || null, makeCountrySheet: (o) => async (it, f) => { await o.records({}); return '' } }, { wall: files[wall], regions: REG })], - ['the wall prints the list of a withheld country', async () => armG(CS, { wall: files[wall].replace("const countryRecords = (c) => (sheetWithheld(c) ? withheld() : ", "const countryRecords = (c) => ("), regions: REG })], + ['India\'s rows un-withheld', async () => armG({ ...CS, rowsWithheld: () => false }, { wall: files[wall], regions: REG })], + ['the wall prints the list of a country whose rows are withheld', async () => armG(CS, { wall: files[wall].replace('rowsWithheld(c) ? withheld() : ', ''), regions: REG })], ['a helper that mutates the shared ADM1', () => armC({ ...H, stateGeometry: (z, a, n) => { (a.features || []).forEach((f) => { if (f.properties.shapeName === n) f.properties.listId = z.id }); return H.stateGeometry(z, a, n) } }, states, JSON.parse(JSON.stringify(adm1Ind)))], ] let bad = 0 diff --git a/plugin-designs/country-posters/scripts/check-poster-section.mjs b/plugin-designs/country-posters/scripts/check-poster-section.mjs index 5139f03d5..7e70647d7 100644 --- a/plugin-designs/country-posters/scripts/check-poster-section.mjs +++ b/plugin-designs/country-posters/scripts/check-poster-section.mjs @@ -3,8 +3,8 @@ // A. the TYPE REGISTRY: 'country' is registered, an unknown type is null, a plugin can register its own. // B. SCOPES: DX Life's /regions slugs (africa … oceania, other) each resolve; every spelling of a region (its // slug, its API value, its English name) resolves to the same countries; the scopes cover all countries once. -// C. a WITHHELD whole-country poster stays withheld through the type (the card's sentence; its rows rejected, -// no records fetched). +// C. India through the type: its whole-country POSTER card is live; its withheld ROWS are rejected with the sentence, +// no records fetched. // D. the ELEMENT's attributes map to its config (collapsible on by default, collapsible="false", collapsed). // E. THE LIFT: every name PosterSection.vue's template uses is injected from the wall (a section never reads // something its wall does not provide). @@ -43,7 +43,7 @@ async function arms({ types, country, element, sectionSrc }) { else { let fetched = 0 const act = country.actions({ assetsBase: () => '', apiBase: () => { fetched++; return '' }, lang: () => 'en', profileConfig: null, t: (k) => k }) - if (act.unsupported(ind) !== 'messages.countryPosterTooMany') fails.push("C: withheld India's card does not show messages.countryPosterTooMany") + if (act.unsupported(ind) !== null) fails.push("C: India's whole-country poster card is marked unsupported (its poster ships)") let why = '' try { await act.records(ind); why = 'resolved' } catch (e) { why = e && (e.i18nKey || e.message) } if (!/countryPosterTooMany/.test(String(why))) fails.push(`C: withheld India's rows are not withheld (${why})`) @@ -93,7 +93,7 @@ try { } else { const f = await arms(real) if (f.length) { f.forEach((x) => console.log('FAIL ' + x)); code = 1 } - else console.log(`PASS check:poster-section — the 'country' type; ${DXL.length} DX Life scopes cover every country once (and their other spellings); withheld India stays withheld; the element's attributes; PosterSection injects every name it uses`) + else console.log(`PASS check:poster-section — the 'country' type; ${DXL.length} DX Life scopes cover every country once (and their other spellings); India's poster live, its rows withheld; the element's attributes; PosterSection injects every name it uses`) } } finally { await server.close() } process.exit(code) diff --git a/plugin-designs/regions-dashboard/regions-list.vue b/plugin-designs/regions-dashboard/regions-list.vue index 11b5c104e..b6e660a57 100644 --- a/plugin-designs/regions-dashboard/regions-list.vue +++ b/plugin-designs/regions-dashboard/regions-list.vue @@ -70,7 +70,7 @@ import PosterWall from '@map/components/poster/PosterWall.vue' import RegionSectionCard from '@map/components/regions/RegionSectionCard.vue' import posterData from '@map/data/country-posters.json' import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js' -import { makeCountrySheet, largeSheet, sheetWithheld } from '@map/packet/countrySheet.js' +import { makeCountrySheet, largeSheet, posterWithheld, rowsWithheld } from '@map/packet/countrySheet.js' import { isPhonePrint } from '@map/composables/printInPlace.js' import { keyedError } from '@map/i18n/keyedError.js' import { resolveAssetsBase } from '@map/utils/embedBase.js' @@ -373,11 +373,11 @@ function postersFor(region) { // its card says so and offers no rows, rather than failing (the Country Posters wall applies the same rule, same table). const ON_PHONE = isPhonePrint() const phoneBlocked = (c) => ON_PHONE && Boolean(largeSheet(c)?.desktopOnly) -const posterUnsupported = (c) => (sheetWithheld(c) ? tKey('messages.countryPosterTooMany') : phoneBlocked(c) ? tKey('poster.phoneLimitAll') +const posterUnsupported = (c) => (posterWithheld(c) ? tKey('messages.countryPosterTooMany') : phoneBlocked(c) ? tKey('poster.phoneLimitAll') : c.unsupportedReason && !largeSheet(c) ? tKey('messages.countryPosterTooMany') : null) // A blocked card's rows REJECT with the notice, never [] ("no people groups" on a card that has hundreds). // A withheld whole-country poster (countrySheet.js WITHHELD: India) withholds its list, packet and cards too. -const posterRows = (item) => (sheetWithheld(item) ? Promise.reject(keyedError('messages.countryPosterTooMany', 'Too many people groups to fit on one poster legibly — a solution is in progress.')) : phoneBlocked(item) ? Promise.reject(keyedError('poster.phoneLimitAll', 'This needs a desktop or laptop computer.')) : countryRecords({ type: 'country', values: [item.iso3] })) +const posterRows = (item) => (rowsWithheld(item) ? Promise.reject(keyedError('messages.countryPosterTooMany', 'Too many people groups to fit on one poster legibly — a solution is in progress.')) : phoneBlocked(item) ? Promise.reject(keyedError('poster.phoneLimitAll', 'This needs a desktop or laptop computer.')) : countryRecords({ type: 'country', values: [item.iso3] })) // One region's wall is already inside a region section, so it needs no second level of // collapsing and no grouping: everything in it is that one region.