add bookmarks view
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
from flask import Blueprint, render_template, request, url_for
|
from flask import Blueprint, render_template, request, url_for
|
||||||
from ..auth import get_active_user, is_logged_in, hard_login_required
|
from ..auth import get_active_user, is_logged_in, hard_login_required
|
||||||
from ..models import BookmarkCollections, BookmarkedPosts, BookmarkedThreads
|
from ..models import BookmarkCollections, BookmarkedPosts, BookmarkedThreads, Threads, Posts
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
bp = Blueprint('hyperapi', __name__, url_prefix='/hyperapi/')
|
bp = Blueprint('hyperapi', __name__, url_prefix='/hyperapi/')
|
||||||
@@ -24,6 +24,14 @@ def get_bookmark_dropdown():
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return 'error', 400
|
return 'error', 400
|
||||||
is_thread = concept_kind == 'thread'
|
is_thread = concept_kind == 'thread'
|
||||||
|
if is_thread:
|
||||||
|
target_thread = Threads.find({'id': concept_id})
|
||||||
|
if not target_thread:
|
||||||
|
return 'This thread no longer exists. Please refresh the page.', 404
|
||||||
|
else:
|
||||||
|
target_post = Posts.find({'id': concept_id})
|
||||||
|
if not target_post:
|
||||||
|
return 'This post no longer exists. Please refresh the page.', 404
|
||||||
collections = BookmarkCollections.get_for_user(user.id)
|
collections = BookmarkCollections.get_for_user(user.id)
|
||||||
in_collection = None
|
in_collection = None
|
||||||
note = ''
|
note = ''
|
||||||
@@ -54,6 +62,9 @@ def bookmark_thread():
|
|||||||
bt.delete()
|
bt.delete()
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
if not Threads.find({'id': thread_id}):
|
||||||
|
return 'error', 404
|
||||||
|
|
||||||
target_collection = BookmarkCollections.find({'id': target_collection_id})
|
target_collection = BookmarkCollections.find({'id': target_collection_id})
|
||||||
note = request.form.get('note', '')
|
note = request.form.get('note', '')
|
||||||
if not target_collection:
|
if not target_collection:
|
||||||
@@ -91,6 +102,9 @@ def bookmark_post():
|
|||||||
bp.delete()
|
bp.delete()
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
if not Posts.find({'id': post_id}):
|
||||||
|
return 'error', 404
|
||||||
|
|
||||||
target_collection = BookmarkCollections.find({'id': target_collection_id})
|
target_collection = BookmarkCollections.find({'id': target_collection_id})
|
||||||
note = request.form.get('note', '')
|
note = request.form.get('note', '')
|
||||||
if not target_collection:
|
if not target_collection:
|
||||||
|
|||||||
@@ -445,8 +445,9 @@ def inbox(username):
|
|||||||
@login_required
|
@login_required
|
||||||
@redirect_to_own
|
@redirect_to_own
|
||||||
def bookmarks(username):
|
def bookmarks(username):
|
||||||
username = username.lower()
|
user = get_active_user()
|
||||||
return 'stub'
|
collections = BookmarkCollections.get_for_user(user.id)
|
||||||
|
return render_template('users/bookmarks.html', collections=collections)
|
||||||
|
|
||||||
@bp.get('/<username>/bookmarks/collections/')
|
@bp.get('/<username>/bookmarks/collections/')
|
||||||
@login_required
|
@login_required
|
||||||
|
|||||||
@@ -136,10 +136,16 @@
|
|||||||
</div>
|
</div>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro bookmark_button(kind, id, text='Bookmark') -%}
|
||||||
|
<button autocomplete='off' data-r="enhance" data-s="showBookmarkMenu" disabled title="This feature requires JavaScript to be enabled." data-concept-kind="{{kind}}" data-concept-id="{{id}}">{{icn_bookmark(24)}}{{text}}…</button>
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro full_post(
|
{% macro full_post(
|
||||||
post, render_sig=true, is_latest=false,
|
post, render_sig=true, is_latest=false,
|
||||||
show_toolbar=true, is_editing=false, thread=none,
|
show_toolbar=true, is_editing=false, thread=none,
|
||||||
show_reactions=true, show_thread=false, allow_reacting=true
|
show_reactions=true, show_thread=false, allow_reacting=true,
|
||||||
|
tb_edit=true, tb_quote=true, tb_delete=true, tb_bookmark=true,
|
||||||
|
bookmark_btn='Bookmark', tb_pretext=''
|
||||||
) -%}
|
) -%}
|
||||||
{%- if is_logged_in() -%}
|
{%- if is_logged_in() -%}
|
||||||
{%- set can_delete = post.user_id == get_active_user().id or is_mod() -%}
|
{%- set can_delete = post.user_id == get_active_user().id or is_mod() -%}
|
||||||
@@ -169,6 +175,9 @@
|
|||||||
<div class="post-content">
|
<div class="post-content">
|
||||||
<div class="plank even minimal secondary-bg no-shadow post-info">
|
<div class="plank even minimal secondary-bg no-shadow post-info">
|
||||||
<span>
|
<span>
|
||||||
|
{%- if tb_pretext -%}
|
||||||
|
<span>{{tb_pretext}} • </span>
|
||||||
|
{%- endif -%}
|
||||||
<a href="{{get_post_url(post.id, _anchor=true)}}">
|
<a href="{{get_post_url(post.id, _anchor=true)}}">
|
||||||
{%- if post.edited_at <= post.created_at -%}
|
{%- if post.edited_at <= post.created_at -%}
|
||||||
<i>Posted on {{timestamp(post.created_at)}}</i>
|
<i>Posted on {{timestamp(post.created_at)}}</i>
|
||||||
@@ -181,17 +190,19 @@
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</span>
|
</span>
|
||||||
{%- if show_toolbar -%}
|
{%- if show_toolbar -%}
|
||||||
<span class="thread-actions">
|
<span class="subheader-actions">
|
||||||
{%- if owns -%}
|
{%- if owns and tb_edit -%}
|
||||||
<a class="linkbutton" href="{{url_for('posts.edit', post_id=post.id, _anchor='babycode-content')}}">Edit</a>
|
<a class="linkbutton" href="{{url_for('posts.edit', post_id=post.id, _anchor='babycode-content')}}">Edit</a>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{%- if can_reply -%}
|
{%- if can_reply and tb_quote -%}
|
||||||
<button autocomplete='off' data-r="enhance" data-s="babycodeEditorQuote" disabled title="This feature requires JavaScript to be enabled." data-quote="{{post.original_markup}}" data-poster-name="{{ post.display_name if post.display_name else post.username }}">Quote</button>
|
<button autocomplete='off' data-r="enhance" data-s="babycodeEditorQuote" disabled title="This feature requires JavaScript to be enabled." data-quote="{{post.original_markup}}" data-poster-name="{{ post.display_name if post.display_name else post.username }}">Quote</button>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{%- if can_delete -%}
|
{%- if can_delete and tb_delete -%}
|
||||||
<a class="linkbutton critical" href="{{url_for('posts.delete', post_id=post.id)}}">Delete</a>
|
<a class="linkbutton critical" href="{{url_for('posts.delete', post_id=post.id)}}">Delete</a>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
<button autocomplete='off' data-r="enhance" data-s="showBookmarkMenu" disabled title="This feature requires JavaScript to be enabled." data-concept-kind="post" data-concept-id="{{post.id}}">{{icn_bookmark(24)}}Bookmark…</button>
|
{%- if tb_bookmark -%}
|
||||||
|
{{ bookmark_button('post', post.id, bookmark_btn) }}
|
||||||
|
{%- endif -%}
|
||||||
</span>
|
</span>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</div>
|
</div>
|
||||||
@@ -229,6 +240,18 @@
|
|||||||
</div>
|
</div>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro bookmark_menu() -%}
|
||||||
|
{%- if is_logged_in() -%}
|
||||||
|
<div id="bookmark-popover" data-r="showBookmarkMenu" class="plank even" popover>
|
||||||
|
<div class="bookmark-menu-header">
|
||||||
|
<span>Bookmark collections</span>
|
||||||
|
<a href="{{url_for('users.bookmarks', username=get_active_user().username)}}">View bookmarks</a>
|
||||||
|
</div>
|
||||||
|
<div class="bookmark-menu-inner" data-r="fillBookmarkMenu">Loading…</div>
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
{% macro infobox(message, kind=InfoboxKind.INFO) -%}
|
{% macro infobox(message, kind=InfoboxKind.INFO) -%}
|
||||||
<div class="infobox plank top contain-svg horizontal {{InfoboxHTMLClass[kind]}}">
|
<div class="infobox plank top contain-svg horizontal {{InfoboxHTMLClass[kind]}}">
|
||||||
{%- if kind == InfoboxKind.INFO -%}
|
{%- if kind == InfoboxKind.INFO -%}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
{%- block content -%}
|
{%- block content -%}
|
||||||
{%- call() subheader("Delete post", "Are you sure you want to delete this post? This action can not be undone.") -%}
|
{%- call() subheader("Delete post", "Are you sure you want to delete this post? This action can not be undone.") -%}
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<fieldset class="plank minimal even no-shadow thread-actions">
|
<fieldset class="plank minimal even no-shadow subheader-actions">
|
||||||
<legend>Please confirm</legend>
|
<legend>Please confirm</legend>
|
||||||
<a href="{{get_post_url(post.id, _anchor=true)}}" class="linkbutton">Cancel</a>
|
<a href="{{get_post_url(post.id, _anchor=true)}}" class="linkbutton">Cancel</a>
|
||||||
<input type="submit" value="Delete" class="critical">
|
<input type="submit" value="Delete" class="critical">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{%- from 'common/macros.html' import subheader, timestamp, pager, babycode_editor_component -%}
|
{%- from 'common/macros.html' import subheader, timestamp, pager, babycode_editor_component -%}
|
||||||
{%- from 'common/icons.html' import icn_bookmark -%}
|
{%- from 'common/icons.html' import icn_bookmark -%}
|
||||||
{%- from 'common/macros.html' import full_post with context -%}
|
{%- from 'common/macros.html' import full_post, bookmark_menu with context -%}
|
||||||
{%- extends 'base.html' -%}
|
{%- extends 'base.html' -%}
|
||||||
{%- block title -%}{{thread.title}}{%- endblock -%}
|
{%- block title -%}{{thread.title}}{%- endblock -%}
|
||||||
{%- block content -%}
|
{%- block content -%}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{%- endset -%}
|
{%- endset -%}
|
||||||
{%- call() subheader(thread.title, td) -%}
|
{%- call() subheader(thread.title, td) -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Actions</legend>
|
<legend>Actions</legend>
|
||||||
{%- if is_logged_in() -%}
|
{%- if is_logged_in() -%}
|
||||||
{%- if thread.user_id == get_active_user().id -%}
|
{%- if thread.user_id == get_active_user().id -%}
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<a href="{{url_for('threads.feed', thread_id=thread.id)}}" class="linkbutton rss">Subscribe via RSS</a>
|
<a href="{{url_for('threads.feed', thread_id=thread.id)}}" class="linkbutton rss">Subscribe via RSS</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{%- if is_mod() -%}
|
{%- if is_mod() -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Moderation actions</legend>
|
<legend>Moderation actions</legend>
|
||||||
{%- if thread.user_id != get_active_user().id -%}
|
{%- if thread.user_id != get_active_user().id -%}
|
||||||
<a class="linkbutton warn" href="{{url_for('threads.edit', thread_id=thread.id)}}">Rename</a>
|
<a class="linkbutton warn" href="{{url_for('threads.edit', thread_id=thread.id)}}">Rename</a>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<input type="submit" value="Move" class="warn">
|
<input type="submit" value="Move" class="warn">
|
||||||
</form>
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count) -}}
|
{{- pager(page, page_count) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
</main>
|
</main>
|
||||||
<div class="plank secondary-bg">
|
<div class="plank secondary-bg">
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count) -}}
|
{{- pager(page, page_count) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -83,15 +83,7 @@
|
|||||||
<button>Stop updates</button>
|
<button>Stop updates</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{%- if is_logged_in() -%}
|
{{ bookmark_menu() }}
|
||||||
<div id="bookmark-popover" data-r="showBookmarkMenu" class="plank even" popover>
|
|
||||||
<div class="bookmark-menu-header">
|
|
||||||
<span>Bookmark collections</span>
|
|
||||||
<a href="{{url_for('users.bookmarks', username=get_active_user().username)}}">View bookmarks</a>
|
|
||||||
</div>
|
|
||||||
<div class="bookmark-menu-inner" data-r="fillBookmarkMenu">Loading…</div>
|
|
||||||
</div>
|
|
||||||
{%- endif -%}
|
|
||||||
{%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(thread) -%}
|
{%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(thread) -%}
|
||||||
<form action="{{url_for('threads.reply', thread_id=thread.id)}}" method="POST" class="plank post-edit-form" data-listen="submit" data-r="clearThreadDraft" data-s="clearThreadDraft">
|
<form action="{{url_for('threads.reply', thread_id=thread.id)}}" method="POST" class="plank post-edit-form" data-listen="submit" data-r="clearThreadDraft" data-s="clearThreadDraft">
|
||||||
<h2 class="info">Reply to "{{thread.title}}"</h2>
|
<h2 class="info">Reply to "{{thread.title}}"</h2>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
{%- endset -%}
|
{%- endset -%}
|
||||||
{%- call() subheader(('Threads in "%s"' % topic.name), td) -%}
|
{%- call() subheader(('Threads in "%s"' % topic.name), td) -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Actions</legend>
|
<legend>Actions</legend>
|
||||||
{%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(topic) -%}
|
{%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(topic) -%}
|
||||||
<a href="{{url_for('threads.new', topic_id=topic.id)}}" class="linkbutton">New thread</a>
|
<a href="{{url_for('threads.new', topic_id=topic.id)}}" class="linkbutton">New thread</a>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{%- if is_mod() -%}
|
{%- if is_mod() -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Moderation actions</legend>
|
<legend>Moderation actions</legend>
|
||||||
<a href="{{url_for('mod.edit_topic', topic_id=topic.id)}}" class="linkbutton">Edit</a>
|
<a href="{{url_for('mod.edit_topic', topic_id=topic.id)}}" class="linkbutton">Edit</a>
|
||||||
<form action="{{url_for('mod.lock_topic', topic_id=topic.id)}}" method="POST">
|
<form action="{{url_for('mod.lock_topic', topic_id=topic.id)}}" method="POST">
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{%- if threads | length > 0 -%}
|
{%- if threads | length > 0 -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count, args=request.args) -}}
|
{{- pager(page, page_count, args=request.args) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{%- if threads | length > 0 -%}
|
{%- if threads | length > 0 -%}
|
||||||
<div class="plank secondary-bg">
|
<div class="plank secondary-bg">
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count, args=request.args) -}}
|
{{- pager(page, page_count, args=request.args) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
{%- block content -%}
|
{%- block content -%}
|
||||||
{%- call() subheader('All topics') -%}
|
{%- call() subheader('All topics') -%}
|
||||||
{%- if is_mod() -%}
|
{%- if is_mod() -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Moderation actions</legend>
|
<legend>Moderation actions</legend>
|
||||||
<a href="{{url_for('mod.new_topic')}}" class="linkbutton">New topic</a>
|
<a href="{{url_for('mod.new_topic')}}" class="linkbutton">New topic</a>
|
||||||
<a href="{{url_for('mod.index', _anchor='sort-topics')}}" class="linkbutton">Sort topics</a>
|
<a href="{{url_for('mod.index', _anchor='sort-topics')}}" class="linkbutton">Sort topics</a>
|
||||||
|
|||||||
56
app/templates/users/bookmarks.html
Normal file
56
app/templates/users/bookmarks.html
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{%- from 'common/macros.html' import full_post, bookmark_menu with context -%}
|
||||||
|
{%- from 'common/macros.html' import subheader, bookmark_button -%}
|
||||||
|
{%- extends 'base.html' -%}
|
||||||
|
{%- block title -%}bookmarks"{%- endblock -%}
|
||||||
|
{%- block content -%}
|
||||||
|
<bitty-8 data-connect="/static/js/bits/bookmark-menu.js"></bitty-8>
|
||||||
|
<bitty-8 data-connect="/static/js/bits/bookmarks.js"></bitty-8>
|
||||||
|
{%- call() subheader('Your bookmarks') -%}
|
||||||
|
<fieldset class="plank even no-shadow minimal subheader-actions js-only" data-r="enhance">
|
||||||
|
<legend>Actions</legend>
|
||||||
|
<a href="{{url_for('users.bookmark_collections', username=get_active_user().username)}}" class="linkbutton">Manage collections</a>
|
||||||
|
</fieldset>
|
||||||
|
{%- endcall -%}
|
||||||
|
<div class="plank">
|
||||||
|
{%- for collection in collections -%}
|
||||||
|
{%- set thread_count = collection.get_threads_count() -%}
|
||||||
|
{%- set post_count = collection.get_posts_count() -%}
|
||||||
|
<details class="separated" data-id="{{collection.id}}" data-r="restoreCollectionDetails setCollectionDetails" data-s="setCollectionDetails">
|
||||||
|
<summary class="plank secondary-bg no-shadow even">{{collection.name}} ({{thread_count}} {{'thread' | pluralize(num=thread_count)}}, {{post_count}} {{'post' | pluralize(num=post_count)}})</summary>
|
||||||
|
{%- if thread_count > 0 -%}
|
||||||
|
<details class="inner" data-id="{{collection.id}}" data-r="restoreThreadDetails setThreadDetails" data-s="setThreadDetails">
|
||||||
|
<summary class="plank no-shadow even">Threads</summary>
|
||||||
|
<table class="three-cols">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="plank even no-shadow contrast-bg">Title</th>
|
||||||
|
<th class="plank even no-shadow contrast-bg">Memo</th>
|
||||||
|
<th class="plank even no-shadow contrast-bg">Manage</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{%- for bt in collection.get_threads() -%}
|
||||||
|
{%- set thread = bt.get_thread() -%}
|
||||||
|
<tr>
|
||||||
|
<td class="plank even no-shadow minimal secondary-bg"><a href="{{url_for('threads.thread_by_id', thread_id=thread.id)}}">{{thread.title}}</a></td>
|
||||||
|
<td class="plank even no-shadow minimal secondary-bg">{{bt.note}}</td>
|
||||||
|
<td class="plank even no-shadow minimal secondary-bg">{{bookmark_button('thread', id=thread.id, text='Manage')}}</td>
|
||||||
|
</tr>
|
||||||
|
{%- endfor -%}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</details>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if post_count > 0 -%}
|
||||||
|
<details class="inner" data-id="{{collection.id}}" data-r="restorePostDetails setPostDetails" data-s="setPostDetails">
|
||||||
|
<summary class="plank no-shadow even">Posts</summary>
|
||||||
|
{%- for bp in collection.get_posts() -%}
|
||||||
|
<div class="post plank no-shadow even">{{ full_post(bp.get_post().get_full_post_view(), render_sig=false, show_thread=true, show_reactions=false, tb_edit=false, tb_quote=false, tb_delete=false, bookmark_btn='Manage', tb_pretext=('memo: ' + bp.note) if bp.note else '') }}</div>
|
||||||
|
{%- endfor -%}
|
||||||
|
</details>
|
||||||
|
{%- endif -%}
|
||||||
|
</details>
|
||||||
|
{%- endfor -%}
|
||||||
|
</div>
|
||||||
|
{{ bookmark_menu() }}
|
||||||
|
{%- endblock -%}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
{%- macro collection_item(name='', can_delete=true, id=-1, thread_count=0, post_count=0) -%}
|
{%- macro collection_item(name='', can_delete=true, id=-1, thread_count=0, post_count=0) -%}
|
||||||
<input name="name[]" type="text" autocomplete="off" value="{{name}}" required maxlength=60 placeholder="Collection name">
|
<input name="name[]" type="text" autocomplete="off" value="{{name}}" required maxlength=60 placeholder="Collection name">
|
||||||
<input type="hidden" name="id[]" value="{{ 'new' if id == -1 else id}}" autocomplete="off">
|
<input type="hidden" name="id[]" value="{{ 'new' if id == -1 else id}}" autocomplete="off">
|
||||||
<span>{{thread_count}} {{'thread' | pluralize(num=thread_count)}}, {{post_count}} {{'post' | pluralize(num=post_count)}} </span>
|
<span>{{thread_count}} {{'thread' | pluralize(num=thread_count)}}, {{post_count}} {{'post' | pluralize(num=post_count)}}</span>
|
||||||
{%- if not can_delete -%}
|
{%- if not can_delete -%}
|
||||||
<i>Default collection</i>
|
<i>Default collection</i>
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
@@ -20,7 +20,7 @@ Drag collections to reoder them. You cannot move or remove the default collectio
|
|||||||
<div data-r="enhanceHide">This page requires JS enabled to work correctly.</div>
|
<div data-r="enhanceHide">This page requires JS enabled to work correctly.</div>
|
||||||
{%- endset -%}
|
{%- endset -%}
|
||||||
{%- call() subheader('Manage bookmark collections', sh) -%}
|
{%- call() subheader('Manage bookmark collections', sh) -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions js-only" data-r="enhance">
|
<fieldset class="plank even no-shadow minimal subheader-actions js-only" data-r="enhance">
|
||||||
<legend>Actions</legend>
|
<legend>Actions</legend>
|
||||||
<button data-s="addCollection">Add new collection</button>
|
<button data-s="addCollection">Add new collection</button>
|
||||||
<input type="submit" class="alt" value="Save collections" form="collections-form">
|
<input type="submit" class="alt" value="Save collections" form="collections-form">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
{%- endset -%}
|
{%- endset -%}
|
||||||
{%- call() subheader("%s's posts" % target_user.get_readable_name(), td) -%}
|
{%- call() subheader("%s's posts" % target_user.get_readable_name(), td) -%}
|
||||||
{%- if posts -%}
|
{%- if posts -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count) -}}
|
{{- pager(page, page_count) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<div class="post plank">{{full_post(post, show_toolbar=false, show_thread=true, allow_reacting=false)}}</div>
|
<div class="post plank">{{full_post(post, show_toolbar=false, show_thread=true, allow_reacting=false)}}</div>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
<div class="plank">
|
<div class="plank">
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count) -}}
|
{{- pager(page, page_count) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
{%- endset -%}
|
{%- endset -%}
|
||||||
{%- call() subheader("%s's started threads" % target_user.get_readable_name(), td) -%}
|
{%- call() subheader("%s's started threads" % target_user.get_readable_name(), td) -%}
|
||||||
{%- if threads -%}
|
{%- if threads -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count) -}}
|
{{- pager(page, page_count) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
<div class="plank">
|
<div class="plank">
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count) -}}
|
{{- pager(page, page_count) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
{%- if is_logged_in() -%}
|
{%- if is_logged_in() -%}
|
||||||
|
|
||||||
{%- if target_user.id == get_active_user().id -%}
|
{%- if target_user.id == get_active_user().id -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Actions</legend>
|
<legend>Actions</legend>
|
||||||
<form action="{{url_for('users.log_out')}}" method="POST">
|
<form action="{{url_for('users.log_out')}}" method="POST">
|
||||||
<input type="submit" class="warn" value="Log out">
|
<input type="submit" class="warn" value="Log out">
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|
||||||
{%- if get_active_user().is_mod() and target_user.id != get_active_user().id and target_user.permission < get_active_user().permission -%}
|
{%- if get_active_user().is_mod() and target_user.id != get_active_user().id and target_user.permission < get_active_user().permission -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal subheader-actions">
|
||||||
<legend>Moderation actions</legend>
|
<legend>Moderation actions</legend>
|
||||||
<form class="thread-actions" method="POST">
|
<form class="subheader-actions" method="POST">
|
||||||
{{csrf_input() | safe}}
|
{{csrf_input() | safe}}
|
||||||
{%- if target_user.is_guest() -%}
|
{%- if target_user.is_guest() -%}
|
||||||
<input class="warn" type="submit" value="Approve user" formaction="{{url_for('mod.make_user_regular', user_id=target_user.id)}}">
|
<input class="warn" type="submit" value="Approve user" formaction="{{url_for('mod.make_user_regular', user_id=target_user.id)}}">
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ footer {
|
|||||||
gap: var(--base-padding);
|
gap: var(--base-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.thread-actions {
|
.subheader-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: var(--base-padding);
|
gap: var(--base-padding);
|
||||||
@@ -662,12 +662,12 @@ details {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not([open]) summary::before {
|
&:not([open]) > summary::before {
|
||||||
content: '▶';
|
content: '▶';
|
||||||
padding-inline: var(--base-padding);
|
padding-inline: var(--base-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
&[open] summary::before {
|
&[open] > summary::before {
|
||||||
content: '▼';
|
content: '▼';
|
||||||
padding-inline: var(--base-padding);
|
padding-inline: var(--base-padding);
|
||||||
}
|
}
|
||||||
@@ -677,6 +677,10 @@ details.separated {
|
|||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
details.inner {
|
||||||
|
margin-inline: var(--base-padding);
|
||||||
|
}
|
||||||
|
|
||||||
.avatar-form {
|
.avatar-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--huge-padding);
|
gap: var(--huge-padding);
|
||||||
@@ -724,6 +728,25 @@ details.separated {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.three-cols > thead > tr > th {
|
||||||
|
&:nth-child(1) {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(3) {
|
||||||
|
width: 15%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* babycode tags */
|
/* babycode tags */
|
||||||
.inline-code {
|
.inline-code {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ async function getHTML(endpoint, options = {}) {
|
|||||||
|
|
||||||
const params = new URLSearchParams(query);
|
const params = new URLSearchParams(query);
|
||||||
const res = await fetch(`${endpoint}?${params}`, options);
|
const res = await fetch(`${endpoint}?${params}`, options);
|
||||||
if (!res.ok) {
|
// if (!res.ok) {
|
||||||
console.error(res);
|
// console.error(res);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return { body: await res.text(), status: res.status };
|
return { body: await res.text(), status: res.status };
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ export async function bookmarkMenuSubmit(ev, _, el) {
|
|||||||
const status = (await getHTML(url, options)).status;
|
const status = (await getHTML(url, options)).status;
|
||||||
|
|
||||||
if (status !== 204) {
|
if (status !== 204) {
|
||||||
b.trigger('bookmarkMenuShowError');
|
b.send({ status: status }, 'bookmarkMenuShowError');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +99,17 @@ export function bookmarkMenuResetSavedButton(_, __, el) {
|
|||||||
el.value = 'Save';
|
el.value = 'Save';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bookmarkMenuShowError(_, __, el) {
|
export function bookmarkMenuShowError(payload, _, el) {
|
||||||
if (el === undefined) {
|
if (el === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payload.status === 404) {
|
||||||
|
el.innerText = 'This thread or post no longer exists. Please refresh the page.';
|
||||||
|
} else {
|
||||||
|
el.innerText = 'Something went wrong. Try again later.';
|
||||||
|
}
|
||||||
|
|
||||||
if (el.classList.contains('hidden')) {
|
if (el.classList.contains('hidden')) {
|
||||||
el.classList.remove('hidden');
|
el.classList.remove('hidden');
|
||||||
setTimeout(() => { b.trigger('bookmarkMenuHideError') }, 4000);
|
setTimeout(() => { b.trigger('bookmarkMenuHideError') }, 4000);
|
||||||
|
|||||||
62
data/static/js/bits/bookmarks.js
Normal file
62
data/static/js/bits/bookmarks.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
export const b = {
|
||||||
|
init: 'restoreCollectionDetails restoreThreadDetails restorePostDetails',
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLLECTION_DETAILS_KEY = 'collectionsOpen';
|
||||||
|
const THREAD_DETAILS_KEY = 'threadsOpen';
|
||||||
|
const POST_DETAILS_KEY = 'postsOpen';
|
||||||
|
|
||||||
|
let collectionDetailsData = {};
|
||||||
|
let collectionThreadDetailsData = {};
|
||||||
|
let collectionPostDetailsData = {};
|
||||||
|
|
||||||
|
async function setDetailsData(obj, key, id, isOpen) {
|
||||||
|
obj[id] = isOpen;
|
||||||
|
await b.savePageData(obj, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function restoreCollectionDetails(_, __, el) {
|
||||||
|
collectionDetailsData = await b.loadPageData(COLLECTION_DETAILS_KEY, {});
|
||||||
|
el.open = collectionDetailsData[el.dataset.id] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setCollectionDetails(ev, sender, el) {
|
||||||
|
if (el !== sender) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.target !== el.querySelector('summary')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(!el.open);
|
||||||
|
await setDetailsData(collectionDetailsData, COLLECTION_DETAILS_KEY, el.dataset.id, !el.open);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function restoreThreadDetails(_, __, el) {
|
||||||
|
collectionThreadDetailsData = await b.loadPageData(THREAD_DETAILS_KEY, {});
|
||||||
|
el.open = collectionThreadDetailsData[el.dataset.id] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setThreadDetails(ev, sender, el) {
|
||||||
|
if (el !== sender) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.target !== el.querySelector('summary')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await setDetailsData(collectionThreadDetailsData, THREAD_DETAILS_KEY, el.dataset.id, !el.open);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function restorePostDetails(_, __, el) {
|
||||||
|
collectionPostDetailsData = await b.loadPageData(POST_DETAILS_KEY, {});
|
||||||
|
el.open = collectionPostDetailsData[el.dataset.id] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setPostDetails(ev, sender, el) {
|
||||||
|
if (el !== sender) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.target !== el.querySelector('summary')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await setDetailsData(collectionPostDetailsData, POST_DETAILS_KEY, el.dataset.id, !el.open);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user