chg: usr: add filter to the Profile page's recent plays and an infite list too #7
This commit is contained in:
@@ -427,10 +427,88 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.profile-games__filter-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-games__filter-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(149, 207, 245, 0.4);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-games__filter {
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(255, 255, 255, 0.025);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 9px 14px 9px 36px;
|
||||||
|
font: 500 13px 'Rajdhani', sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: border-color 200ms ease, background 200ms ease;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
background: rgba(255, 255, 255, 0.045);
|
||||||
|
border-color: rgba(35, 111, 135, 0.55);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.profile-games {
|
.profile-games {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
|
&.is-filtering + .profile-games__load-more {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-filtering .profile-game--hidden:not(.profile-game--filtered-out) {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game--filtered-out {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__load-more {
|
||||||
|
align-self: center;
|
||||||
|
margin-top: 14px;
|
||||||
|
background: rgba(35, 111, 135, 0.12);
|
||||||
|
color: rgba(149, 207, 245, 0.75);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.3);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 9px 20px;
|
||||||
|
font: 600 12px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: background 200ms ease, border-color 200ms ease, color 200ms ease;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(35, 111, 135, 0.22);
|
||||||
|
border-color: rgba(35, 111, 135, 0.55);
|
||||||
|
color: rgba(149, 207, 245, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-game {
|
.profile-game {
|
||||||
@@ -469,6 +547,10 @@
|
|||||||
border-left-color: rgba(255, 193, 7, 0.4);
|
border-left-color: rgba(255, 193, 7, 0.4);
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-game__badge {
|
.profile-game__badge {
|
||||||
|
|||||||
@@ -29,3 +29,28 @@ if (battleRoot) {
|
|||||||
<BattleDialog games={JSON.parse(battleRoot.dataset.games)} />,
|
<BattleDialog games={JSON.parse(battleRoot.dataset.games)} />,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const list = document.querySelector('.profile-games');
|
||||||
|
const loadMoreBtn = document.querySelector('[data-load-more]');
|
||||||
|
if (list && loadMoreBtn) {
|
||||||
|
const batchSize = parseInt(list.dataset.batchSize, 10) || 5;
|
||||||
|
loadMoreBtn.addEventListener('click', () => {
|
||||||
|
const hidden = list.querySelectorAll('.profile-game--hidden');
|
||||||
|
Array.from(hidden).slice(0, batchSize).forEach((el) => el.classList.remove('profile-game--hidden'));
|
||||||
|
if (list.querySelectorAll('.profile-game--hidden').length === 0) {
|
||||||
|
loadMoreBtn.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterInput = document.querySelector('[data-filter]');
|
||||||
|
if (list && filterInput) {
|
||||||
|
filterInput.addEventListener('input', () => {
|
||||||
|
const term = filterInput.value.trim().toLowerCase();
|
||||||
|
list.classList.toggle('is-filtering', term.length > 0);
|
||||||
|
list.querySelectorAll('.profile-game').forEach((card) => {
|
||||||
|
const opp = card.querySelector('.profile-game__opponent')?.textContent.trim().toLowerCase() ?? '';
|
||||||
|
card.classList.toggle('profile-game--filtered-out', term.length > 0 && !opp.includes(term));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class ProfileController extends AbstractController
|
|||||||
'blindHits' => $bonus['totalBlindHits'],
|
'blindHits' => $bonus['totalBlindHits'],
|
||||||
'edgeMines' => $bonus['totalEdgeMines'],
|
'edgeMines' => $bonus['totalEdgeMines'],
|
||||||
],
|
],
|
||||||
'recent' => ($recent = $this->repo->findRecentFinishedForUser($user)),
|
'recent' => ($recent = $this->repo->findRecentFinishedForUser($user, 30)),
|
||||||
'gamesData' => array_map(function (PlayedGame $game) use ($userId, $cacheManager): array {
|
'gamesData' => array_map(function (PlayedGame $game) use ($userId, $cacheManager): array {
|
||||||
$isRed = $game->getRed()?->getId() === $userId;
|
$isRed = $game->getRed()?->getId() === $userId;
|
||||||
$resign = $game->getResign();
|
$resign = $game->getResign();
|
||||||
|
|||||||
@@ -120,7 +120,12 @@
|
|||||||
<h2 class="profile-section__title">
|
<h2 class="profile-section__title">
|
||||||
<i class="fas fa-clock-rotate-left"></i> Recent battles
|
<i class="fas fa-clock-rotate-left"></i> Recent battles
|
||||||
</h2>
|
</h2>
|
||||||
<div class="profile-games">
|
<div class="profile-games__filter-wrap">
|
||||||
|
<i class="fas fa-search profile-games__filter-icon"></i>
|
||||||
|
<input type="text" class="profile-games__filter" data-filter
|
||||||
|
placeholder="Filter by opponent…" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="profile-games" data-batch-size="5">
|
||||||
{% for game in recent %}
|
{% for game in recent %}
|
||||||
{% set is_red = game.red and game.red.id == app.user.id %}
|
{% set is_red = game.red and game.red.id == app.user.id %}
|
||||||
{% set my_points = is_red ? game.redPoints : game.bluePoints %}
|
{% set my_points = is_red ? game.redPoints : game.bluePoints %}
|
||||||
@@ -149,7 +154,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="profile-game profile-game--{{ result }}{% if not is_finished and not is_anonymous %} profile-game--ongoing{% elseif is_anonymous %} profile-game--abandoned{% endif %}" data-game-index="{{ loop.index0 }}">
|
<div class="profile-game profile-game--{{ result }}{% if not is_finished and not is_anonymous %} profile-game--ongoing{% elseif is_anonymous %} profile-game--abandoned{% endif %}{% if loop.index0 >= 5 %} profile-game--hidden{% endif %}" data-game-index="{{ loop.index0 }}">
|
||||||
<span class="profile-game__badge">
|
<span class="profile-game__badge">
|
||||||
{% if is_finished %}
|
{% if is_finished %}
|
||||||
{{ result == 'win' ? 'Win' : (result == 'loss' ? 'Loss' : 'Draw') }}
|
{{ result == 'win' ? 'Win' : (result == 'loss' ? 'Loss' : 'Draw') }}
|
||||||
@@ -181,6 +186,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
{% if recent|length > 5 %}
|
||||||
|
<button type="button" class="profile-games__load-more" data-load-more>
|
||||||
|
<i class="fas fa-chevron-down"></i> Load more
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="profile-empty">
|
<div class="profile-empty">
|
||||||
|
|||||||
Reference in New Issue
Block a user