From fd2ca001afdb50e2a9e2a35f1da3efecead236e3 Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 17:16:40 +0100 Subject: [PATCH 01/16] server: poolpost nearby implementation --- doc/API.md | 37 ++++++++++++++++++++++++ server/szurubooru/api/post_api.py | 15 ++++++++-- server/szurubooru/func/posts.py | 48 ++++++++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/doc/API.md b/doc/API.md index 63a50a2f..5fc33084 100644 --- a/doc/API.md +++ b/doc/API.md @@ -975,6 +975,43 @@ data. Retrieves information about posts that are before or after an existing post. +## Getting pools around post +- **Request** + + `GET /post//pools-nearby` + +- **Output** + + ```json5 + [ + { + "pool": , + "firstPost": , + "lastPost": , + "nextPost": , + "previousPost": + }, + ... + ] + ``` + +- **Field meaning** + +- ``: The associated [micro pool resource](#micro-pool). +- `firstPost`: A [micro post resource](#micro-post) that displays the first post in the pool. +- `lastPost`: A [micro post resource](#micro-post) that displays the last post in the pool. +- `nextPost`: A [micro post resource](#micro-post) that displays the next post in the pool. +- `prevPost`: A [micro post resource](#micro-post) that displays the previous post in the pool. + +- **Errors** + + - the post does not exist + - privileges are too low + +- **Description** + + Retrieves extra information about any pools that the post is in. + ## Deleting post - **Request** diff --git a/server/szurubooru/api/post_api.py b/server/szurubooru/api/post_api.py index daba7f7e..34a2136c 100644 --- a/server/szurubooru/api/post_api.py +++ b/server/szurubooru/api/post_api.py @@ -5,12 +5,10 @@ from szurubooru import db, errors, model, rest, search from szurubooru.func import ( auth, favorites, - mime, posts, scores, serialization, snapshots, - tags, versions, ) @@ -283,6 +281,19 @@ def get_posts_around( ctx, post_id, lambda post: _serialize_post(ctx, post) ) +@rest.routes.get("/post/(?P[^/]+)/pools-nearby/?") +def get_pools_around( + ctx: rest.Context, params: Dict[str, str] +) -> rest.Response: + auth.verify_privilege(ctx.user, "posts:list") + auth.verify_privilege(ctx.user, "posts:view") + auth.verify_privilege(ctx.user, "pools:list") + _search_executor_config.user = ctx.user + post = _get_post(params) + results = posts.get_pools_nearby(post) + return posts.serialize_pool_posts_nearby(results) + + @rest.routes.post("/posts/reverse-search/?") def get_posts_by_image( diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index be2259cf..eeea4509 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -1,5 +1,6 @@ import hmac import logging +from collections import namedtuple from datetime import datetime from typing import Any, Callable, Dict, List, Optional, Tuple @@ -15,7 +16,6 @@ from szurubooru.func import ( pools, scores, serialization, - snapshots, tags, users, util, @@ -968,3 +968,49 @@ def search_by_image(image_content: bytes) -> List[Tuple[float, model.Post]]: ] else: return [] + +PoolPostsNearby = namedtuple('PoolPostsNearby', 'pool first_post prev_post next_post last_post') +def get_pools_nearby( + post: model.Post +) -> List[PoolPostsNearby]: + response = [] + pools = post.pools + + for pool in pools: + prev_post_id = None + next_post_id = None + break_loop = False + + for pool_post in pools.posts: + next_post_id = pool_post.post_id + + if break_loop == True: + break + + if post.id == pool_post.post_id: + break_loop = True + + prev_post_id = pool_post.post_id + + resp_entry = PoolPostsNearby( + pool=pool, + first_post=pool.posts[0].post_id, + last_post=pool.posts[-1].post_id, + prev_post=next_post_id, + next_post=prev_post_id, + ) + response.append(resp_entry) + return response + +def serialize_pool_posts_nearby( + nearby: List[PoolPostsNearby] +) -> Optional[rest.Response]: + return [ + { + "pool": pools.serialize_pool(entry.pool), + "firstPost": serialize_micro_post(try_get_post_by_id(entry.first_post)), + "lastPost": serialize_micro_post(try_get_post_by_id(entry.last_post)), + "prevPost": serialize_micro_post(try_get_post_by_id(entry.prev_post)), + "nextPost": serialize_micro_post(try_get_post_by_id(entry.first_post)), + } for entry in nearby + ] From d362736185e134c14deabff45408ac907e1e8b7e Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 20:04:37 +0100 Subject: [PATCH 02/16] server: fix small typo --- server/szurubooru/func/posts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index eeea4509..96d2783c 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -981,7 +981,7 @@ def get_pools_nearby( next_post_id = None break_loop = False - for pool_post in pools.posts: + for pool_post in pool.posts: next_post_id = pool_post.post_id if break_loop == True: From f51c27291a5306ef045f9b8262049f0eb6ba5bf7 Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 20:06:31 +0100 Subject: [PATCH 03/16] server: rename incorrect flag --- server/szurubooru/func/posts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index 96d2783c..daade7c1 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -987,7 +987,7 @@ def get_pools_nearby( if break_loop == True: break - if post.id == pool_post.post_id: + if post.post_id == pool_post.post_id: break_loop = True prev_post_id = pool_post.post_id From b1a012eac56a04e090979298292ec0d3b9a0b738 Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 20:09:20 +0100 Subject: [PATCH 04/16] server: add missing None argument --- server/szurubooru/func/posts.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index daade7c1..d8f7f012 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -1008,9 +1008,9 @@ def serialize_pool_posts_nearby( return [ { "pool": pools.serialize_pool(entry.pool), - "firstPost": serialize_micro_post(try_get_post_by_id(entry.first_post)), - "lastPost": serialize_micro_post(try_get_post_by_id(entry.last_post)), - "prevPost": serialize_micro_post(try_get_post_by_id(entry.prev_post)), - "nextPost": serialize_micro_post(try_get_post_by_id(entry.first_post)), + "firstPost": serialize_micro_post(try_get_post_by_id(entry.first_post), None), + "lastPost": serialize_micro_post(try_get_post_by_id(entry.last_post), None), + "prevPost": serialize_micro_post(try_get_post_by_id(entry.prev_post), None), + "nextPost": serialize_micro_post(try_get_post_by_id(entry.first_post), None), } for entry in nearby ] From a028b4ee6ee79a98799a66e20bc719a3bff69aef Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 20:45:44 +0100 Subject: [PATCH 05/16] server: fix incorrect values being used --- server/szurubooru/func/posts.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index d8f7f012..a4d4e185 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -996,8 +996,8 @@ def get_pools_nearby( pool=pool, first_post=pool.posts[0].post_id, last_post=pool.posts[-1].post_id, - prev_post=next_post_id, - next_post=prev_post_id, + prev_post=prev_post_id, + next_post=next_post_id, ) response.append(resp_entry) return response @@ -1011,6 +1011,6 @@ def serialize_pool_posts_nearby( "firstPost": serialize_micro_post(try_get_post_by_id(entry.first_post), None), "lastPost": serialize_micro_post(try_get_post_by_id(entry.last_post), None), "prevPost": serialize_micro_post(try_get_post_by_id(entry.prev_post), None), - "nextPost": serialize_micro_post(try_get_post_by_id(entry.first_post), None), + "nextPost": serialize_micro_post(try_get_post_by_id(entry.next_post), None), } for entry in nearby ] From 0cc8407d76fa5b49444ec4bbf1ab6716f4e06700 Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 20:53:09 +0100 Subject: [PATCH 06/16] server: slightly better way of prev/next --- server/szurubooru/func/posts.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index a4d4e185..f502d9a0 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -979,23 +979,21 @@ def get_pools_nearby( for pool in pools: prev_post_id = None next_post_id = None - break_loop = False - - for pool_post in pool.posts: - next_post_id = pool_post.post_id - - if break_loop == True: - break + first_post_id = pool.posts[0].post_id, + last_post_id = pool.posts[-1].post_id, + for idx, pool_post in enumerate(pool.posts): if post.post_id == pool_post.post_id: - break_loop = True - - prev_post_id = pool_post.post_id + if post.post_id != first_post_id: + prev_post_id = pool.posts[idx-1].post_id + if post.post_id != last_post_id: + next_post_id = pool.posts[idx+1].post_id + break resp_entry = PoolPostsNearby( pool=pool, - first_post=pool.posts[0].post_id, - last_post=pool.posts[-1].post_id, + first_post=first_post_id, + last_post=last_post_id, prev_post=prev_post_id, next_post=next_post_id, ) @@ -1007,7 +1005,7 @@ def serialize_pool_posts_nearby( ) -> Optional[rest.Response]: return [ { - "pool": pools.serialize_pool(entry.pool), + "pool": pools.serialize_micro_pool(entry.pool), "firstPost": serialize_micro_post(try_get_post_by_id(entry.first_post), None), "lastPost": serialize_micro_post(try_get_post_by_id(entry.last_post), None), "prevPost": serialize_micro_post(try_get_post_by_id(entry.prev_post), None), From 07267b9737ec3f6f6fb340d568166cd1f12c4d3f Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 21:04:53 +0100 Subject: [PATCH 07/16] server: better iterable logic --- server/szurubooru/func/posts.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index f502d9a0..eeed83ec 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -2,6 +2,7 @@ import hmac import logging from collections import namedtuple from datetime import datetime +from itertools import tee, chain, islice, izip from typing import Any, Callable, Dict, List, Optional, Tuple import sqlalchemy as sa @@ -96,6 +97,13 @@ FLAG_MAP = { model.Post.FLAG_SOUND: "sound", } +# https://stackoverflow.com/a/1012089 +def _get_nearby_iter(post_list): + previous_item, current_item, next_item = tee(post_list, 3) + previous_item = chain([None], previous_item) + next_item = chain(islice(next_item, 1, None), [None]) + return izip(previous_item, current_item, next_item) + def get_post_security_hash(id: int) -> str: return hmac.new( @@ -982,12 +990,10 @@ def get_pools_nearby( first_post_id = pool.posts[0].post_id, last_post_id = pool.posts[-1].post_id, - for idx, pool_post in enumerate(pool.posts): - if post.post_id == pool_post.post_id: - if post.post_id != first_post_id: - prev_post_id = pool.posts[idx-1].post_id - if post.post_id != last_post_id: - next_post_id = pool.posts[idx+1].post_id + for previous_item, current_item, next_item in _get_nearby_iter(pool.posts): + if post.post_id == current_item.post_id: + prev_post_id = previous_item.post_id + next_post_id = next_item.post_id break resp_entry = PoolPostsNearby( From 31d7fe7d904f7bef38f6950799508c13eda24026 Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 21:06:17 +0100 Subject: [PATCH 08/16] server: izip doesnt exist anymore --- server/szurubooru/func/posts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index eeed83ec..8c8067d3 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -2,7 +2,7 @@ import hmac import logging from collections import namedtuple from datetime import datetime -from itertools import tee, chain, islice, izip +from itertools import tee, chain, islice from typing import Any, Callable, Dict, List, Optional, Tuple import sqlalchemy as sa @@ -102,7 +102,7 @@ def _get_nearby_iter(post_list): previous_item, current_item, next_item = tee(post_list, 3) previous_item = chain([None], previous_item) next_item = chain(islice(next_item, 1, None), [None]) - return izip(previous_item, current_item, next_item) + return zip(previous_item, current_item, next_item) def get_post_security_hash(id: int) -> str: From 00e2b503d6a3ba79ee57ada7e1b7fb462559c56e Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 21:07:17 +0100 Subject: [PATCH 09/16] server: add none check --- server/szurubooru/func/posts.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index 8c8067d3..f1e21889 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -992,8 +992,10 @@ def get_pools_nearby( for previous_item, current_item, next_item in _get_nearby_iter(pool.posts): if post.post_id == current_item.post_id: - prev_post_id = previous_item.post_id - next_post_id = next_item.post_id + if previous_item != None: + prev_post_id = previous_item.post_id + if next_item != None: + next_post_id = next_item.post_id break resp_entry = PoolPostsNearby( From 65a6694925a602bbe24eca706b65ef6a0faf379e Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 22:00:12 +0100 Subject: [PATCH 10/16] client: add pool navigation elements this implementation was *heavily* cherry-picked from PR #403. --- client/css/pool-navigator-control.styl | 34 +++++++++++++ client/css/pool-navigator-list.styl | 9 ++++ client/html/pool_navigator.tpl | 49 ++++++++++++++++++ client/html/pool_navigator_list.tpl | 4 ++ client/html/post_main.tpl | 4 ++ client/js/controllers/post_main_controller.js | 12 ++++- client/js/controls/pool_navigator_control.js | 33 ++++++++++++ .../controls/pool_navigator_list_control.js | 51 +++++++++++++++++++ client/js/models/post_list.js | 9 ++++ client/js/views/post_main_view.js | 15 ++++++ 10 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 client/css/pool-navigator-control.styl create mode 100644 client/css/pool-navigator-list.styl create mode 100644 client/html/pool_navigator.tpl create mode 100644 client/html/pool_navigator_list.tpl create mode 100644 client/js/controls/pool_navigator_control.js create mode 100644 client/js/controls/pool_navigator_list_control.js diff --git a/client/css/pool-navigator-control.styl b/client/css/pool-navigator-control.styl new file mode 100644 index 00000000..56c22d28 --- /dev/null +++ b/client/css/pool-navigator-control.styl @@ -0,0 +1,34 @@ +@import colors + +.pool-navigator-container + padding: 0 + margin: 0 auto + + .pool-info-wrapper + box-sizing: border-box + width: 100% + margin: 0 0 1em 0 + display: flex + padding: 0.5em 1em + border: 1px solid $pool-navigator-border-color + background: $pool-navigator-background-color + + .pool-name + flex: 1 1; + text-align: center; + overflow: hidden; + white-space: nowrap; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + + .first, .last + flex-basis: 1em; + + .first, .prev, .next, .last + flex: 0 1; + margin: 0 .25em; + white-space: nowrap; + + +.darktheme .pool-navigator-container + background: $pool-navigator-header-background-color-darktheme \ No newline at end of file diff --git a/client/css/pool-navigator-list.styl b/client/css/pool-navigator-list.styl new file mode 100644 index 00000000..080ad01a --- /dev/null +++ b/client/css/pool-navigator-list.styl @@ -0,0 +1,9 @@ +.pool-navigators>ul + list-style-type: none + margin: 0 + padding: 0 + + >li + margin-bottom: 1em + &:last-child + margin-bottom: 0 diff --git a/client/html/pool_navigator.tpl b/client/html/pool_navigator.tpl new file mode 100644 index 00000000..647684d8 --- /dev/null +++ b/client/html/pool_navigator.tpl @@ -0,0 +1,49 @@ +
+
+ + <% if (ctx.canViewPosts && ctx.firstPost) { %> + + <% } %> + « + <% if (ctx.canViewPosts && ctx.firstPost) { %> + + <% } %> + + + <% if (ctx.canViewPosts && ctx.previousPost) { %> + + <% } %> + ‹ prev + <% if (ctx.canViewPosts && ctx.previousPost) { %> + + <% } %> + + + <% if (ctx.canViewPools) { %> + + <% } %> + Pool: <%- ctx.pool.names[0] %> + <% if (ctx.canViewPools) { %> + + <% } %> + + + <% if (ctx.canViewPosts && ctx.nextPost) { %> + + <% } %> + next › + <% if (ctx.canViewPosts && ctx.nextPost) { %> + + <% } %> + + + <% if (ctx.canViewPosts && ctx.lastPost) { %> + + <% } %> + » + <% if (ctx.canViewPosts && ctx.lastPost) { %> + + <% } %> + +
+
diff --git a/client/html/pool_navigator_list.tpl b/client/html/pool_navigator_list.tpl new file mode 100644 index 00000000..6c60e4e2 --- /dev/null +++ b/client/html/pool_navigator_list.tpl @@ -0,0 +1,4 @@ +
+
    +
