- Remove reply
- Add comment
- Remove pinned item sidebar
- Revamp post UI
This commit is contained in:
Suraj Shetty 2018-10-15 11:46:47 +05:30
parent 7e95b1b4d4
commit fcedced4bf
13 changed files with 368 additions and 235 deletions

View file

@ -1,42 +1,42 @@
<template>
<div class="post-card">
<div class="post-body">
<div class="pull-right text-muted" v-html="post_time"></div>
<div
class="text-muted pull-right"
v-if="post.is_pinned || post.is_globally_pinned">
{{ post.is_globally_pinned ? 'Globally ': ''}} Pinned
</div>
<div class="user-avatar" v-html="user_avatar" @click="goto_profile(post.owner)"></div>
<div class="user-name text-muted" @click="goto_profile(post.owner)">{{ user_name }}</div>
<div class="user-name" @click="goto_profile(post.owner)">{{ user_name }}</div>
<div class="text-muted" v-html="post_time"></div>
<div class="content" v-html="post.content"></div>
</div>
<post-action
:is_globally_pinnable="is_globally_pinnable"
:is_pinnable="is_pinnable"
:is_globally_pinned="post.is_globally_pinned"
:is_pinned="post.is_pinned"
:liked_by="post.liked_by"
:reply_count="replies.length"
@toggle_reply="toggle_reply"
@new_reply="create_new_reply"
:comment_count="comments.length"
@toggle_comment="toggle_comment"
@toggle_like="toggle_like"
@toggle_global_pin="toggle_global_pin"
@toggle_pin="toggle_pin"
/>
<post-reply
class="post-reply"
v-if="show_replies"
:replies="replies"
/>
<post-comment
v-if="show_comments"
class="post-comments"
:comments="comments"
@create_comment="create_comment">
</post-comment>
</div>
</template>
<script>
import PostReply from './PostReply.vue';
import PostAction from './PostAction.vue';
import PostComment from './PostComment.vue';
frappe.provide('frappe.social');
const Post = {
export default {
props: ['post'],
components: {
PostAction,
PostReply
PostComment
},
mounted() {
this.$el.querySelectorAll('img').forEach((img) => {
@ -50,32 +50,32 @@ const Post = {
user_avatar: frappe.avatar(this.post.owner, 'avatar-medium'),
post_time: comment_when(this.post.creation),
user_name: frappe.user_info(this.post.owner).fullname,
reply_count: 0,
replies: [],
show_replies: false,
is_globally_pinnable: !this.post.reply_to && frappe.user_roles.includes('System Manager'),
is_pinnable: !this.post.reply_to
comment_count: 0,
comments: [],
show_comments: false,
is_globally_pinnable: !this.post.comment_to && frappe.user_roles.includes('System Manager'),
is_pinnable: !this.post.comment_to
&& frappe.session.user === this.post.owner
&& frappe.get_route()[2] === frappe.session.user
}
},
created() {
frappe.db.get_list('Post', {
fields: ['name', 'content', 'owner', 'creation', 'liked_by', 'is_globally_pinned', 'is_pinned', 'reply_to'],
frappe.db.get_list('Post Comment', {
fields: ['name', 'content', 'owner', 'creation'],
order_by: 'creation desc',
filters: {
reply_to: this.post.name
parent: this.post.name
}
}).then(replies => {
this.replies = replies;
}).then(comments => {
this.comments = comments;
})
if (!this.post.liked_by) {
this.$set(this.post, 'liked_by', '')
}
frappe.realtime.on('new_post_reply' + this.post.name, (post) => {
this.replies.push(post);
frappe.realtime.on('new_post_comment' + this.post.name, (comment) => {
this.comments = [comment].concat(this.comments);
})
frappe.realtime.on('toggle_global_pin' + this.post.name, (is_globally_pinned) => {
this.post.is_globally_pinned = cint(is_globally_pinned);
@ -88,16 +88,14 @@ const Post = {
this.$root.$on('user_image_updated', () => {
this.user_avatar = frappe.avatar(this.post.owner, 'avatar-medium')
})
},
methods: {
goto_profile(user) {
frappe.set_route('social', 'profile/' + user)
},
create_new_reply() {
frappe.social.post_dialog.open(__('Reply'), __('Reply'), this.post.name);
},
toggle_reply() {
this.show_replies = !this.show_replies
toggle_comment() {
this.show_comments = !this.show_comments
},
update_liked_by(liked_by) {
this.post.liked_by = liked_by;
@ -112,11 +110,21 @@ const Post = {
},
toggle_pin() {
frappe.db.set_value('Post', this.post.name, 'is_pinned', cint(!this.post.is_pinned))
},
create_comment(content) {
const comment = frappe.model.get_new_doc('Post Comment');
comment.content = content
comment.parent = this.post.name;
frappe.db.insert(comment);
}
}
}
frappe.social.Post = Post;
export default Post;
</script>
<style lang="less" scoped>
.post-comments {
padding: 10px 46px;
padding-top: 0px;
background: #F6F6F6;
}
</style>

View file

@ -2,25 +2,20 @@
<div class="post-action-container text-muted">
<div class="pin" v-if="is_pinnable">
<i class="fa fa-thumb-tack" :class="{'pinned': is_pinned}" @click="$emit('toggle_pin')"></i>
</div>
</div>
<div class="pin" v-else-if="is_globally_pinnable">
<i class="fa fa-thumb-tack" :class="{'pinned': is_globally_pinned}" @click="$emit('toggle_global_pin')"></i>
</div>
<div class="reply">
<i class="fa fa-reply" @click="$emit('new_reply')"></i>
<span @click="$emit('toggle_reply')">{{ reply_count }}</span>
<div class="like" :class="{'liked': post_liked}" @click="$emit('toggle_like')">
Like
</div>
<div class="like">
<i
class="fa fa-heart"
@click="$emit('toggle_like')"
:class="{'liked': post_liked}">
</i>
<span
class="likes"
:data-liked-by="JSON.stringify(split_string(liked_by))">
{{ like_count }}
</span>
<div class="comment" @click="$emit('toggle_comment')">
Comment
</div>
<div >
<span class="likes" :data-liked-by="JSON.stringify(split_string(liked_by))">{{ like_count }} likes</span>
.
<span>{{ comment_count }} comments</span>
</div>
</div>
</template>
@ -30,7 +25,7 @@ export default {
'liked_by': {
'type': String,
},
'reply_count': {
'comment_count': {
'type': Number,
'default': 0,
},
@ -68,12 +63,12 @@ export default {
</script>
<style lang='less' scoped>
.post-action-container {
clear: both;
display: flex;
justify-content: flex-end;
.reply, .like, .pin {
background-color: #F6F6F6;
padding: 10px;
.comment, .like, .pin {
padding-right: 20px;
cursor: pointer;
padding: 10px;
span {
padding-left: 5px;
}
@ -81,10 +76,13 @@ export default {
color: black;
}
}
.likes {
cursor: pointer;
}
.liked {
color: red;
color: #7F7FFF;
&:hover {
color: lighten(red, 20%) !important;
color: lighten(#7F7FFF, 10%) !important;
}
}
.pinned {

View file

@ -0,0 +1,63 @@
<template>
<div>
<div class="comment-box">
<div class="text-muted comment-label">Add a comment</div>
<textarea v-model="comment_content"></textarea>
<button class="pull-right btn btn-primary btn-sm" @click="$emit('create_comment', comment_content); comment_content = ''">Comment</button>
</div>
<div class="comment-list">
<div class="comment" v-for="comment in comments" :key="comment.name">
<span class="pull-right text-muted" v-html="get_time(comment.creation)"></span>
<span v-html="get_avatar(comment.owner)"></span>
<span>{{comment.content}}</span>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['comments'],
data() {
return {
comment_content: ''
}
},
methods: {
get_avatar(user) {
return frappe.avatar(user)
},
get_time(timestamp) {
return comment_when(timestamp)
},
}
}
</script>
<style lang="less" scoped>
.comment-box {
.comment-label {
margin-bottom: 5px;
}
textarea {
width: 100%;
border-radius: 4px;
outline: none;
border: none;
margin-bottom: 5px;
clear: both;
height: 40px;
padding: 5px;
border: 1px solid #d1d8dd;
}
button {
padding: 2px 5px;
font-size: 10px;
}
}
.comment-list {
margin-top: 30px;
clear: both;
.comment {
padding: 5px 0;
}
}
</style>

View file

@ -28,12 +28,7 @@
</div>
<post :post="post" v-for="post in current_list" :key="post.name"/>
</div>
<div class="pinned-posts hidden-xs">
<div class="muted-title padding"><i class="fa fa-thumb-tack">&nbsp;</i> Pinned Posts </div>
<div v-for="post in pinned_posts" :key="post.name">
<post :post="post"></post>
</div>
</div>
<div class="right-sidebar"></div>
</div>
</div>
</template>
@ -56,15 +51,9 @@ export default {
}
},
computed: {
pinned_posts() {
return this.user_posts.filter(post => post.is_pinned)
},
other_posts() {
return this.user_posts.filter(post => !post.is_pinned)
},
current_list() {
if(this.show_list == 'user_posts') {
return this.other_posts;
return this.user_posts;
} else {
return this.liked_posts;
}
@ -105,7 +94,7 @@ export default {
.profile-sidebar {
margin-top: 50px;
}
.pinned-posts {
.right-sidebar {
margin-top: 5px;
}
.list-options {

View file

@ -6,17 +6,18 @@
{{ new_posts_count + ' new post'}}
</div>
<div v-for="post in user_posts" :key="post.name">
<post v-if="post.type == 'post' && !post.is_globally_pinned" :post="post"></post>
<event-card v-else :event="post"></event-card>
<post :post="post"></post>
</div>
<div v-if="!user_posts.length" class="no-post text-center text-muted">
No Posts Yet !
<div class="icon">
<i class="fa fa-frown-o"></i>
</div>
</div>
<div v-show="loading_old_posts" class="text-center padding">Loading old posts</div>
<div v-show="!more_posts_available" class="text-center padding">That's all folks</div>
</div>
<div class="pinned-posts hidden-xs">
<div class="muted-title padding"><i class="fa fa-thumb-tack">&nbsp;</i> Pinned Posts </div>
<div v-for="post in pinned_posts" :key="post.name">
<post :post="post"></post>
</div>
<div class="right-sidebar hidden-xs">
</div>
</div>
</template>
@ -56,7 +57,7 @@ export default {
return this.posts.filter((post) => post.is_globally_pinned)
},
user_posts() {
return this.posts.filter((post) => !post.is_globally_pinned)
return this.posts;
}
},
methods: {

View file

@ -25,46 +25,31 @@ frappe.social.Home = class SocialHome {
}
set_primary_action() {
this.page.set_primary_action(__('Post'), () => {
frappe.social.post_dialog.open();
frappe.social.post_dialog.show();
});
}
};
frappe.social.post_dialog = {
open(title=__('Create Post'), button_label=__('Post'), reply_to=null) {
const d = new frappe.ui.Dialog({
title,
fields: [
{
fieldtype: "Text Editor",
fieldname: "content",
label: __("Content"),
reqd: 1
},
{
fieldtype: "Link",
fieldname: "reply_to",
label: __("Reply"),
hidden: 1,
default: reply_to
}
],
primary_action_label: title,
primary_action: (values) => {
const post = frappe.model.get_new_doc('Post');
post.content = values.content;
if (values.reply_to) {
post.reply_to = values.reply_to;
}
frappe.db.insert(post).then(() => {
d.hide();
});
}
frappe.social.post_dialog = new frappe.ui.Dialog({
title: __('Create Post'),
fields: [
{
fieldtype: "Text Editor",
fieldname: "content",
label: __("Content"),
reqd: 1
}
],
primary_action_label: __('Post'),
primary_action: (values) => {
const post = frappe.model.get_new_doc('Post');
post.content = values.content;
frappe.db.insert(post).then(() => {
frappe.social.post_dialog.clear();
frappe.social.post_dialog.hide();
});
d.show();
}
};
});
frappe.social.update_user_image = new frappe.ui.Dialog({
title: __("User Image"),
@ -78,8 +63,7 @@ frappe.social.update_user_image = new frappe.ui.Dialog({
},
],
primary_action_label: __('Set Image'),
primary_action: () => {
const values = frappe.social.update_user_image.get_values();
primary_action: (values) => {
frappe.db.set_value('User', frappe.session.user, 'user_image', values.image)
.then((resp) => {
frappe.boot.user_info[frappe.session.user].image = resp.message.user_image;

View file

@ -2,6 +2,10 @@
@import (reference) 'common.less';
body[data-route*="social"] {
.page-container {
background: #FAFBFC;
}
.layout-main-section {
border: none;
}
@ -16,15 +20,18 @@ body[data-route*="social"] {
.wall-container {
display: flex;
font-size: 12px;
.no-post {
.icon {
font-size: 40px;
}
}
.post-sidebar {
flex: 20%
}
.post-container {
width: 500px;
flex: 45%;
display: flex;
margin: 0 20px;
margin-top: 24px;
padding-top: 10px;
padding: 20px;
flex-direction: column;
.new_posts_count {
cursor: pointer;
@ -33,7 +40,7 @@ body[data-route*="social"] {
margin-bottom: 10px;
}
}
.pinned-posts {
.right-sidebar {
padding: 0px;
flex: 35%
}
@ -47,27 +54,25 @@ body[data-route*="social"] {
.post-container {
flex: 45%
}
.pinned-posts {
.right-sidebar {
padding: 0px;
flex: 35%
}
}
.generic-card() {
background: white;
font-size: 12px;
margin-bottom: 10px;
max-width: 500px;
max-height: 500px;
min-height: 70px;
overflow: scroll;
border: 1px solid @border-color;
border-radius: 3px;
padding: 10px 10px 5px 10px;
border-radius: 4px;
.content {
font-size: 14px;
img, iframe {
border-radius: 5px;
border: none;
border: 1px solid @light-border-color;
margin: 5px 0;
}
img {
@ -79,41 +84,28 @@ body[data-route*="social"] {
}
}
.event-card {
.generic-card();
.content {
text-align: center;
clear: both;
}
}
.post-card {
.generic-card();
.user-name {
font-weight: 400;
}
.user-avatar {
float: left;
margin-right: 5px;
.avatar {
width: 48px;
height: 48px;
.post-body {
padding: 10px;
.user-name {
font-weight: 500;
}
.avatar-frame, .standard-image {
border-radius: 50%;
.user-avatar {
float: left;
margin-right: 5px;
}
.user-avatar, .user-name {
cursor: pointer;
}
.content {
margin: 10px 0 0 46px;
}
}
.user-name {
margin-bottom: 5px;
}
.user-avatar, .user-name {
cursor: pointer;
}
.content {
margin-left: 58px;
}
.post-reply {
margin-left: 20px;
.post-action-container {
padding-left: 46px;
background-color: #F6F6F6;
border-top: 1px solid @border-color;
}
}

View file

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -51,8 +52,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reply_to",
"fieldtype": "Link",
"fieldname": "comments",
"fieldtype": "Table",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -60,43 +61,10 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reply To",
"label": "comments",
"length": 0,
"no_copy": 0,
"options": "Post",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Type",
"length": 0,
"no_copy": 0,
"options": "post\nevent",
"options": "Post Comment",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -142,38 +110,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "is_globally_pinned",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Globally Pinned ",
"length": 0,
"no_copy": 0,
"permlevel": 1,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -205,6 +141,38 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "is_globally_pinned",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Globally Pinned",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@ -217,7 +185,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-05 10:56:45.871676",
"modified": "2018-10-14 10:20:35.430108",
"modified_by": "Administrator",
"module": "Social",
"name": "Post",

View file

@ -15,10 +15,7 @@ class Post(Document):
def after_insert(self):
if self.reply_to:
frappe.publish_realtime('new_post_reply' + self.reply_to, self, after_commit=True)
else:
frappe.publish_realtime('new_post', self.owner, after_commit=True)
frappe.publish_realtime('new_post', self.owner, after_commit=True)
@frappe.whitelist()
def get_profile_data(post_user):
@ -30,7 +27,7 @@ def get_profile_data(post_user):
user_post = frappe.db.get_list(
'Post',
fields=['name', 'content', 'owner', 'creation', 'liked_by', 'is_pinned'],
filters={"owner":['like',post_user]}
filters={"owner":['like', post_user]}
)
return {
'liked_posts': liked_post,

View file

@ -0,0 +1,8 @@
// Copyright (c) 2018, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Post Comment', {
refresh: function(frm) {
}
});

View file

@ -0,0 +1,114 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-10-14 10:16:22.852930",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "content",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Content",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-15 07:46:12.504396",
"modified_by": "Administrator",
"module": "Social",
"name": "Post Comment",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class PostComment(Document):
def after_insert(self):
frappe.publish_realtime('new_post_comment' + self.parent, self, after_commit=True)