OASIS_MOBILE/src/views/games_view.js

151 lines
6.6 KiB
JavaScript

const { div, h2, h3, p, section, form, input, button, a, img, table, tr, td, th, span, iframe } = require("../server/node_modules/hyperaxe");
const { template, i18n, userLink} = require('./main_views');
const moment = require("../server/node_modules/moment");
const getGames = () => [
{ id: 'cocoland', title: () => i18n.gamesCocolandTitle, desc: () => i18n.gamesCocolandDesc },
{ id: 'ecoinflow', title: () => i18n.gamesTheFlowTitle, desc: () => i18n.gamesTheFlowDesc },
{ id: 'neoninfiltrator', title: () => i18n.gamesNeonInfiltratorTitle, desc: () => i18n.gamesNeonInfiltratorDesc },
{ id: 'audiopendulum', title: () => i18n.gamesAudioPendulumTitle, desc: () => i18n.gamesAudioPendulumDesc },
{ id: 'spaceinvaders', title: () => i18n.gamesSpaceInvadersTitle, desc: () => i18n.gamesSpaceInvadersDesc },
{ id: 'arkanoid', title: () => i18n.gamesArkanoidTitle, desc: () => i18n.gamesArkanoidDesc },
{ id: 'pingpong', title: () => i18n.gamesPingPongTitle, desc: () => i18n.gamesPingPongDesc },
{ id: 'asteroids', title: () => i18n.gamesAsteroidsTitle, desc: () => i18n.gamesAsteroidsDesc },
{ id: 'rockpaperscissors', title: () => i18n.gamesRockPaperScissorsTitle, desc: () => i18n.gamesRockPaperScissorsDesc },
{ id: 'tiktaktoe', title: () => i18n.gamesTikTakToeTitle, desc: () => i18n.gamesTikTakToeDesc },
{ id: 'flipflop', title: () => i18n.gamesFlipFlopTitle, desc: () => i18n.gamesFlipFlopDesc },
{ id: '8ball', title: () => i18n.games8BallTitle, desc: () => i18n.games8BallDesc },
{ id: 'artillery', title: () => i18n.gamesArtilleryTitle, desc: () => i18n.gamesArtilleryDesc },
{ id: 'labyrinth', title: () => i18n.gamesLabyrinthTitle, desc: () => i18n.gamesLabyrinthDesc },
{ id: 'cocoman', title: () => i18n.gamesCocomanTitle, desc: () => i18n.gamesCocomanDesc },
{ id: 'tetris', title: () => i18n.gamesTetrisTitle, desc: () => i18n.gamesTetrisDesc }
];
const shortId = (feedId) => feedId ? '@' + feedId.slice(1, 9) + '...' : '?';
const renderHallOfFame = (hall) => {
const games = getGames();
const gamesWithScores = games.filter(g => hall[g.id] && hall[g.id].length > 0);
if (gamesWithScores.length === 0) {
return p({ class: 'no-content' }, i18n.gamesNoScores || 'No scores yet.');
}
return div({ class: 'games-scoring-list' },
gamesWithScores.map(game =>
div({ class: 'game-scoring-section' },
div({ class: 'game-scoring-header' },
img({ src: `/game-assets/${game.id}/thumbnail.svg`, alt: game.title(), class: 'game-scoring-thumb', loading: 'lazy' }),
div({ class: 'game-scoring-info' },
h3({ class: 'game-card-title' }, game.title()),
p({ class: 'game-card-desc game-desc-yellow' }, game.desc())
)
),
table({ class: 'hall-of-fame-table' },
tr(th('#'), th(i18n.gamesHallPlayer), th(i18n.gamesHallScore), th(i18n.gamesHallDate || 'Date')),
...hall[game.id].map((entry, idx) =>
tr(
td(String(idx + 1)),
td(userLink(entry.author)),
td({ class: idx === 0 ? 'score-first' : '' }, String(entry.score)),
td(entry.ts ? moment(entry.ts).format('YYYY-MM-DD') : '\u2014')
)
)
)
)
)
);
};
const VALID_GAME_IDS = new Set(['cocoland','ecoinflow','neoninfiltrator','audiopendulum','spaceinvaders','arkanoid','pingpong','asteroids','rockpaperscissors','tiktaktoe','flipflop','8ball','artillery','labyrinth','cocoman','tetris']);
exports.gameShellView = (name) => {
if (!VALID_GAME_IDS.has(name)) {
return template(i18n.gamesTitle, section(p(i18n.notFound || 'Not found')));
}
const game = getGames().find(g => g.id === name);
const filterBar = div({ class: 'filter-group' },
form({ method: 'GET', action: '/games' },
input({ type: 'hidden', name: 'filter', value: 'all' }),
button({ type: 'submit', class: 'filter-btn' }, i18n.gamesFilterAll)
),
form({ method: 'GET', action: '/games' },
input({ type: 'hidden', name: 'filter', value: 'scoring' }),
button({ type: 'submit', class: 'filter-btn' }, i18n.gamesFilterScoring)
)
);
return template(
game ? game.title() : name,
section(
div({ class: 'tags-header' },
h2(i18n.gamesTitle),
p(i18n.gamesDescription || 'Discover and play some mini-games in your network.')
),
filterBar
),
section({ class: 'game-shell-section' },
iframe({
src: `/game-assets/${name}/index.html`,
class: `game-iframe game-iframe-${name}`,
scrolling: 'no',
allowfullscreen: true
})
)
);
};
exports.gamesView = (filter = 'all', hall = null) => {
const games = getGames();
const filterBar = div({ class: 'filter-group' },
form({ method: 'GET', action: '/games' },
input({ type: 'hidden', name: 'filter', value: 'all' }),
button({ type: 'submit', class: filter === 'all' ? 'filter-btn active' : 'filter-btn' },
i18n.gamesFilterAll
)
),
form({ method: 'GET', action: '/games' },
input({ type: 'hidden', name: 'filter', value: 'scoring' }),
button({ type: 'submit', class: filter === 'scoring' ? 'filter-btn active' : 'filter-btn' },
i18n.gamesFilterScoring
)
)
);
const content = filter === 'scoring' && hall
? renderHallOfFame(hall)
: div({ class: 'games-single-col' },
games.map(game => {
const topScore = hall && hall[game.id] && hall[game.id].length > 0 ? hall[game.id][0] : null;
return div({ class: 'game-row' },
div({ class: 'game-row-media' },
img({ src: `/game-assets/${game.id}/thumbnail.svg`, alt: game.title(), loading: 'lazy' })
),
div({ class: 'game-row-body' },
h2({ class: 'game-card-title' }, game.title()),
p({ class: 'game-card-desc game-desc-yellow' }, game.desc()),
topScore
? p({ class: 'game-top-score' },
userLink(topScore.author),
span({ class: 'game-new-record-label' }, ' - ' + (i18n.gamesNewRecord || 'New Record') + ': '),
String(topScore.score)
)
: null
),
div({ class: 'game-row-actions' },
a({ href: `/games/${game.id}`, class: 'filter-btn' }, i18n.gamesPlayButton)
)
);
})
);
return template(
i18n.gamesTitle,
section(
div({ class: 'tags-header' },
h2(i18n.gamesTitle),
p(filter === 'scoring' ? i18n.gamesHallOfFame : (i18n.gamesDescription || 'Discover and play some mini-games in your network.'))
),
filterBar
),
section(content)
);
};