+
\ No newline at end of file diff --git a/client/html/post_main.tpl b/client/html/post_main.tpl index 54c57333..b2e1e6f5 100644 --- a/client/html/post_main.tpl +++ b/client/html/post_main.tpl @@ -54,6 +54,10 @@
+ <% if (ctx.canListPools && ctx.canViewPools) { %> +
+ <% } %> + <% if (ctx.canListComments) { %>
<% } %> diff --git a/client/js/controllers/post_main_controller.js b/client/js/controllers/post_main_controller.js index 95cfdb52..b60a1686 100644 --- a/client/js/controllers/post_main_controller.js +++ b/client/js/controllers/post_main_controller.js @@ -11,11 +11,17 @@ const PostList = require("../models/post_list.js"); const PostMainView = require("../views/post_main_view.js"); const BasePostController = require("./base_post_controller.js"); const EmptyView = require("../views/empty_view.js"); +const PoolNavigatorListControl = require("../controls/pool_navigator_list_control.js"); class PostMainController extends BasePostController { constructor(ctx, editMode) { super(ctx); + let poolPostsNearby = Promise.resolve({results: []}); + if (api.hasPrivilege("pools:list") && api.hasPrivilege("pools:view")) { + poolPostsNearby = PostList.getNearbyPoolPosts(ctx.parameters.id) + } + let parameters = ctx.parameters; Promise.all([ Post.get(ctx.parameters.id), @@ -23,9 +29,10 @@ class PostMainController extends BasePostController { ctx.parameters.id, parameters ? parameters.query : null ), + poolPostsNearby ]).then( (responses) => { - const [post, aroundResponse] = responses; + const [post, aroundResponse, poolPostsNearby] = responses; // remove junk from query, but save it into history so that it can // be still accessed after history navigation / page refresh @@ -44,6 +51,7 @@ class PostMainController extends BasePostController { this._post = post; this._view = new PostMainView({ post: post, + poolPostsNearby: poolPostsNearby, editMode: editMode, prevPostId: aroundResponse.prev ? aroundResponse.prev.id @@ -56,6 +64,8 @@ class PostMainController extends BasePostController { canFeaturePosts: api.hasPrivilege("posts:feature"), canListComments: api.hasPrivilege("comments:list"), canCreateComments: api.hasPrivilege("comments:create"), + canListPools: api.hasPrivilege("pools:list"), + canViewPools: api.hasPrivilege("pools:view"), parameters: parameters, }); diff --git a/client/js/controls/pool_navigator_control.js b/client/js/controls/pool_navigator_control.js new file mode 100644 index 00000000..fa4394d1 --- /dev/null +++ b/client/js/controls/pool_navigator_control.js @@ -0,0 +1,33 @@ +"use strict"; + +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const events = require("../events.js"); +const views = require("../util/views.js"); + +const template = views.getTemplate("pool-navigator"); + +class PoolNavigatorControl extends events.EventTarget { + constructor(hostNode, poolPostNearby) { + super(); + this._hostNode = hostNode; + this._poolPostNearby = poolPostNearby; + + views.replaceContent( + this._hostNode, + template({ + pool: poolPostNearby.pool, + parameters: { query: `pool:${poolPostNearby.pool.id}` }, + linkClass: misc.makeCssName(poolPostNearby.pool.category, "pool"), + canViewPosts: api.hasPrivilege("posts:view"), + canViewPools: api.hasPrivilege("pools:view"), + firstPost: poolPostNearby.firstPost, + previousPost: poolPostNearby.previousPost, + nextPost: poolPostNearby.nextPost, + lastPost: poolPostNearby.lastPost, + }) + ); + } +} + +module.exports = PoolNavigatorControl; \ No newline at end of file diff --git a/client/js/controls/pool_navigator_list_control.js b/client/js/controls/pool_navigator_list_control.js new file mode 100644 index 00000000..38ec2f12 --- /dev/null +++ b/client/js/controls/pool_navigator_list_control.js @@ -0,0 +1,51 @@ +"use strict"; + +const events = require("../events.js"); +const views = require("../util/views.js"); +const PoolNavigatorControl = require("../controls/pool_navigator_control.js"); + +const template = views.getTemplate("pool-navigator-list"); + +class PoolNavigatorListControl extends events.EventTarget { + constructor(hostNode, poolPostNearby) { + super(); + this._hostNode = hostNode; + this._poolPostNearby = poolPostNearby; + this._indexToNode = {}; + + for (let [i, entry] of this._poolPostNearby.entries()) { + this._installPoolNavigatorNode(entry, i); + } + } + + get _poolNavigatorListNode() { + return this._hostNode; + } + + _installPoolNavigatorNode(poolPostNearby, i) { + const poolListItemNode = document.createElement("div"); + const poolControl = new PoolNavigatorControl( + poolListItemNode, + poolPostNearby, + ); + // events.proxyEvent(commentControl, this, "submit"); + // events.proxyEvent(commentControl, this, "score"); + // events.proxyEvent(commentControl, this, "delete"); + this._indexToNode[poolPostNearby.id] = poolListItemNode; + } + + _uninstallPoolNavigatorNode(index) { + const poolListItemNode = this._indexToNode[index]; + poolListItemNode.parentNode.removeChild(poolListItemNode); + } + + _evtAdd(e) { + this._installPoolNavigatorNode(e.detail.index); + } + + _evtRemove(e) { + this._uninstallPoolNavigatorNode(e.detail.index); + } +} + +module.exports = PoolNavigatorListControl; \ No newline at end of file diff --git a/client/js/models/post_list.js b/client/js/models/post_list.js index 8c2c9d4e..0d3655be 100644 --- a/client/js/models/post_list.js +++ b/client/js/models/post_list.js @@ -16,6 +16,15 @@ class PostList extends AbstractList { ); } + static getNearbyPoolPosts(id) { + return api.get( + uri.formatApiLink("post", id, "pools-nearby", { + query: PostList._decorateSearchQuery(searchQuery || ""), + fields: "id", + }) + ); + } + static search(text, offset, limit, fields) { return api .get( diff --git a/client/js/views/post_main_view.js b/client/js/views/post_main_view.js index 5ef7f61e..88b056d4 100644 --- a/client/js/views/post_main_view.js +++ b/client/js/views/post_main_view.js @@ -57,6 +57,7 @@ class PostMainView { this._installSidebar(ctx); this._installCommentForm(); this._installComments(ctx.post.comments); + this._installPoolNavigators(ctx.poolPostsAround); const showPreviousImage = () => { if (ctx.prevPostId) { @@ -137,6 +138,20 @@ class PostMainView { } } + _installPoolNavigators(poolPostsAround) { + const poolNavigatorsContainerNode = document.querySelector( + "#content-holder .pool-navigators-container" + ); + if (!poolNavigatorsContainerNode) { + return; + } + + this.poolNavigatorsControl = new PoolNavigatorListControl( + poolNavigatorsContainerNode, + poolPostsAround, + ); + } + _installCommentForm() { const commentFormContainer = document.querySelector( "#content-holder .comment-form-container" From 5d1215016547427d86a7ea73c867d780eb4288e8 Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 22:07:32 +0100 Subject: [PATCH 11/16] client: add missing import --- client/js/views/post_main_view.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/js/views/post_main_view.js b/client/js/views/post_main_view.js index 88b056d4..fa44eb27 100644 --- a/client/js/views/post_main_view.js +++ b/client/js/views/post_main_view.js @@ -10,6 +10,7 @@ const PostContentControl = require("../controls/post_content_control.js"); const PostNotesOverlayControl = require("../controls/post_notes_overlay_control.js"); const PostReadonlySidebarControl = require("../controls/post_readonly_sidebar_control.js"); const PostEditSidebarControl = require("../controls/post_edit_sidebar_control.js"); +const PoolNavigatorListControl = require("../controls/pool_navigator_list_control.js"); const CommentControl = require("../controls/comment_control.js"); const CommentListControl = require("../controls/comment_list_control.js"); From 467937a952fc7c0b731aa85ff6255ff6686eccd0 Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 22:12:22 +0100 Subject: [PATCH 12/16] client: append missing child --- client/js/controls/pool_navigator_list_control.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/js/controls/pool_navigator_list_control.js b/client/js/controls/pool_navigator_list_control.js index 38ec2f12..cc9a8151 100644 --- a/client/js/controls/pool_navigator_list_control.js +++ b/client/js/controls/pool_navigator_list_control.js @@ -32,6 +32,7 @@ class PoolNavigatorListControl extends events.EventTarget { // events.proxyEvent(commentControl, this, "score"); // events.proxyEvent(commentControl, this, "delete"); this._indexToNode[poolPostNearby.id] = poolListItemNode; + this._poolNavigatorListNode.appendChild(poolListItemNode); } _uninstallPoolNavigatorNode(index) { From 115d436a06c704917ec37fd2ca1e8fc0fc18966f Mon Sep 17 00:00:00 2001 From: noirscape Date: Wed, 4 Jan 2023 22:34:48 +0100 Subject: [PATCH 13/16] client: fix some incorrect references --- client/js/controllers/post_main_controller.js | 2 +- client/js/controls/pool_navigator_list_control.js | 5 +---- client/js/models/post_list.js | 1 - client/js/views/post_main_view.js | 6 +++--- server/szurubooru/func/posts.py | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/client/js/controllers/post_main_controller.js b/client/js/controllers/post_main_controller.js index b60a1686..5576b0e2 100644 --- a/client/js/controllers/post_main_controller.js +++ b/client/js/controllers/post_main_controller.js @@ -19,7 +19,7 @@ class PostMainController extends BasePostController { let poolPostsNearby = Promise.resolve({results: []}); if (api.hasPrivilege("pools:list") && api.hasPrivilege("pools:view")) { - poolPostsNearby = PostList.getNearbyPoolPosts(ctx.parameters.id) + poolPostsNearby = PostList.getNearbyPoolPosts(ctx.parameters.id); } let parameters = ctx.parameters; diff --git a/client/js/controls/pool_navigator_list_control.js b/client/js/controls/pool_navigator_list_control.js index cc9a8151..6aa5302f 100644 --- a/client/js/controls/pool_navigator_list_control.js +++ b/client/js/controls/pool_navigator_list_control.js @@ -28,9 +28,6 @@ class PoolNavigatorListControl extends events.EventTarget { poolListItemNode, poolPostNearby, ); - // events.proxyEvent(commentControl, this, "submit"); - // events.proxyEvent(commentControl, this, "score"); - // events.proxyEvent(commentControl, this, "delete"); this._indexToNode[poolPostNearby.id] = poolListItemNode; this._poolNavigatorListNode.appendChild(poolListItemNode); } @@ -49,4 +46,4 @@ class PoolNavigatorListControl extends events.EventTarget { } } -module.exports = PoolNavigatorListControl; \ No newline at end of file +module.exports = PoolNavigatorListControl; diff --git a/client/js/models/post_list.js b/client/js/models/post_list.js index 0d3655be..16df6eb8 100644 --- a/client/js/models/post_list.js +++ b/client/js/models/post_list.js @@ -19,7 +19,6 @@ class PostList extends AbstractList { static getNearbyPoolPosts(id) { return api.get( uri.formatApiLink("post", id, "pools-nearby", { - query: PostList._decorateSearchQuery(searchQuery || ""), fields: "id", }) ); diff --git a/client/js/views/post_main_view.js b/client/js/views/post_main_view.js index fa44eb27..805a9e73 100644 --- a/client/js/views/post_main_view.js +++ b/client/js/views/post_main_view.js @@ -58,7 +58,7 @@ class PostMainView { this._installSidebar(ctx); this._installCommentForm(); this._installComments(ctx.post.comments); - this._installPoolNavigators(ctx.poolPostsAround); + this._installPoolNavigators(ctx.poolPostsNearby); const showPreviousImage = () => { if (ctx.prevPostId) { @@ -139,7 +139,7 @@ class PostMainView { } } - _installPoolNavigators(poolPostsAround) { + _installPoolNavigators(poolPostsNearby) { const poolNavigatorsContainerNode = document.querySelector( "#content-holder .pool-navigators-container" ); @@ -149,7 +149,7 @@ class PostMainView { this.poolNavigatorsControl = new PoolNavigatorListControl( poolNavigatorsContainerNode, - poolPostsAround, + poolPostsNearby, ); } diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index f1e21889..c2d88c8d 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -1016,7 +1016,7 @@ def serialize_pool_posts_nearby( "pool": pools.serialize_micro_pool(entry.pool), "firstPost": serialize_micro_post(try_get_post_by_id(entry.first_post), None), "lastPost": serialize_micro_post(try_get_post_by_id(entry.last_post), None), - "prevPost": serialize_micro_post(try_get_post_by_id(entry.prev_post), None), + "previousPost": serialize_micro_post(try_get_post_by_id(entry.prev_post), None), "nextPost": serialize_micro_post(try_get_post_by_id(entry.next_post), None), } for entry in nearby ] From 475139b7ebbb99810d8b0c708cd544bc876b7ac6 Mon Sep 17 00:00:00 2001 From: noirscape Date: Sun, 8 Jan 2023 17:38:15 +0100 Subject: [PATCH 14/16] doc: properly name API elements --- doc/API.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/API.md b/doc/API.md index 5fc33084..30773c8f 100644 --- a/doc/API.md +++ b/doc/API.md @@ -998,10 +998,10 @@ data. - **Field meaning** - ``: The associated [micro pool resource](#micro-pool). -- `firstPost`: A [micro post resource](#micro-post) that displays the first post in the pool. -- `lastPost`: A [micro post resource](#micro-post) that displays the last post in the pool. -- `nextPost`: A [micro post resource](#micro-post) that displays the next post in the pool. -- `prevPost`: A [micro post resource](#micro-post) that displays the previous post in the pool. +- ``: A [micro post resource](#micro-post) that displays the first post in the pool. +- ``: A [micro post resource](#micro-post) that displays the last post in the pool. +- ``: A [micro post resource](#micro-post) that displays the next post in the pool. +- ``: A [micro post resource](#micro-post) that displays the previous post in the pool. - **Errors** From 113f7a6a488c638b3c956e86b93604df4b2c6612 Mon Sep 17 00:00:00 2001 From: noirscape Date: Sun, 8 Jan 2023 17:49:31 +0100 Subject: [PATCH 15/16] server/tests: add some tests --- .../tests/api/test_post_retrieving.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/server/szurubooru/tests/api/test_post_retrieving.py b/server/szurubooru/tests/api/test_post_retrieving.py index ac984c24..8d8dc2a0 100644 --- a/server/szurubooru/tests/api/test_post_retrieving.py +++ b/server/szurubooru/tests/api/test_post_retrieving.py @@ -125,3 +125,55 @@ def test_trying_to_retrieve_single_without_privileges( context_factory(user=user_factory(rank=model.User.RANK_ANONYMOUS)), {"post_id": 999}, ) + +def test_get_pool_post_around(user_factory, post_factory, pool_factory, pool_post_factory, context_factory): + p1 = post_factory(id=1) + p2 = post_factory(id=2) + p3 = post_factory(id=3) + db.session.add_all([p1, p2, p3]) + + pool = pool_factory(id=1) + db.session.add(pool) + + pool_posts = [pool_post_factory(pool=pool, post=p1), pool_post_factory(pool=pool, post=p2), pool_post_factory(pool=pool, post=p3)] + db.session.add_all(pool_posts) + + result = api.post_api.get_pools_around(context_factory(user=user_factory(rank=model.User.RANK_REGULAR)), {"post_id": 2}) + assert result[0]["previousPost"]["id"] == 1 and result[0]["nextPost"]["id"] == 3 + +def test_get_pool_post_around_start(user_factory, post_factory, pool_factory, pool_post_factory, context_factory): + p1 = post_factory(id=1) + p2 = post_factory(id=2) + p3 = post_factory(id=3) + db.session.add_all([p1, p2, p3]) + + pool = pool_factory(id=1) + db.session.add(pool) + + pool_posts = [pool_post_factory(pool=pool, post=p1), pool_post_factory(pool=pool, post=p2), pool_post_factory(pool=pool, post=p3)] + db.session.add_all(pool_posts) + + result = api.post_api.get_pools_around(context_factory(user=user_factory(rank=model.User.RANK_REGULAR)), {"post_id": 1}) + assert result[0]["previousPost"] == None and result[0]["nextPost"]["id"] == 2 + +def test_get_pool_post_around_end(user_factory, post_factory, pool_factory, pool_post_factory, context_factory): + p1 = post_factory(id=1) + p2 = post_factory(id=2) + p3 = post_factory(id=3) + db.session.add_all([p1, p2, p3]) + + pool = pool_factory(id=1) + db.session.add(pool) + + pool_posts = [pool_post_factory(pool=pool, post=p1), pool_post_factory(pool=pool, post=p2), pool_post_factory(pool=pool, post=p3)] + db.session.add_all(pool_posts) + + result = api.post_api.get_pools_around(context_factory(user=user_factory(rank=model.User.RANK_REGULAR)), {"post_id": 3}) + assert result[0]["previousPost"]["id"] == 2 and result[0]["nextPost"] == None + +def test_get_pool_post_around_no_pool(user_factory, post_factory, pool_factory, pool_post_factory, context_factory): + p1 = post_factory(id=1) + db.session.add(p1) + + result = api.post_api.get_pools_around(context_factory(user=user_factory(rank=model.User.RANK_REGULAR)), {"post_id": 1}) + assert result == [] From 564cb360a1a9fb546da8718c0b1b77554b021104 Mon Sep 17 00:00:00 2001 From: noirscape Date: Sun, 8 Jan 2023 18:02:07 +0100 Subject: [PATCH 16/16] server/tests: add necessary privilege to fixture --- server/szurubooru/tests/api/test_post_retrieving.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/szurubooru/tests/api/test_post_retrieving.py b/server/szurubooru/tests/api/test_post_retrieving.py index 8d8dc2a0..aa70f77a 100644 --- a/server/szurubooru/tests/api/test_post_retrieving.py +++ b/server/szurubooru/tests/api/test_post_retrieving.py @@ -14,6 +14,7 @@ def inject_config(config_injector): "privileges": { "posts:list": model.User.RANK_REGULAR, "posts:view": model.User.RANK_REGULAR, + "pools:list": model.User.RANK_REGULAR, }, } )