Skip to content

Commit e8616a9

Browse files
committed
- Make the search page mostly work
1 parent 7a0cb2a commit e8616a9

File tree

5 files changed

+118
-16
lines changed

5 files changed

+118
-16
lines changed

kirovy/serializers/cnc_map_serializers.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,24 @@ class CncMapBaseSerializer(CncNetUserOwnedModelSerializer):
151151
default=None,
152152
)
153153

154+
# TODO: These serializer method fields really ought to be sub serializers
155+
# TODO: Make sure queries are optimized in the views for listing maps.
156+
latest_map_file_hash = serializers.SerializerMethodField()
157+
game_slug = serializers.SerializerMethodField()
158+
created_date = serializers.DateTimeField("%Y-%m-%d", source="created")
159+
154160
class Meta:
155161
model = cnc_map.CncMap
156162
# We return the ID instead of the whole object.
157163
exclude = ["cnc_game", "categories", "parent"]
158164
fields = "__all__"
159165

166+
def get_latest_map_file_hash(self, obj: cnc_map.CncMap) -> str:
167+
return obj.cncmapfile_set.order_by("-version").first().hash_sha1
168+
169+
def get_game_slug(self, obj: cnc_map.CncMap) -> str:
170+
return obj.cnc_game.slug
171+
160172
def create(self, validated_data: t.DictStrAny) -> cnc_map.CncMap:
161173
cnc_map_instance = cnc_map.CncMap(**validated_data)
162174
cnc_map_instance.save()

kirovy/templates/legacy_search.html

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,56 @@
77
{% block wrapped_content %}
88
<div class="form-wrapper limit-w-1000">
99
<h2>Search Options</h2>
10-
<form action="/search" method="post" enctype="multipart/form-data">
10+
<form action="/search" method="get" enctype="multipart/form-data">
1111
<div>
1212
<div>
1313
<label for="game-slug">Game</label>
14-
<select id="game-slug" name="game">
15-
<option value="td" selected="selected">Command &amp; Conquer (Tiberian Dawn)</option>
16-
<option value="ra" selected="selected">Red Alert 1</option>
17-
<option value="d2" selected="selected">Dune 2000</option>
18-
<option value="ts" selected="selected">Tiberian Sun</option>
19-
<option value="yr" selected="selected">Yuri's Revenge</option>
14+
<select id="game-slug" name="game_slug" required="required">
15+
<option disabled selected="selected" value=""> -- Select a game -- </option>
16+
<option value="td">Command &amp; Conquer (Tiberian Dawn)</option>
17+
<option value="ra">Red Alert 1</option>
18+
<option value="d2">Dune 2000</option>
19+
<option value="ts">Tiberian Sun</option>
20+
<option value="yr">Yuri's Revenge</option>
2021
</select>
2122
</div>
2223
<div>
23-
<label for="map-name-or-description">Search text</label>
24-
<input type="text" id="map-name-or-description" name="map-name-or-description">
24+
<label for="search">Search text</label>
25+
<input type="text" id="search" name="search" required="required">
2526
</div>
2627
</div>
2728
<button class="full-width-flex-row btn" type="submit">Search</button>
2829
</form>
2930
</div>
31+
<div id="search-results">
32+
<div class="callout-note limit-w-1000" {% if results == '' or results|length == 0 %}style="display: none"{% endif %}>
33+
You can download the map files and place in your game directory,
34+
or you can use the <span class="inline-code">/downloadmap {MAP_HASH}</span> in a CnCNet multiplayer lobby.
35+
</div>
36+
<div class="callout-note limit-w-1000" {% if results is None or results|length > 0 %}style="display: none"{% endif %}>
37+
No maps found. Try altering your search.
38+
</div>
39+
<div class="flex-grid">
40+
{% for cnc_map in results %}
41+
<div class="map-result">
42+
{% autoescape on %}
43+
<div>
44+
{{ cnc_map.created_date }}
45+
</div>
46+
<div>
47+
{{ cnc_map.latest_map_file_hash }}
48+
</div>
49+
<div style="flex-grow: 2; text-align: right">
50+
{{ cnc_map.map_name }}
51+
</div>
52+
<div style="flex-grow: 0">
53+
<a href="/{{ cnc_map.game_slug }}/{{ cnc_map.latest_map_file_hash }}">
54+
<i class="bi bi-floppy btn-smol"></i>
55+
</a>
56+
</div>
57+
{% endautoescape %}
58+
</div>
59+
{% endfor %}
60+
</div>
61+
</div>
3062
{% endblock %}

