This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/templates/topic.hbs
Martin Brennan 6261339da9
Improving bookmarks part 1 (#8466)
Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated!

This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality:

* We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them.
* Posts now have a new button in their actions menu that has the icon of an actual book
* Clicking the button opens the new bookmark modal.
* Both name and the reminder type are optional.
* If you close the modal without doing anything, the bookmark is saved with no reminder.
* If you click the Cancel button, no bookmark is saved at all.
* All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you).
* If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark.
* A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked.

This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-11 14:04:02 +10:00

376 lines
17 KiB
Handlebars

{{#discourse-topic multiSelect=multiSelect enteredAt=enteredAt topic=model hasScrolled=hasScrolled}}
{{#if model}}
{{add-category-tag-classes category=model.category tags=model.tags}}
<div class="container">
{{discourse-banner user=currentUser banner=site.banner overlay=hasScrolled hide=model.errorLoading}}
</div>
{{/if}}
{{#if showSharedDraftControls}}
{{shared-draft-controls topic=model}}
{{/if}}
{{plugin-outlet name="topic-above-post-stream" args=(hash model=model)}}
{{#if model.postStream.loaded}}
{{#if model.postStream.firstPostPresent}}
{{#topic-title cancelled=(action "cancelEditingTopic") save=(action "finishedEditingTopic") model=model}}
{{#if editingTopic}}
<div class="edit-topic-title">
{{#if model.isPrivateMessage}}
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
{{/if}}
{{text-field id="edit-title" value=buffered.title maxlength=siteSettings.max_topic_title_length autofocus="true"}}
{{#if showCategoryChooser}}
{{category-chooser
class="small"
value=(unbound buffered.category_id)
onSelectAny=(action "topicCategoryChanged")}}
{{/if}}
{{#if canEditTags}}
{{mini-tag-chooser
filterable=true
tags=(unbound buffered.tags)
categoryId=(unbound buffered.category_id)
onChangeTags=(action "topicTagsChanged")}}
{{/if}}
{{plugin-outlet name="edit-topic" args=(hash model=model buffered=buffered)}}
<div class="edit-controls">
{{d-button action=(action "finishedEditingTopic") class="btn-primary submit-edit" icon="check"}}
{{d-button action=(action "cancelEditingTopic") class="btn-default cancel-edit" icon="times"}}
{{#if canRemoveTopicFeaturedLink}}
<a href {{action "removeFeaturedLink"}} class="remove-featured-link" title="{{i18n "composer.remove_featured_link"}}">
{{d-icon "times-circle"}}
{{featuredLinkDomain}}
</a>
{{/if}}
</div>
</div>
{{else}}
<h1 data-topic-id="{{unbound model.id}}">
{{#unless model.is_warning}}
{{#if siteSettings.enable_personal_messages}}
{{#if model.isPrivateMessage}}
<a href={{pmPath}} title="{{i18n 'topic_statuses.personal_message.title'}}" aria-label={{i18n 'user.messages.inbox'}}>
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
</a>
{{/if}}
{{else}}
{{#if model.isPrivateMessage}}
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
{{/if}}
{{/if}}
{{/unless}}
{{#if model.details.loaded}}
{{topic-status topic=model}}
<a href="{{unbound model.url}}" {{action "jumpTop"}} class="fancy-title">
{{{model.fancyTitle}}}
</a>
{{/if}}
{{#if model.details.can_edit}}
<a href {{action "editTopic"}} class="edit-topic" title="{{i18n "edit"}}">{{d-icon "pencil-alt"}}</a>
{{/if}}
</h1>
{{topic-category topic=model class="topic-category"}}
{{/if}}
{{/topic-title}}
{{/if}}
<div class="container posts">
<div class='selected-posts {{unless multiSelect 'hidden'}}'>
{{partial "selected-posts"}}
</div>
{{#topic-navigation topic=model jumpToDate=(action "jumpToDate") jumpToIndex=(action "jumpToIndex") as |info|}}
{{#if info.renderTimeline}}
{{#if info.renderAdminMenuButton}}
{{topic-admin-menu-button
topic=model
fixed="true"
toggleMultiSelect=(action "toggleMultiSelect")
deleteTopic=(action "deleteTopic")
recoverTopic=(action "recoverTopic")
toggleClosed=(action "toggleClosed")
toggleArchived=(action "toggleArchived")
toggleVisibility=(action "toggleVisibility")
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
showFeatureTopic=(route-action "showFeatureTopic")
showChangeTimestamp=(route-action "showChangeTimestamp")
resetBumpDate=(action "resetBumpDate")
convertToPublicTopic=(action "convertToPublicTopic")
convertToPrivateMessage=(action "convertToPrivateMessage")}}
{{/if}}
{{topic-timeline
topic=model
notificationLevel=model.details.notification_level
prevEvent=info.prevEvent
fullscreen=info.topicProgressExpanded
enteredIndex=enteredIndex
loading=model.postStream.loading
jumpToPost=(action "jumpToPost")
jumpTop=(action "jumpTop")
jumpBottom=(action "jumpBottom")
jumpEnd=(action "jumpEnd")
jumpToPostPrompt=(action "jumpToPostPrompt")
jumpToIndex=(action "jumpToIndex")
replyToPost=(action "replyToPost")
toggleMultiSelect=(action "toggleMultiSelect")
deleteTopic=(action "deleteTopic")
recoverTopic=(action "recoverTopic")
toggleClosed=(action "toggleClosed")
toggleArchived=(action "toggleArchived")
toggleVisibility=(action "toggleVisibility")
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
showFeatureTopic=(route-action "showFeatureTopic")
showChangeTimestamp=(route-action "showChangeTimestamp")
resetBumpDate=(action "resetBumpDate")
convertToPublicTopic=(action "convertToPublicTopic")
convertToPrivateMessage=(action "convertToPrivateMessage")}}
{{else}}
{{#topic-progress
prevEvent=info.prevEvent
topic=model
expanded=info.topicProgressExpanded
jumpToPost=(action "jumpToPost")}}
{{plugin-outlet name="before-topic-progress" args=(hash model=model jumpToPost=(action "jumpToPost"))}}
{{#if info.renderAdminMenuButton}}
{{topic-admin-menu-button
topic=model
openUpwards="true"
rightSide="true"
toggleMultiSelect=(action "toggleMultiSelect")
deleteTopic=(action "deleteTopic")
recoverTopic=(action "recoverTopic")
toggleClosed=(action "toggleClosed")
toggleArchived=(action "toggleArchived")
toggleVisibility=(action "toggleVisibility")
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
showFeatureTopic=(route-action "showFeatureTopic")
showChangeTimestamp=(route-action "showChangeTimestamp")
resetBumpDate=(action "resetBumpDate")
convertToPublicTopic=(action "convertToPublicTopic")
convertToPrivateMessage=(action "convertToPrivateMessage")}}
{{/if}}
{{/topic-progress}}
{{/if}}
{{/topic-navigation}}
<div class="row">
<section class="topic-area" id="topic" data-topic-id="{{unbound model.id}}">
<div class="posts-wrapper">
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
{{plugin-outlet name="topic-above-posts" args=(hash model=model)}}
{{#unless model.postStream.loadingFilter}}
{{scrolling-post-stream
posts=postsToRender
canCreatePost=model.details.can_create_post
multiSelect=multiSelect
selectedPostsCount=selectedPostsCount
selectedQuery=selectedQuery
gaps=model.postStream.gaps
showReadIndicator=model.show_read_indicator
showFlags=(action "showPostFlags")
editPost=(action "editPost")
showHistory=(route-action "showHistory")
showLogin=(route-action "showLogin")
showRawEmail=(route-action "showRawEmail")
deletePost=(action "deletePost")
recoverPost=(action "recoverPost")
expandHidden=(action "expandHidden")
newTopicAction=(action "replyAsNewTopic")
toggleBookmark=(action "toggleBookmark")
toggleBookmarkWithReminder=(action "toggleBookmarkWithReminder")
togglePostType=(action "togglePostType")
rebakePost=(action "rebakePost")
changePostOwner=(action "changePostOwner")
grantBadge=(action "grantBadge")
addNotice=(action "addNotice")
removeNotice=(action "removeNotice")
lockPost=(action "lockPost")
unlockPost=(action "unlockPost")
unhidePost=(action "unhidePost")
replyToPost=(action "replyToPost")
toggleWiki=(action "toggleWiki")
toggleSummary=(action "toggleSummary")
removeAllowedUser=(action "removeAllowedUser")
removeAllowedGroup=(action "removeAllowedGroup")
topVisibleChanged=(action "topVisibleChanged")
currentPostChanged=(action "currentPostChanged")
currentPostScrolled=(action "currentPostScrolled")
bottomVisibleChanged=(action "bottomVisibleChanged")
togglePostSelection=(action "togglePostSelection")
selectReplies=(action "selectReplies")
selectBelow=(action "selectBelow")
fillGapBefore=(action "fillGapBefore")
fillGapAfter=(action "fillGapAfter")
showInvite=(route-action "showInvite")}}
{{/unless}}
{{conditional-loading-spinner condition=model.postStream.loadingBelow}}
</div>
<div id="topic-bottom"></div>
{{#conditional-loading-spinner condition=model.postStream.loadingFilter}}
{{#if loadedAllPosts}}
{{#if model.pending_posts}}
<div class='pending-posts'>
{{#each model.pending_posts as |pending|}}
<div class='reviewable-item'>
<div class='reviewable-meta-data'>
<span class='reviewable-type'>
{{i18n "review.awaiting_approval"}}
</span>
<span class='created-at'>
{{age-with-tooltip pending.created_at}}
</span>
</div>
<div class='post-contents-wrapper'>
{{reviewable-created-by user=currentUser tagName=''}}
<div class='post-contents'>
{{reviewable-created-by-name user=currentUser tagName=''}}
<div class='post-body'>{{cook-text pending.raw}}</div>
</div>
</div>
<div class='reviewable-actions'>
{{d-button
class="btn-danger"
label="review.delete"
icon="trash-alt"
action=(action "deletePending" pending) }}
</div>
</div>
{{/each}}
</div>
{{/if}}
{{#if model.queued_posts_count}}
<div class="has-pending-posts">
<div>
{{{i18n "review.topic_has_pending" count=model.queued_posts_count}}}
</div>
{{#link-to 'review' (query-params topic_id=model.id type="ReviewableQueuedPost" status="pending")}}
{{i18n "review.view_pending"}}
{{/link-to}}
</div>
{{/if}}
{{#if model.private_topic_timer.execute_at}}
{{topic-timer-info
topicClosed=model.closed
statusType=model.private_topic_timer.status_type
executeAt=model.private_topic_timer.execute_at
duration=model.private_topic_timer.duration
removeTopicTimer=(action "removeTopicTimer" model.private_topic_timer.status_type "private_topic_timer")}}
{{/if}}
{{topic-timer-info
topicClosed=model.closed
statusType=model.topic_timer.status_type
executeAt=model.topic_timer.execute_at
basedOnLastPost=model.topic_timer.based_on_last_post
duration=model.topic_timer.duration
categoryId=model.topic_timer.category_id
removeTopicTimer=(action "removeTopicTimer" model.topic_timer.status_type "topic_timer")}}
{{#if session.showSignupCta}}
{{! replace "Log In to Reply" with the infobox }}
{{signup-cta}}
{{else}}
{{#if currentUser}}
{{plugin-outlet name="topic-above-footer-buttons" args=(hash model=model)}}
{{topic-footer-buttons
topic=model
toggleMultiSelect=(action "toggleMultiSelect")
deleteTopic=(action "deleteTopic")
recoverTopic=(action "recoverTopic")
toggleClosed=(action "toggleClosed")
toggleArchived=(action "toggleArchived")
toggleVisibility=(action "toggleVisibility")
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
showFeatureTopic=(route-action "showFeatureTopic")
showChangeTimestamp=(route-action "showChangeTimestamp")
resetBumpDate=(action "resetBumpDate")
convertToPublicTopic=(action "convertToPublicTopic")
convertToPrivateMessage=(action "convertToPrivateMessage")
toggleBookmark=(action "toggleBookmark")
showFlagTopic=(route-action "showFlagTopic")
toggleArchiveMessage=(action "toggleArchiveMessage")
editFirstPost=(action "editFirstPost")
deferTopic=(action "deferTopic")
replyToPost=(action "replyToPost")
toggleFeaturedOnProfile=(action "toggleFeaturedOnProfile")}}
{{else}}
<div id="topic-footer-buttons">
{{d-button icon="reply" class="btn-primary pull-right" action=(route-action "showLogin") label="topic.reply.title"}}
</div>
{{/if}}
{{/if}}
{{#if showSelectedPostsAtBottom}}
<div class='selected-posts {{unless multiSelect 'hidden'}}'>
{{partial "selected-posts"}}
</div>
{{/if}}
{{plugin-outlet name="topic-above-suggested" args=(hash model=model)}}
<div class="{{if model.relatedMessages.length 'related-messages-wrapper'}} {{if model.suggestedTopics.length 'suggested-topics-wrapper'}}">
{{#if model.relatedMessages.length}}
{{related-messages topic=model}}
{{/if}}
{{#if model.suggestedTopics.length}}
{{suggested-topics topic=model}}
{{/if}}
</div>
{{/if}}
{{/conditional-loading-spinner}}
</section>
</div>
</div>
{{else}}
<div class="container">
{{#conditional-loading-spinner condition=noErrorYet}}
{{#if model.errorHtml}}
<div class="not-found">{{{model.errorHtml}}}</div>
{{else}}
<div class="topic-error">
<div>{{model.errorMessage}}</div>
{{#if model.noRetry}}
{{#unless currentUser}}
{{d-button action=(route-action "showLogin") class="btn-primary topic-retry" icon="user" label="log_in"}}
{{/unless}}
{{else}}
{{d-button action=(action "retryLoading") class="btn-primary topic-retry" icon="sync" label="errors.buttons.again"}}
{{/if}}
</div>
{{conditional-loading-spinner condition=retrying}}
{{/if}}
{{/conditional-loading-spinner}}
</div>
{{/if}}
{{share-popup topic=model replyAsNewTopic=(action "replyAsNewTopic")}}
{{#if embedQuoteButton}}
{{quote-button quoteState=quoteState selectText=(action "selectText")}}
{{/if}}
{{/discourse-topic}}