diff --git a/funnel/assets/js/update.js b/funnel/assets/js/update.js index fd0ca04cd..11e673bb7 100644 --- a/funnel/assets/js/update.js +++ b/funnel/assets/js/update.js @@ -1,9 +1,11 @@ import Vue from 'vue/dist/vue.min'; import VS2 from 'vue-script2'; +import toastr from 'toastr'; import Utils from './utils/helper'; import ScrollHelper from './utils/scrollhelper'; import getTimeago from './utils/get_timeago'; import { userAvatarUI, faSvg, shareDropdown } from './utils/vue_util'; +import Form from './utils/formhelper'; const Updates = { init({ @@ -45,6 +47,23 @@ const Updates = { this.setReadMore = action; this.truncated = action; }, + getCsrfToken() { + return $('meta[name="csrf-token"]').attr('content'); + }, + handlePinEvent(event, formId, postUrl) { + event.preventDefault(); + const onSuccess = (response) => { + this.update.is_pinned = !this.update.is_pinned; + Form.updateFormNonce(response); + }; + + const onError = (error) => { + const errorMsg = Form.handleAjaxError(error); + toastr.error(errorMsg); + }; + + Form.ajaxFormSubmit(formId, postUrl, onSuccess, onError, {}); + }, }, computed: { age() { diff --git a/funnel/assets/sass/pages/update.scss b/funnel/assets/sass/pages/update.scss index c6d2002cc..3ebcb3a86 100644 --- a/funnel/assets/sass/pages/update.scss +++ b/funnel/assets/sass/pages/update.scss @@ -36,6 +36,12 @@ margin: $mui-grid-padding * 0.5 0; position: relative; + .update__heading { + .update__heading__unpin { + display: none; + } + } + .update__content { display: inline-block; width: 100%; @@ -47,6 +53,12 @@ } } +.update:hover { + .update__heading__unpin { + display: inline-block; + } +} + .update--border { padding: $mui-grid-padding * 0.5 $mui-grid-padding 0; border-radius: 4px; diff --git a/funnel/forms/__init__.py b/funnel/forms/__init__.py index 55a2c3cdf..8f1ec1237 100644 --- a/funnel/forms/__init__.py +++ b/funnel/forms/__init__.py @@ -130,7 +130,7 @@ TicketParticipantForm, TicketTypeForm, ) -from .update import UpdateForm +from .update import UpdateForm, UpdatePinForm from .venue import VenueForm, VenuePrimaryForm, VenueRoomForm __all__ = [ @@ -214,6 +214,7 @@ "TicketTypeForm", "UnsubscribeForm", "UpdateForm", + "UpdatePinForm", "UserPermissionAssignForm", "UsernameAvailableForm", "VenueForm", diff --git a/funnel/forms/update.py b/funnel/forms/update.py index 15e121c27..9fcb4b2cd 100644 --- a/funnel/forms/update.py +++ b/funnel/forms/update.py @@ -6,7 +6,7 @@ from ..models import Update -__all__ = ['UpdateForm'] +__all__ = ['UpdateForm', 'UpdatePinForm'] @Update.forms('main') @@ -29,3 +29,12 @@ class UpdateForm(forms.Form): is_restricted = forms.BooleanField( __("Limit access to current participants only"), default=False ) + + +@Update.forms('pin') +class UpdatePinForm(forms.Form): + """Pin an update in a project.""" + + is_pinned = forms.BooleanField( + __("Pin this update above other updates"), default=False + ) diff --git a/funnel/templates/js/update.js.jinja2 b/funnel/templates/js/update.js.jinja2 index b09d18aef..30b4b30c5 100644 --- a/funnel/templates/js/update.js.jinja2 +++ b/funnel/templates/js/update.js.jinja2 @@ -17,14 +17,28 @@
{{ gettext('Draft') }}
diff --git a/funnel/views/update.py b/funnel/views/update.py index 82526e700..8b493047d 100644 --- a/funnel/views/update.py +++ b/funnel/views/update.py @@ -2,7 +2,7 @@ from __future__ import annotations -from flask import abort, flash +from flask import abort, flash, request from baseframe import _, forms from baseframe.forms import render_form @@ -18,7 +18,7 @@ from .. import app from ..auth import current_auth -from ..forms import SavedProjectForm, UpdateForm +from ..forms import SavedProjectForm, UpdateForm, UpdatePinForm from ..models import Account, NewUpdateNotification, Project, Update, db from ..typing import ReturnRenderWith, ReturnView from .helpers import html_in_json, render_redirect @@ -139,13 +139,27 @@ def publish(self) -> ReturnView: @route('edit', methods=['GET', 'POST']) @requires_roles({'editor'}) def edit(self) -> ReturnView: - form = UpdateForm(obj=self.obj) + if request.form.get('form.id') == 'pin': + form = UpdatePinForm(obj=self.obj) + else: + form = UpdateForm(obj=self.obj) if form.validate_on_submit(): form.populate_obj(self.obj) db.session.commit() + if request.form.get('form.id') == 'pin': + return {'status': 'ok', 'is_pinned': self.obj.is_pinned} flash(_("The update has been edited"), 'success') return render_redirect(self.obj.url_for()) - + if request.form.get('form.id') == 'pin': + return ( + { + 'status': 'error', + 'error': 'pin_form_invalid', + 'error_description': _("This page timed out. Reload and try again"), + 'form_nonce': form.form_nonce.data, + }, + 400, + ) return render_form( form=form, title=_("Edit update"),