kirovy/views/base_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class KirovyListCreateView(_g.ListCreateAPIView):
4848
"""
4949

5050
permission_classes = [permissions.CanUpload | permissions.ReadOnly]
51-
pagination_class: t.Type[KirovyDefaultPagination] = KirovyDefaultPagination
51+
pagination_class: t.Optional[t.Type[KirovyDefaultPagination]] = KirovyDefaultPagination
5252
_paginator: t.Optional[KirovyDefaultPagination]
5353
request: KirovyRequest # Added for type hinting. Populated by DRF ``.setup()``
5454

kirovy/views/cnc_map_views.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
CncMap,
1919
CncMapFile,
2020
)
21+
from kirovy.objects.ui_objects import ResultResponseData, ListResponseData, BaseResponseData
2122
from kirovy.request import KirovyRequest
2223
from kirovy.response import KirovyResponse
2324
from kirovy.serializers import cnc_map_serializers
@@ -54,7 +55,7 @@ class MapListFilters(filters.FilterSet):
5455
cnc_game = filters.ModelMultipleChoiceFilter(
5556
field_name="cnc_game__id", to_field_name="id", queryset=CncGame.objects.filter(is_visible=True)
5657
)
57-
# categories = filters.ModelMultipleChoiceFilter(queryset=MapCategory.objects.filter())
58+
game_slug = filters.CharFilter(field_name="cnc_game__slug")
5859

5960
class Meta:
6061
model = CncMap
@@ -154,17 +155,23 @@ def get_queryset(self):
154155
# Prefetch the categories because they're displayed like tags.
155156
# TODO: Since the category list is going to be somewhat small,
156157
# maybe the UI should just cache them and I return IDs instead of objects?
157-
.prefetch_related("categories")
158+
.prefetch_related("categories", "cncmapfile_set")
158159
)
159160
return base_query
160161

161162
filter_backends = [
163+
filters.DjangoFilterBackend, # filter first to reduce the count of rows that we full text search on.
162164
SearchFilter,
163165
OrderingFilter,
164-
filters.DjangoFilterBackend,
165166
]
166167
filterset_class = MapListFilters
167168

169+
search_param = "search"
170+
"""attr: The query param to use in the URL
171+
172+
Searches the fields defined in :attr:`~kirovy.views.cnc_map_views.MapListCreateView`
173+
"""
174+
168175
search_fields = [
169176
"@map_name",
170177
"^description",
@@ -288,3 +295,11 @@ class MapLegacySearchUI(MapListCreateView):
288295
permission_classes = [AllowAny]
289296
renderer_classes = [TemplateHTMLRenderer]
290297
template_name = "legacy_search.html"
298+
pagination_class = None
299+
300+
# TODO: Require filters.
301+
def get(self, request, *args, **kwargs) -> KirovyResponse[ListResponseData | None]:
302+
if not request.query_params.get("game_slug"):
303+
return KirovyResponse[None](status=status.HTTP_200_OK)
304+
response = super().get(request, *args, **kwargs)
305+
return response

static/bc-assets/bc-main.css

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@import url("https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css");
2+
13
body {
24
--bs-body-color: #f1f1f1;
35
--bs-body-bg: #141619;
@@ -111,6 +113,7 @@ form > label {
111113
padding: 1.5rem;
112114
border-radius: 4px;
113115
border: 1px solid #00fe89;
116+
box-sizing: border-box;
114117
}
115118

116119
.form-wrapper > h2 {
@@ -167,17 +170,21 @@ form > div > div:last-of-type {
167170

168171
.btn {
169172
padding: 1rem 0;
170-
color: #33ffa1;
171-
background: rgba(0, 255, 208, 0);
172173
border-color: #33ffa1;
173174
border-radius: 5px;
175+
176+
}
177+
178+
.btn, .btn-smol {
179+
color: #33ffa1;
180+
background: rgba(0, 255, 208, 0);
174181
font-weight: bold;
175182
font-size: 1.5rem;
176183
transition: all ease .35s;
177184
backdrop-filter: blur(15px);
178185
}
179186

180-
.btn:hover {
187+
.btn:hover, .btn-smol:hover {
181188
background: rgba(0,255,208,.1450980392);
182189
cursor: pointer;
183190
}
@@ -191,3 +198,39 @@ html {
191198
visibility: visible;
192199
opacity: 1;
193200
}
201+
202+
.callout-note {
203+
border: solid #1f6febb3;
204+
background-color: rgba(31, 111, 235, 0.12);
205+
border-width: 0 0 0 .5rem;
206+
padding: 1.5rem 1rem;
207+
margin: 3rem auto;
208+
box-sizing: border-box;
209+
}
210+
211+
212+
.flex-grid {
213+
display: flex block;
214+
flex-direction: column;
215+
}
216+
217+
.flex-grid > div {
218+
flex-direction: row;
219+
display: flex;
220+
}
221+
222+
.flex-grid > div > div {
223+
flex-grow: 0;
224+
text-align: left;
225+
padding: .5rem 1.5rem;
226+
}
227+
228+
.flex-grid > div > div:first-child {
229+
padding-left: 0;
230+
}
231+
232+
.flex-grid > div > div:last-child {
233+
text-align: right;
234+
flex-grow: 1;
235+
padding-right: 0;
236+
}

0 commit comments

Comments
 (0)