applet rows: 3-col grid <title> | <body> | <ts> mirroring post.html's .post-line shape — _my_posts_applet_item / _my_buds_applet_item / _my_notes_item / _my_scrolls_item / _my_games_item all gain a .applet-list-entry.row-3col w. <a class="row-title"> (clickable, 35c/32+... server-side truncated via new lyric_extras.truncate_title filter) + <span class="row-body"> (most-recent activity excerpt, dimmed 0.6 opacity, CSS-text-overflow: ellipsis clipped to whatever space remains — no server-side trunc here so the full line lives in the DOM for inspectors) + <time class="row-ts"> (relative_ts formatted, same minmax(3rem,auto) rightward column allocation post.html's .post-line-time uses, font-size 0.75rem + opacity 0.5 + right-aligned + nowrap); SCSS grid minmax(4rem,auto) 1fr minmax(3rem,auto) lifted from .post-line's template so the timestamp column lines up across post.html / scroll.html / every applet list; per-applet data shapes — _recent_posts annotates each Post w. latest_line (Line FK ordered by -id, None for empty Note-unlock posts); _recent_buds select_related('to_user__active_title') warms the bud's donned-Note FK in one query for the buds row body ("the {{ bud.active_title_display }}" + "since {{ bud.active_title.earned_at|relative_ts }}" — the "since " prefix is unique to this row since the ts is "when they donned it", not the row's own creation); _recent_notes attaches description from _NOTE_META per slug; annotate_latest_event(rooms) helper added to apps.epic.utils (next to rooms_for_user) — attaches room.latest_event per Room w. one .events.order_by('-timestamp').first() per item, used by _billboard_context for my_rooms (My Scrolls applet) AND by apps.gameboard.views.gameboard + toggle_game_applets for my_games (My Games applet), keeping the My Scrolls + My Games shapes symmetric; _billboard_context.my_rooms = annotate_latest_event(...) swaps rooms_for_user(...).order_by("-created_at") materialisation point — bud row's "no active title" branch silently drops body + ts cells so unrecognised buds still surface but don't fabricate a "since None" line; new truncate_title filter is the existing _truncate_post_title view helper hoisted into the template namespace (literal ... past 35 chars, None-safe); 5 ITs in BillboardViewTest cover row content / row absence on missing activity / "since" prefix uniquely on the buds row + 1 in GameboardViewTest for My Games row event prose; deferred row-prose body content cap on <span class="row-body"> purely to CSS text-overflow: ellipsis per user's "middle col should take up the remaining space" steer (initial pass also server-side trunc'd the body to 35c; removed) — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,25 @@ class GameboardViewTest(TestCase):
|
||||
game_items = self.parsed.cssselect("#id_applet_my_games .game-item")
|
||||
self.assertEqual(len(game_items), 0)
|
||||
|
||||
def test_my_games_row_shows_latest_event_prose_and_ts(self):
|
||||
from apps.drama.models import GameEvent, record
|
||||
room = Room.objects.create(name="StampedRoom", owner=self.user)
|
||||
record(
|
||||
room, GameEvent.SLOT_FILLED, actor=self.user,
|
||||
slot_number=1, token_type="coin",
|
||||
token_display="Coin-on-a-String", renewal_days=7,
|
||||
)
|
||||
response = self.client.get("/gameboard/")
|
||||
body = response.content.decode()
|
||||
self.assertIn("StampedRoom", body)
|
||||
# Row carries a ts cell from the recorded event
|
||||
self.assertRegex(
|
||||
body,
|
||||
r'#id_applet_my_games|class="[^"]*row-ts'.replace("#", ""),
|
||||
)
|
||||
# A .row-body cell carries some event prose
|
||||
self.assertRegex(body, r'<time[^>]+class="[^"]*row-ts')
|
||||
|
||||
def test_game_kit_has_coin_on_a_string(self):
|
||||
[_] = self.parsed.cssselect("#id_game_kit #id_kit_coin_on_a_string")
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ def _annotate_deck_in_use(decks, user):
|
||||
deck.in_use_room_name = active.get(deck.pk)
|
||||
return decks
|
||||
from apps.epic.models import DeckVariant, Room, TableSeat
|
||||
from apps.epic.utils import rooms_for_user
|
||||
from apps.epic.utils import annotate_latest_event, rooms_for_user
|
||||
from apps.lyric.models import Token
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ def gameboard(request):
|
||||
"free_count": len(free_tokens),
|
||||
"applets": applet_context(request.user, "gameboard"),
|
||||
"page_class": "page-gameboard",
|
||||
"my_games": rooms_for_user(request.user),
|
||||
"my_games": annotate_latest_event(rooms_for_user(request.user)),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -71,7 +71,7 @@ def toggle_game_applets(request):
|
||||
"deck_variants": _annotate_deck_in_use(list(request.user.unlocked_decks.all()), request.user),
|
||||
"free_tokens": free_tokens,
|
||||
"free_count": len(free_tokens),
|
||||
"my_games": rooms_for_user(request.user),
|
||||
"my_games": annotate_latest_event(rooms_for_user(request.user)),
|
||||
})
|
||||
return redirect("gameboard")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user