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/lib/transform-post.js.es6
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

259 lines
8.6 KiB
JavaScript

import { isEmpty } from "@ember/utils";
import { userPath } from "discourse/lib/url";
const _additionalAttributes = [];
export function includeAttributes(...attributes) {
attributes.forEach(a => _additionalAttributes.push(a));
}
export function transformBasicPost(post) {
// Note: it can be dangerous to not use `get` in Ember code, but this is significantly
// faster and has tests to confirm it works. We only call `get` when the property is a CP
const postAtts = {
id: post.id,
hidden: post.hidden,
deleted: post.get("deleted"),
deleted_at: post.deleted_at,
user_deleted: post.user_deleted,
isDeleted: post.deleted_at || post.user_deleted, // xxxxx
deletedByAvatarTemplate: null,
deletedByUsername: null,
primary_group_name: post.primary_group_name,
primary_group_flair_url: post.primary_group_flair_url,
primary_group_flair_bg_color: post.primary_group_flair_bg_color,
primary_group_flair_color: post.primary_group_flair_color,
wiki: post.wiki,
lastWikiEdit: post.last_wiki_edit,
firstPost: post.post_number === 1,
post_number: post.post_number,
cooked: post.cooked,
via_email: post.via_email,
isAutoGenerated: post.is_auto_generated,
user_id: post.user_id,
usernameUrl: userPath(post.username),
username: post.username,
avatar_template: post.avatar_template,
bookmarked: post.bookmarked,
bookmarkedWithReminder: post.bookmarked_with_reminder,
bookmarkReminderAt: post.bookmark_reminder_at,
yours: post.yours,
shareUrl: post.get("shareUrl"),
staff: post.staff,
admin: post.admin,
moderator: post.moderator,
new_user: post.trust_level === 0,
name: post.name,
user_title: post.user_title,
created_at: post.created_at,
updated_at: post.updated_at,
canDelete: post.can_delete,
showFlagDelete: false,
canRecover: post.can_recover,
canEdit: post.can_edit,
canFlag: !isEmpty(post.get("flagsAvailable")),
canReviewTopic: false,
reviewableId: post.reviewable_id,
reviewableScoreCount: post.reviewable_score_count,
reviewableScorePendingCount: post.reviewable_score_pending_count,
version: post.version,
canRecoverTopic: false,
canDeleteTopic: false,
canViewEditHistory: post.can_view_edit_history,
canWiki: post.can_wiki,
showLike: false,
liked: false,
canToggleLike: false,
likeCount: false,
actionsSummary: null,
read: post.read,
replyToUsername: null,
replyToAvatarTemplate: null,
reply_to_post_number: post.reply_to_post_number,
cooked_hidden: !!post.cooked_hidden,
expandablePost: false,
replyCount: post.reply_count,
locked: post.locked,
userCustomFields: post.user_custom_fields,
readCount: post.readers_count
};
_additionalAttributes.forEach(a => (postAtts[a] = post[a]));
return postAtts;
}
export default function transformPost(
currentUser,
site,
post,
prevPost,
nextPost
) {
// Note: it can be dangerous to not use `get` in Ember code, but this is significantly
// faster and has tests to confirm it works. We only call `get` when the property is a CP
const postType = post.post_type;
const postTypes = site.post_types;
const topic = post.topic;
const details = topic.get("details");
const postAtts = transformBasicPost(post);
const createdBy = details.created_by || {};
postAtts.topicId = topic.id;
postAtts.topicOwner = createdBy.id === post.user_id;
postAtts.topicCreatedById = createdBy.id;
postAtts.post_type = postType;
postAtts.via_email = post.via_email;
postAtts.isAutoGenerated = post.is_auto_generated;
postAtts.isModeratorAction = postType === postTypes.moderator_action;
postAtts.isWhisper = postType === postTypes.whisper;
postAtts.isSmallAction =
postType === postTypes.small_action || post.action_code === "split_topic";
postAtts.canBookmark = !!currentUser;
postAtts.canManage = currentUser && currentUser.get("canManageTopic");
postAtts.canViewRawEmail =
currentUser && (currentUser.id === post.user_id || currentUser.staff);
postAtts.canReplyAsNewTopic = details.can_reply_as_new_topic;
postAtts.canReviewTopic = !!details.can_review_topic;
postAtts.isWarning = topic.is_warning;
postAtts.links = post.get("internalLinks");
postAtts.replyDirectlyBelow =
nextPost && nextPost.reply_to_post_number === post.post_number;
postAtts.replyDirectlyAbove =
prevPost && post.reply_to_post_number === prevPost.post_number;
postAtts.linkCounts = post.link_counts;
postAtts.actionCode = post.action_code;
postAtts.actionCodeWho = post.action_code_who;
postAtts.topicUrl = topic.get("url");
postAtts.isSaving = post.isSaving;
if (post.notice_type) {
postAtts.noticeType = post.notice_type;
if (postAtts.noticeType === "custom") {
postAtts.noticeMessage = post.notice_args;
} else if (postAtts.noticeType === "returning_user") {
postAtts.noticeTime = new Date(post.notice_args);
}
}
const showPMMap =
topic.archetype === "private_message" && post.post_number === 1;
if (showPMMap) {
postAtts.showPMMap = true;
postAtts.allowedGroups = details.allowed_groups;
postAtts.allowedUsers = details.allowed_users;
postAtts.canRemoveAllowedUsers = details.can_remove_allowed_users;
postAtts.canRemoveSelfId = details.can_remove_self_id;
postAtts.canInvite = details.can_invite_to;
}
const showTopicMap =
showPMMap ||
(post.post_number === 1 &&
topic.archetype === "regular" &&
topic.posts_count > 1);
if (showTopicMap) {
postAtts.showTopicMap = true;
postAtts.topicCreatedAt = topic.created_at;
postAtts.createdByUsername = createdBy.username;
postAtts.createdByAvatarTemplate = createdBy.avatar_template;
postAtts.createdByName = createdBy.name;
postAtts.lastPostUrl = topic.get("lastPostUrl");
postAtts.lastPostUsername = details.last_poster.username;
postAtts.lastPostAvatarTemplate = details.last_poster.avatar_template;
postAtts.lastPostName = details.last_poster.name;
postAtts.lastPostAt = topic.last_posted_at;
postAtts.topicReplyCount = topic.get("replyCount");
postAtts.topicViews = topic.views;
postAtts.topicViewsHeat = topic.get("viewsHeat");
postAtts.participantCount = topic.participant_count;
postAtts.topicLikeCount = topic.like_count;
postAtts.topicLinks = details.links;
if (postAtts.topicLinks) {
postAtts.topicLinkLength = details.links.length;
}
postAtts.topicPostsCount = topic.posts_count;
postAtts.participants = details.participants;
const postStream = topic.get("postStream");
postAtts.userFilters = postStream.userFilters;
postAtts.topicSummaryEnabled = postStream.summary;
postAtts.topicWordCount = topic.word_count;
postAtts.hasTopicSummary = topic.has_summary;
}
if (postAtts.isDeleted) {
postAtts.deletedByAvatarTemplate = post.get(
"postDeletedBy.avatar_template"
);
postAtts.deletedByUsername = post.get("postDeletedBy.username");
}
const replyToUser = post.get("reply_to_user");
if (replyToUser) {
postAtts.replyToUsername = replyToUser.username;
postAtts.replyToAvatarTemplate = replyToUser.avatar_template;
}
if (post.actions_summary) {
postAtts.actionsSummary = post.actions_summary
.filter(a => {
return a.actionType.name_key !== "like" && a.acted;
})
.map(a => {
const action = a.actionType.name_key;
return {
id: a.id,
postId: post.id,
action,
canUndo: a.can_undo,
description: I18n.t(`post.actions.by_you.${action}`)
};
});
}
const likeAction = post.likeAction;
if (likeAction) {
postAtts.liked = likeAction.acted;
postAtts.canToggleLike = likeAction.get("canToggle");
postAtts.showLike = postAtts.liked || postAtts.canToggleLike;
postAtts.likeCount = likeAction.count;
}
if (!currentUser) {
postAtts.showLike = !topic.archived;
}
if (postAtts.post_number === 1) {
postAtts.canRecoverTopic = postAtts.isDeleted && details.can_recover;
postAtts.canDeleteTopic = !postAtts.isDeleted && details.can_delete;
postAtts.expandablePost = topic.expandable_first_post;
// Show a "Flag to delete" message if not staff and you can't
// otherwise delete it.
postAtts.showFlagDelete =
!postAtts.canDelete &&
postAtts.yours &&
currentUser &&
!currentUser.staff;
} else {
postAtts.canRecover = postAtts.isDeleted && postAtts.canRecover;
postAtts.canDelete =
postAtts.canDelete &&
!post.deleted_at &&
currentUser &&
(currentUser.staff || !post.user_deleted);
}
_additionalAttributes.forEach(a => (postAtts[a] = post[a]));
return postAtts;
}