From dc7db1a44ac3a565e9059821535bc9ede05a2877 Mon Sep 17 00:00:00 2001 From: Aodhan Date: Sun, 20 Jul 2025 01:56:24 +0100 Subject: [PATCH] Improved UI --- README.md | 5 ++++ handler.py | 14 +++++++++- index.html | 17 +++++++++--- js/main.js | 22 +++++++++++----- styles.css | 76 +++++++++++++++++++++++++++++++++++------------------- 5 files changed, 96 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index e7b7f6f..10c196d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ A web application for sorting and organizing images using swipe gestures, simila - **NSFW Filtering**: Toggle to include/exclude NSFW images on both the main swiper and history pages - **NSFW Blur**: Optional blur for NSFW thumbnails on the history page with a toolbar toggle - **Orientation & Action Filters**: Filter results by orientation (portrait/landscape/square) and by action taken +- **Image Sorting**: Choose to display images in random order (default), oldest first, or newest first - **Database Storage**: All selections are saved in a SQLite database - **Reset Functionality**: Option to clear all selections and start fresh @@ -29,6 +30,10 @@ A web application for sorting and organizing images using swipe gestures, simila 1. Run the server: `python server.py` 2. Open a web browser and navigate to `http://localhost:8000` + +### Main Page Features +- **Sort Order**: Use the dropdown in the sidebar to select image display order (Random, Oldest to Newest, or Newest to Oldest) +- **Mobile View**: On smaller screens, action buttons appear directly below the swipe window for easy access 3. Use the on-screen buttons or swipe gestures to categorize images: - Left: Discard - Right: Keep diff --git a/handler.py b/handler.py index 1557afa..e54e307 100644 --- a/handler.py +++ b/handler.py @@ -157,6 +157,7 @@ class ImageSwipeHandler(BaseHTTPRequestHandler): search_keywords = [kw.strip() for kw in search_keywords_str.split(',') if kw.strip()] actions_str = query_params.get("actions", ["Unactioned"])[0] actions = [a.strip() for a in actions_str.split(',') if a.strip()] + sort_order = query_params.get("sort", ["random"])[0] # Get sort order parameter conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row # Important to access columns by name @@ -209,6 +210,12 @@ class ImageSwipeHandler(BaseHTTPRequestHandler): if where_clauses: query += " WHERE " + " AND ".join(where_clauses) + # Add sorting based on sort_order + if sort_order == "oldest": + query += " ORDER BY meta.creation_date ASC" + elif sort_order == "newest": + query += " ORDER BY meta.creation_date DESC" + cur.execute(query, params) rows = cur.fetchall() conn.close() @@ -217,7 +224,12 @@ class ImageSwipeHandler(BaseHTTPRequestHandler): self._json_response({"message": "No more images available for this filter."}) return - row = random.choice(rows) + # For random order, select a random row from the results + if sort_order == "random": + row = random.choice(rows) + else: + # For oldest/newest, use the first row in the sorted results + row = rows[0] # Convert row to a dictionary for easier access row_dict = dict(row) diff --git a/index.html b/index.html index 5a42aaa..35d56ba 100644 --- a/index.html +++ b/index.html @@ -36,9 +36,18 @@
-
- -
+
+ + +
+ +
+ +
@@ -68,11 +77,11 @@ -
History +

Resolution: Loading...

diff --git a/js/main.js b/js/main.js index 7b37e84..ce33474 100644 --- a/js/main.js +++ b/js/main.js @@ -8,6 +8,7 @@ document.addEventListener('DOMContentLoaded', () => { previousOrientation: ['all'], allowNsfw: false, searchKeywords: [], + sortOrder: 'random', // Added sortOrder to state isLoading: false, isDragging: false, startX: 0, @@ -84,13 +85,16 @@ document.addEventListener('DOMContentLoaded', () => { // NSFW param params.append('allow_nsfw', state.allowNsfw ? '1' : '0'); - if (state.searchKeywords.length > 0) { - params.append('search', state.searchKeywords.join(',')); - } + if (state.searchKeywords.length > 0) { + params.append('search', state.searchKeywords.join(',')); + } - if (state.currentActions.length > 0) { - params.append('actions', state.currentActions.join(',')); - } + if (state.currentActions.length > 0) { + params.append('actions', state.currentActions.join(',')); + } + + // Add sort order parameter + params.append('sort', state.sortOrder); fetch(`/random-image?${params.toString()}`) .then(response => response.json()) @@ -400,5 +404,11 @@ document.addEventListener('DOMContentLoaded', () => { loadNewImage(); }); + // Add event listener for sort order change + document.getElementById('sort-order').addEventListener('change', (e) => { + state.sortOrder = e.target.value; + loadNewImage(); + }); + loadNewImage(); // Always load an image on startup }); diff --git a/styles.css b/styles.css index 5dd4ac3..6926f61 100644 --- a/styles.css +++ b/styles.css @@ -809,33 +809,55 @@ html, body { content: "↓"; } } -/* ---------------- MOBILE VIEW TWEAKS ---------------- */ -@media (max-width: 767px) { - /* tighter spacing between panels */ - .side-panel { - gap: 10px; - } - .filter-controls, - .action-buttons, - .status-area { - padding: 10px; - } - /* combined action buttons */ - .action-buttons { - flex-direction: row; - gap: 0; - } - .action-buttons .action-btn { - flex: 1; - border-radius: 0; - padding: 16px 0; - } - /* minimal spacing between blocks */ - .side-panel > * + * { - margin-top: 4px; - border-top: 1px solid #ccc; - } -} + /* ---------------- MOBILE VIEW TWEAKS ---------------- */ + @media (max-width: 991px) { + /* tighter spacing between panels */ + .side-panel { + gap: 10px; + } + .filter-controls, + .action-buttons, + .status-area { + padding: 10px; + } + /* combined action buttons */ + .action-buttons { + flex-direction: row; + gap: 0; + order: 1; /* First element in side panel */ + flex-wrap: wrap; + } + .action-buttons .action-btn { + flex: 1; + min-width: 25%; + border-radius: 0; + padding: 16px 0; + } + /* Ensure all other elements come after action buttons */ + .search-controls, + .sort-controls, + .filter-controls, + .status-area, + #btn-history { + order: 2; + } + /* minimal spacing between blocks */ + .side-panel > * + * { + margin-top: 4px; + border-top: 1px solid #ccc; + } + + /* Move action buttons below swipe window */ + .main-section { + flex-direction: column; + } + .swipe-container { + order: 1; + } + .side-panel { + order: 2; + } + } /* ---------------- DESKTOP 2x2 ACTION GRID ---------------- */ @media (min-width: 992px) { .action-buttons {