New Chat RoomView Layout
This commit is contained in:
parent
27e5cf6967
commit
b04e3cce42
10 changed files with 591 additions and 415 deletions
|
|
@ -18,7 +18,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "type",
|
||||
"fieldname": "room_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Type",
|
||||
"label": "Room Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Direct\nGroup\nVisitor",
|
||||
|
|
@ -206,7 +206,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-01-18 05:22:09.458705",
|
||||
"modified": "2018-01-21 12:35:12.069954",
|
||||
"modified_by": "faris@erpnext.com",
|
||||
"module": "Chat",
|
||||
"name": "Chat Message",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ from frappe.chat.util import (
|
|||
dictify,
|
||||
get_emojis,
|
||||
safe_json_loads,
|
||||
get_user_doc
|
||||
get_user_doc,
|
||||
squashify
|
||||
)
|
||||
|
||||
session = frappe.session
|
||||
|
|
@ -91,13 +92,13 @@ def get_new_chat_message_doc(user, room, content, link = True):
|
|||
|
||||
meta = get_message_meta(content)
|
||||
mess = frappe.new_doc('Chat Message')
|
||||
mess.type = room.type
|
||||
mess.room = room.name
|
||||
mess.content = sanitize_message_content(content)
|
||||
mess.user = user.name
|
||||
mess.room = room.name
|
||||
mess.room_type = room.type
|
||||
mess.content = sanitize_message_content(content)
|
||||
mess.user = user.name
|
||||
|
||||
mess.mentions = json.dumps(meta.mentions)
|
||||
mess.urls = ','.join(meta.urls)
|
||||
mess.mentions = json.dumps(meta.mentions)
|
||||
mess.urls = ','.join(meta.urls)
|
||||
mess.save(ignore_permissions = True)
|
||||
|
||||
if link:
|
||||
|
|
@ -112,14 +113,15 @@ def get_new_chat_message(user, room, content):
|
|||
mess = get_new_chat_message_doc(user, room, content)
|
||||
|
||||
resp = dict(
|
||||
name = mess.name,
|
||||
user = mess.user,
|
||||
room = mess.room,
|
||||
content = mess.content,
|
||||
urls = mess.urls,
|
||||
mentions = json.loads(mess.mentions),
|
||||
creation = mess.creation,
|
||||
seen = json.loads(mess._seen) if mess._seen else [ ],
|
||||
name = mess.name,
|
||||
user = mess.user,
|
||||
room = mess.room,
|
||||
room_type = mess.room_type,
|
||||
content = mess.content,
|
||||
urls = mess.urls,
|
||||
mentions = json.loads(mess.mentions),
|
||||
creation = mess.creation,
|
||||
seen = json.loads(mess._seen) if mess._seen else [ ],
|
||||
)
|
||||
|
||||
return resp
|
||||
|
|
@ -145,11 +147,11 @@ def history(room, fields = None, limit = 10, start = None, end = None):
|
|||
room = frappe.get_doc('Chat Room', room)
|
||||
mess = frappe.get_all('Chat Message',
|
||||
filters = [
|
||||
('Chat Message', 'room', '=', room.name),
|
||||
('Chat Message', 'type', '=', room.type)
|
||||
('Chat Message', 'room', '=', room.name),
|
||||
('Chat Message', 'room_type', '=', room.type)
|
||||
],
|
||||
fields = fields if fields else [
|
||||
'name', 'type', 'room', 'content', 'user', 'mentions', 'urls', 'creation', '_seen'
|
||||
'name', 'room_type', 'room', 'content', 'user', 'mentions', 'urls', 'creation', '_seen'
|
||||
],
|
||||
order_by = 'creation'
|
||||
)
|
||||
|
|
@ -167,14 +169,15 @@ def get(name, rooms = None, fields = None):
|
|||
|
||||
dmess = frappe.get_doc('Chat Message', name)
|
||||
data = dict(
|
||||
name = dmess.name,
|
||||
user = dmess.user,
|
||||
room = dmess.room,
|
||||
content = dmess.content,
|
||||
urls = dmess.urls,
|
||||
mentions = dmess.mentions,
|
||||
creation = dmess.creation,
|
||||
seen = assign_if_empty(dmess._seen, [ ])
|
||||
name = dmess.name,
|
||||
user = dmess.user,
|
||||
room = dmess.room,
|
||||
room_type = dmess.room_type,
|
||||
content = dmess.content,
|
||||
urls = dmess.urls,
|
||||
mentions = dmess.mentions,
|
||||
creation = dmess.creation,
|
||||
seen = assign_if_empty(dmess._seen, [ ])
|
||||
)
|
||||
|
||||
return data
|
||||
|
|
@ -3,9 +3,7 @@
|
|||
"public/css/font-awesome.css",
|
||||
"public/css/octicons/octicons.css",
|
||||
"public/css/website.css",
|
||||
"public/css/avatar.css",
|
||||
|
||||
"public/css/chat.css"
|
||||
"public/css/avatar.css"
|
||||
],
|
||||
"js/frappe-web.min.js": [
|
||||
"public/js/frappe/class.js",
|
||||
|
|
@ -22,11 +20,7 @@
|
|||
"public/js/lib/microtemplate.js",
|
||||
"public/js/frappe/query_string.js",
|
||||
"website/js/website.js",
|
||||
"public/js/frappe/misc/rating_icons.html",
|
||||
|
||||
"public/js/lib/hyper.min.js",
|
||||
"public/js/lib/fuse.min.js",
|
||||
"public/js/frappe/chat.js"
|
||||
"public/js/frappe/misc/rating_icons.html"
|
||||
],
|
||||
"js/control.min.js": [
|
||||
"public/js/frappe/ui/capture.js",
|
||||
|
|
@ -146,7 +140,7 @@
|
|||
"public/js/lib/moment/moment-with-locales.min.js",
|
||||
"public/js/lib/moment/moment-timezone-with-data.min.js",
|
||||
"public/js/lib/socket.io.min.js",
|
||||
"public/js/lib/markdown.js",
|
||||
"public/js/lib/showdown.js",
|
||||
"public/js/lib/jSignature.min.js",
|
||||
"public/js/frappe/translate.js",
|
||||
"public/js/lib/datepicker/datepicker.min.js",
|
||||
|
|
|
|||
|
|
@ -314,21 +314,8 @@ a.no-decoration:active {
|
|||
vertical-align: middle;
|
||||
max-width: 180px;
|
||||
}
|
||||
.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .frappe-chat-room-list .message-count {
|
||||
background: #ff5858;
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 12px;
|
||||
height: 20px;
|
||||
line-height: 10px;
|
||||
text-align: center;
|
||||
min-width: 20px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .chat-list.list-group {
|
||||
height: 390px;
|
||||
height: 387px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.frappe-chat > .frappe-chat-popper > .frappe-chat-popper-collapse > .panel .frappe-chat-room-footer {
|
||||
|
|
@ -354,40 +341,99 @@ a.no-decoration:active {
|
|||
.frappe-chat .panel {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.frappe-chat .panel .frappe-chat-form {
|
||||
margin: 1px;
|
||||
}
|
||||
.frappe-chat .panel .frappe-chat-form .form-control {
|
||||
.frappe-chat .panel .chat-form .form-control {
|
||||
font-size: 12px;
|
||||
}
|
||||
.frappe-chat .panel .frappe-chat-form .dropdown-menu {
|
||||
.frappe-chat .panel .chat-form .dropdown-menu {
|
||||
border-radius: 4px;
|
||||
}
|
||||
.frappe-chat .panel .frappe-chat-form .btn {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
.frappe-chat .panel .frappe-chat-form .hint-list.list-group {
|
||||
.frappe-chat .panel .chat-form .hint-list.list-group {
|
||||
margin: 0px;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.frappe-chat .panel .frappe-chat-form .hint-list.list-group .hint-list-item.list-group-item:first-child,
|
||||
.frappe-chat .panel .frappe-chat-form .hint-list.list-group .hint-list-item.list-group-item:last-child {
|
||||
.frappe-chat .panel .chat-form .hint-list.list-group .hint-list-item.list-group-item:first-child,
|
||||
.frappe-chat .panel .chat-form .hint-list.list-group .hint-list-item.list-group-item:last-child {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
.frappe-chat .panel .frappe-chat-form .hint-list.list-group .hint-list-item.list-group-item:first-child a,
|
||||
.frappe-chat .panel .frappe-chat-form .hint-list.list-group .hint-list-item.list-group-item:last-child a {
|
||||
.frappe-chat .panel .chat-form .hint-list.list-group .hint-list-item.list-group-item:first-child a,
|
||||
.frappe-chat .panel .chat-form .hint-list.list-group .hint-list-item.list-group-item:last-child a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.chat-message {
|
||||
background: #E8DDFF;
|
||||
padding: 5px 15px;
|
||||
margin: 5px;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
.chat-list {
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
.seen-check {
|
||||
font-size: 12px;
|
||||
.chat-form {
|
||||
border-top: 1px solid #D1D8DD;
|
||||
}
|
||||
.chat-form .input-group-btn .btn {
|
||||
background: white;
|
||||
}
|
||||
.chat-form .form-control {
|
||||
line-height: 27px;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
resize: none;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
.chat-form .fa {
|
||||
font-size: 14px;
|
||||
}
|
||||
.chat-list {
|
||||
background: #FAFBFC;
|
||||
}
|
||||
.chat-list .chat-list-item {
|
||||
cursor: pointer;
|
||||
border: none !important;
|
||||
padding: 5px 10px;
|
||||
background: transparent;
|
||||
}
|
||||
.chat-list .chat-list-item .avatar {
|
||||
vertical-align: top;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble {
|
||||
min-width: 20%;
|
||||
max-width: 75%;
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
-webkit-box-shadow: 0px 0.1px 0.5px 0px rgba(0, 0, 0, 0.5);
|
||||
-moz-box-shadow: 0px 0.1px 0.5px 0px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 0px 0.1px 0.5px 0px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble.chat-bubble-l {
|
||||
background-color: #EBEFF2;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble.chat-bubble-l .chat-bubble-meta > .chat-bubble-creation,
|
||||
.chat-list .chat-list-item .chat-bubble.chat-bubble-l .chat-bubble-meta > .chat-bubble-check i {
|
||||
color: #577287 !important;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble.chat-bubble-r {
|
||||
text-align: right;
|
||||
background-color: #EBF7CF;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble.chat-bubble-r .chat-bubble-meta > .chat-bubble-creation,
|
||||
.chat-list .chat-list-item .chat-bubble.chat-bubble-r .chat-bubble-meta > .chat-bubble-check i {
|
||||
color: #80ab1c !important;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble .chat-bubble-author {
|
||||
font-size: 12px;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble .chat-bubble-author a {
|
||||
font-weight: 700;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble .chat-bubble-content {
|
||||
margin-bottom: 5px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble .chat-bubble-meta {
|
||||
font-size: 10px;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble .chat-bubble-meta > .chat-bubble-check {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.chat-list .chat-list-item .chat-bubble .chat-bubble-meta > .chat-bubble-check i {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
// Frappe Chat
|
||||
// Author - Achilles Rasquinha <achilles@frappe.io>
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------------
|
||||
* Developer Notes
|
||||
* --------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* eslint semi: "never" */
|
||||
// Fuck semicolons - https://mislav.net/2010/05/semicolons
|
||||
|
||||
|
|
@ -1064,6 +1070,8 @@ const { h, Component } = hyper
|
|||
// frappe's component namespace.
|
||||
frappe.provide('frappe.components')
|
||||
|
||||
frappe.provide('frappe.chat.component')
|
||||
|
||||
/**
|
||||
* @description Button Component
|
||||
*
|
||||
|
|
@ -1171,7 +1179,7 @@ class extends Component
|
|||
{
|
||||
const { props } = this
|
||||
|
||||
return props.type ? h("i", { ...props, class: `fa ${props.fixed ? "fa-fw" : ""} fa-${props.type}` }) : null
|
||||
return props.type ? h("i", { ...props, class: `fa ${props.fixed ? "fa-fw" : ""} fa-${props.type} ${props.class}` }) : null
|
||||
}
|
||||
}
|
||||
frappe.components.FontAwesome.defaultProps
|
||||
|
|
@ -1396,7 +1404,7 @@ class extends Component
|
|||
const state = [ ]
|
||||
|
||||
for (const room of rooms)
|
||||
if ( room.type === "Group" || room.last_message )
|
||||
if ( room.type === "Group" || room.owner === frappe.session.user || room.last_message )
|
||||
{
|
||||
frappe.log.info(`Adding ${room.name} to component.`)
|
||||
state.push(room)
|
||||
|
|
@ -2091,7 +2099,8 @@ class extends Component
|
|||
{
|
||||
icon: "camera",
|
||||
label: "Camera",
|
||||
click: ( ) => {
|
||||
on_click: ( ) =>
|
||||
{
|
||||
const capture = new frappe.ui.Capture({
|
||||
animate: false,
|
||||
error: true
|
||||
|
|
@ -2107,7 +2116,8 @@ class extends Component
|
|||
{
|
||||
icon: "file",
|
||||
label: "File",
|
||||
click: ( ) => {
|
||||
on_click: ( ) =>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -2127,7 +2137,7 @@ class extends Component
|
|||
h("div", { class: `panel panel-default ${frappe._.is_mobile() ? "panel-span" : ""}` },
|
||||
h(frappe.Chat.Widget.Room.Header, { ...props, back: props.destroy }),
|
||||
!frappe._.is_empty(props.messages) ?
|
||||
h(frappe.Chat.Widget.ChatList, {
|
||||
h(frappe.chat.component.ChatList, {
|
||||
messages: props.messages
|
||||
})
|
||||
:
|
||||
|
|
@ -2140,11 +2150,11 @@ class extends Component
|
|||
)
|
||||
),
|
||||
h("div", { class: "frappe-chat-room-footer" },
|
||||
h(frappe.Chat.Widget.ChatForm, { actions: actions,
|
||||
change: () => {
|
||||
h(frappe.chat.component.ChatForm, { actions: actions,
|
||||
on_change: () => {
|
||||
frappe.chat.message.typing(props.name)
|
||||
},
|
||||
submit: (message) => {
|
||||
on_submit: (message) => {
|
||||
frappe.chat.message.send(props.name, message)
|
||||
},
|
||||
hint: hints
|
||||
|
|
@ -2220,23 +2230,141 @@ class extends Component
|
|||
}
|
||||
|
||||
/**
|
||||
* @description Chat Form Component
|
||||
* @description ChatList Component
|
||||
*
|
||||
* @prop {array} messages - ChatMessage(s)
|
||||
*/
|
||||
frappe.Chat.Widget.ChatForm
|
||||
frappe.chat.component.ChatList
|
||||
=
|
||||
class extends Component {
|
||||
constructor (props) {
|
||||
super (props)
|
||||
|
||||
this.change = this.change.bind(this)
|
||||
this.submit = this.submit.bind(this)
|
||||
|
||||
this.hint = this.hint.bind(this)
|
||||
|
||||
this.state = frappe.Chat.Widget.ChatForm.defaultState
|
||||
class extends Component
|
||||
{
|
||||
on_mounted ( )
|
||||
{
|
||||
this.$element = $('.frappe-chat').find('.chat-list')
|
||||
this.$element.scrollTop(this.$element[0].scrollHeight)
|
||||
}
|
||||
|
||||
change (e)
|
||||
on_updated ( )
|
||||
{
|
||||
this.$element.scrollTop(this.$element[0].scrollHeight)
|
||||
}
|
||||
|
||||
render ( )
|
||||
{
|
||||
const { props } = this
|
||||
|
||||
return !frappe._.is_empty(props.messages) ? (
|
||||
h("div",{class:"chat-list list-group"},
|
||||
props.messages.map(m => h(frappe.chat.component.ChatList.Item, {...m}))
|
||||
)
|
||||
) : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description ChatList.Item Component
|
||||
*
|
||||
* @prop {string} name - ChatMessage name
|
||||
* @prop {string} user - ChatMessage user
|
||||
* @prop {string} room - ChatMessage room
|
||||
* @prop {string} room_type - ChatMessage Room Type ("Direct", "Group" or "Visitor")
|
||||
* @prop {string} content - ChatMessage content
|
||||
* @prop {frappe.datetime.datetime} creation - Chat Message creation
|
||||
*/
|
||||
frappe.chat.component.ChatList.Item
|
||||
=
|
||||
class extends Component
|
||||
{
|
||||
render ( )
|
||||
{
|
||||
const { props } = this
|
||||
|
||||
const me = props.user === frappe.session.user
|
||||
|
||||
return (
|
||||
h("div",{class: "chat-list-item list-group-item"},
|
||||
h("div",{class:`${me ? "text-right" : ""}`},
|
||||
props.room_type === "Group" && !me?
|
||||
h(frappe.components.Avatar,
|
||||
{
|
||||
title: frappe.user.full_name(props.user),
|
||||
image: frappe.user.image(props.user)
|
||||
}) : null,
|
||||
h(frappe.chat.component.ChatBubble, props)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description ChatBubble Component
|
||||
*
|
||||
* @prop {string} name - ChatMessage name
|
||||
* @prop {string} user - ChatMessage user
|
||||
* @prop {string} room - ChatMessage room
|
||||
* @prop {string} room_type - ChatMessage room_type ("Direct", "Group" or "Visitor")
|
||||
* @prop {string} content - ChatMessage content
|
||||
* @prop {frappe.datetime.datetime} creation - ChatMessage creation
|
||||
*/
|
||||
frappe.chat.component.ChatBubble
|
||||
=
|
||||
class extends Component
|
||||
{
|
||||
render ( )
|
||||
{
|
||||
const { props } = this
|
||||
|
||||
const creation = props.creation.format('hh:mm A')
|
||||
|
||||
const me = props.user === frappe.session.user
|
||||
const read = !frappe._.is_empty(props.seen) && !props.seen.includes(frappe.session.user)
|
||||
|
||||
const content = props.content
|
||||
|
||||
return (
|
||||
h("div",{class:`chat-bubble chat-bubble-${me ? "r" : "l"}`},
|
||||
props.room_type === "Group" && !me?
|
||||
h("div",{class:"chat-bubble-author"},
|
||||
h("a", { onclick: () => { frappe.set_route(`Form/User/${props.user}`) } },
|
||||
frappe.user.full_name(props.user)
|
||||
)
|
||||
) : null,
|
||||
h("div",{class:"chat-bubble-content"},
|
||||
h("small","",content)
|
||||
),
|
||||
h("div",{class:"chat-bubble-meta"},
|
||||
h("span",{class:"chat-bubble-creation"},creation),
|
||||
me && read ?
|
||||
h("span",{class:"chat-bubble-check"},
|
||||
h(frappe.components.Octicon,{type:"check"})
|
||||
) : null
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description ChatForm Component
|
||||
*/
|
||||
frappe.chat.component.ChatForm
|
||||
=
|
||||
class extends Component
|
||||
{
|
||||
constructor (props)
|
||||
{
|
||||
super (props)
|
||||
|
||||
this.on_change = this.on_change.bind(this)
|
||||
this.on_submit = this.on_submit.bind(this)
|
||||
|
||||
this.hint = this.hint.bind(this)
|
||||
|
||||
this.state = frappe.chat.component.ChatForm.defaultState
|
||||
}
|
||||
|
||||
on_change (e)
|
||||
{
|
||||
const { props, state } = this
|
||||
const value = e.target.value
|
||||
|
|
@ -2245,7 +2373,7 @@ class extends Component {
|
|||
[e.target.name]: value
|
||||
})
|
||||
|
||||
props.change(state)
|
||||
props.on_change(state)
|
||||
|
||||
this.hint(value)
|
||||
}
|
||||
|
|
@ -2290,23 +2418,24 @@ class extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
submit (e)
|
||||
on_submit (e)
|
||||
{
|
||||
e.preventDefault()
|
||||
|
||||
if ( this.state.content )
|
||||
{
|
||||
this.props.submit(this.state.content)
|
||||
this.props.on_submit(this.state.content)
|
||||
|
||||
this.set_state({ content: null })
|
||||
}
|
||||
}
|
||||
|
||||
render ( ) {
|
||||
render ( )
|
||||
{
|
||||
const { props, state } = this
|
||||
|
||||
return (
|
||||
h("div", { class: "frappe-chat-form" },
|
||||
h("div",{class:"chat-form"},
|
||||
state.hints.length ?
|
||||
h("ul", { class: "hint-list list-group" },
|
||||
state.hints.map((item) =>
|
||||
|
|
@ -2323,25 +2452,27 @@ class extends Component {
|
|||
)
|
||||
})
|
||||
) : null,
|
||||
h("form", { oninput: this.change, onsubmit: this.submit },
|
||||
h("div", { class: "input-group input-group-lg" },
|
||||
h("div", { class: "input-group-btn dropup" },
|
||||
h(frappe.components.Button, { class: "dropdown-toggle", "data-toggle": "dropdown" },
|
||||
h(frappe.components.FontAwesome, { type: "paperclip", fixed: true, style: { "font-size": "14px" } })
|
||||
),
|
||||
h("div", { class: "dropdown-menu dropdown-menu-left", onclick: e => e.stopPropagation() },
|
||||
!frappe._.is_empty(props.actions) && props.actions.map((action) => {
|
||||
return (
|
||||
h("li", null,
|
||||
h("a", { onclick: action.click },
|
||||
h(frappe.components.FontAwesome, { type: action.icon, fixed: true }), ` ${action.label}`,
|
||||
h("form", { oninput: this.on_change, onsubmit: this.on_submit },
|
||||
h("div",{class:"input-group input-group-lg"},
|
||||
!frappe._.is_empty(props.actions) ?
|
||||
h("div",{class:"input-group-btn dropup"},
|
||||
h(frappe.components.Button,{ class: "dropdown-toggle", "data-toggle": "dropdown"},
|
||||
h(frappe.components.FontAwesome, { class: "text-muted", type: "paperclip", fixed: true })
|
||||
),
|
||||
h("div",{ class:"dropdown-menu dropdown-menu-left", onclick: e => e.stopPropagation() },
|
||||
!frappe._.is_empty(props.actions) && props.actions.map((action) =>
|
||||
{
|
||||
return (
|
||||
h("li", null,
|
||||
h("a",{onclick:action.on_click},
|
||||
h(frappe.components.FontAwesome,{type:action.icon,fixed:true}), ` ${action.label}`,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
)
|
||||
),
|
||||
h("input",
|
||||
})
|
||||
)
|
||||
) : null,
|
||||
h("textarea",
|
||||
{
|
||||
class: "form-control",
|
||||
name: "content",
|
||||
|
|
@ -2351,12 +2482,12 @@ class extends Component {
|
|||
onkeypress: (e) =>
|
||||
{
|
||||
if ( e.which === frappe.ui.keycode.RETURN && !e.shiftKey )
|
||||
this.submit(e)
|
||||
this.on_submit(e)
|
||||
}
|
||||
}),
|
||||
h("div", { class: "input-group-btn" },
|
||||
h(frappe.components.Button, { type: "primary", class: "dropdown-toggle", "data-toggle": "dropdown", onclick: this.submit },
|
||||
h(frappe.components.FontAwesome, { type: "send", fixed: true, style: { "font-size": "14px" } })
|
||||
h("div",{class:"input-group-btn"},
|
||||
h(frappe.components.Button, { onclick: this.on_submit },
|
||||
h(frappe.components.FontAwesome, { class: !frappe._.is_empty(state.content) ? "text-primary" : "text-muted", type: "send", fixed: true })
|
||||
),
|
||||
)
|
||||
)
|
||||
|
|
@ -2365,14 +2496,20 @@ class extends Component {
|
|||
)
|
||||
}
|
||||
}
|
||||
frappe.Chat.Widget.ChatForm.defaultState
|
||||
frappe.chat.component.ChatForm.defaultState
|
||||
=
|
||||
{
|
||||
content: null,
|
||||
hints: [ ],
|
||||
}
|
||||
|
||||
frappe.Chat.Widget.EmojiPicker
|
||||
|
||||
/**
|
||||
* @description EmojiPicker Component
|
||||
*
|
||||
* @todo Under Development
|
||||
*/
|
||||
frappe.chat.component.EmojiPicker
|
||||
=
|
||||
class extends Component
|
||||
{
|
||||
|
|
@ -2387,14 +2524,14 @@ class extends Component
|
|||
),
|
||||
h("div", { class: "dropdown-menu dropdown-menu-right", onclick: e => e.stopPropagation() },
|
||||
h("div", { class: "panel panel-default" },
|
||||
h(frappe.Chat.Widget.EmojiPicker.List)
|
||||
h(frappe.chat.component.EmojiPicker.List)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
frappe.Chat.Widget.EmojiPicker.List
|
||||
frappe.chat.component.EmojiPicker.List
|
||||
=
|
||||
class extends Component
|
||||
{
|
||||
|
|
@ -2408,72 +2545,4 @@ class extends Component
|
|||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Chat List HOC
|
||||
*/
|
||||
frappe.Chat.Widget.ChatList
|
||||
=
|
||||
class extends Component {
|
||||
on_mounted ( )
|
||||
{
|
||||
const $element = $('.frappe-chat').find('.chat-list')
|
||||
$element.scrollTop($element[0].scrollHeight)
|
||||
}
|
||||
|
||||
on_updated ( )
|
||||
{
|
||||
const $element = $('.frappe-chat').find('.chat-list')
|
||||
$element.scrollTop($element[0].scrollHeight)
|
||||
}
|
||||
|
||||
render ( ) {
|
||||
const { props } = this
|
||||
|
||||
return !frappe._.is_empty(props.messages) ? (
|
||||
h("ul", { class: "chat-list list-group" },
|
||||
props.messages.map(m => h(frappe.Chat.Widget.ChatList.Item, {
|
||||
...m
|
||||
}))
|
||||
)
|
||||
) : null
|
||||
}
|
||||
}
|
||||
|
||||
frappe.Chat.Widget.ChatList.Item
|
||||
=
|
||||
class extends Component {
|
||||
render ( ) {
|
||||
const { props } = this
|
||||
|
||||
return (
|
||||
h("li", { class: "list-group-item", style: "border: none !important" },
|
||||
h(frappe.Chat.Widget.ChatList.Bubble, props)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
frappe.Chat.Widget.ChatList.Bubble
|
||||
=
|
||||
class extends Component {
|
||||
render ( ) {
|
||||
const { props } = this
|
||||
|
||||
return (
|
||||
h(frappe.Chat.Widget.MediaProfile, {
|
||||
title: frappe.user.full_name(props.user),
|
||||
subtitle: `${frappe.chat.pretty_datetime(props.creation)}`,
|
||||
content: props.content,
|
||||
image: frappe.user.image(props.user),
|
||||
width_title: "100%",
|
||||
position: frappe.user.full_name(props.user) === "You" ? "right" : "left"
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
frappe.Chat.Widget.ChatList.Bubble.defaultState =
|
||||
{
|
||||
creation: ""
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ frappe.tools.downloadify = function(data, roles, title) {
|
|||
|
||||
frappe.markdown = function(txt) {
|
||||
if(!frappe.md2html) {
|
||||
frappe.md2html = new Showdown.converter();
|
||||
frappe.md2html = new showdown.Converter();
|
||||
}
|
||||
|
||||
while(txt.substr(0,1)==="\n") {
|
||||
|
|
|
|||
2
frappe/public/js/lib/hyper.min.js
vendored
2
frappe/public/js/lib/hyper.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frappe/public/js/lib/showdown.js
Normal file
1
frappe/public/js/lib/showdown.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -41,184 +41,309 @@
|
|||
|
||||
.navbar
|
||||
{
|
||||
.frappe-chat-toggle
|
||||
{
|
||||
height: @frappe-chat-toggle-height;
|
||||
text-align: center;
|
||||
}
|
||||
.frappe-chat-toggle
|
||||
{
|
||||
height: @frappe-chat-toggle-height;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.octicon { margin-top: 5px; } // Hack, somewhat.
|
||||
.octicon { margin-top: 5px; } // Hack, somewhat.
|
||||
}
|
||||
|
||||
.frappe-chat
|
||||
{
|
||||
& > .frappe-chat-popper
|
||||
{
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
margin: @frappe-chat-popper-margin;
|
||||
z-index: @frappe-chat-popper-z-index;
|
||||
|
||||
& > .frappe-chat-popper-collapse
|
||||
{
|
||||
& > .panel
|
||||
{
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: @frappe-chat-popper-panel-width;
|
||||
height: @frappe-chat-popper-panel-height;
|
||||
box-shadow: @frappe-chat-popper-panel-box-shadow;
|
||||
& > .frappe-chat-popper
|
||||
{
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
margin: @frappe-chat-popper-margin;
|
||||
z-index: @frappe-chat-popper-z-index;
|
||||
|
||||
& > .frappe-chat-popper-collapse
|
||||
{
|
||||
& > .panel
|
||||
{
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: @frappe-chat-popper-panel-width;
|
||||
height: @frappe-chat-popper-panel-height;
|
||||
box-shadow: @frappe-chat-popper-panel-box-shadow;
|
||||
|
||||
.vcenter
|
||||
{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.vcenter
|
||||
{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.panel-heading
|
||||
{
|
||||
.frappe-chat-action-bar
|
||||
{
|
||||
form
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
.panel-heading
|
||||
{
|
||||
.frappe-chat-action-bar
|
||||
{
|
||||
form
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-action
|
||||
{
|
||||
margin-left: 5px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn-action
|
||||
{
|
||||
margin-left: 5px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.frappe-chat-room-list
|
||||
{
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 0 1px 0 1px;
|
||||
.frappe-chat-room-list
|
||||
{
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 0 1px 0 1px;
|
||||
|
||||
& > li > a
|
||||
{
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
& > li > a
|
||||
{
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
.media
|
||||
{
|
||||
.media-heading, .media-subtitle
|
||||
{
|
||||
.ellipsis;
|
||||
max-width: @frappe-chat-room-list-content-max-width;
|
||||
}
|
||||
}
|
||||
.media
|
||||
{
|
||||
.media-heading, .media-subtitle
|
||||
{
|
||||
.ellipsis;
|
||||
max-width: @frappe-chat-room-list-content-max-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-count
|
||||
{
|
||||
background: #ff5858;
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 12px;
|
||||
height: 20px;
|
||||
line-height: 10px;
|
||||
text-align: center;
|
||||
min-width: 20px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
.chat-list.list-group
|
||||
{
|
||||
height: 387px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.chat-list.list-group
|
||||
{
|
||||
height: 390px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.frappe-chat-room-footer
|
||||
{
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.frappe-chat-room-footer
|
||||
{
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
}
|
||||
& > .panel.panel-span
|
||||
{
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
overflow: auto;
|
||||
border-radius: 0px;
|
||||
|
||||
.panel-heading
|
||||
{
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .panel.panel-span
|
||||
{
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
overflow: auto;
|
||||
border-radius: 0px;
|
||||
|
||||
.panel-heading
|
||||
{
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.panel
|
||||
{
|
||||
margin-bottom: 0px !important;
|
||||
|
||||
.panel
|
||||
{
|
||||
margin-bottom: 0px !important;
|
||||
.chat-form
|
||||
{
|
||||
.form-control
|
||||
{
|
||||
font-size: @frappe-chat-form-font-size;
|
||||
}
|
||||
|
||||
.dropdown-menu
|
||||
{
|
||||
border-radius: @frappe-chat-form-menu-border-radius;
|
||||
}
|
||||
|
||||
// Hints
|
||||
.hint-list.list-group
|
||||
{
|
||||
margin: 0px;
|
||||
max-height: @frappe-chat-form-list-group-height;
|
||||
overflow-y: auto;
|
||||
|
||||
.hint-list-item.list-group-item:first-child, .hint-list-item.list-group-item:last-child
|
||||
{
|
||||
border-radius: 0px !important;
|
||||
|
||||
.frappe-chat-form
|
||||
{
|
||||
// HACK: Wraps the ChatForm within the panel.
|
||||
margin: 1px;
|
||||
|
||||
.form-control
|
||||
{
|
||||
font-size: @frappe-chat-form-font-size;
|
||||
}
|
||||
|
||||
.dropdown-menu
|
||||
{
|
||||
border-radius: @frappe-chat-form-menu-border-radius;
|
||||
}
|
||||
|
||||
.btn
|
||||
{
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
// Hints
|
||||
.hint-list.list-group
|
||||
{
|
||||
margin: 0px;
|
||||
max-height: @frappe-chat-form-list-group-height;
|
||||
overflow-y: auto;
|
||||
|
||||
.hint-list-item.list-group-item:first-child, .hint-list-item.list-group-item:last-child
|
||||
{
|
||||
border-radius: 0px !important;
|
||||
|
||||
a { text-decoration: none }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
a { text-decoration: none }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
.chat-message {
|
||||
background: #E8DDFF;
|
||||
padding: 5px 15px;
|
||||
margin: 5px;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
// hacks
|
||||
.chat-list
|
||||
{
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.seen-check {
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
@frappe-chat-color-grey: #8D99A6;
|
||||
|
||||
@frappe-chat-base-font-size: 12px;
|
||||
@frappe-chat-base-font-size-lg: 14px;
|
||||
|
||||
@frappe-chat-base-spacing: 5px;
|
||||
|
||||
// ChatForm
|
||||
@frappe-chat-form-border: 1px solid #D1D8DD;
|
||||
|
||||
// ChatList
|
||||
@frappe-chat-list-bg-color: #FAFBFC;
|
||||
|
||||
// ChatList.Item
|
||||
@frappe-chat-list-item-padding: @frappe-chat-base-spacing @frappe-chat-base-spacing * 2;
|
||||
|
||||
// ChatBubble
|
||||
@frappe-chat-bubble-padding: @frappe-chat-base-spacing @frappe-chat-base-spacing * 2;
|
||||
@frappe-chat-bubble-min-width: 20%;
|
||||
@frappe-chat-bubble-max-width: 75%;
|
||||
|
||||
@frappe-chat-bubble-box-shadow: 0px 0.1px 0.5px 0px rgba(0,0,0,0.5);
|
||||
|
||||
@frappe-chat-bubble-border-size: 1px;
|
||||
@frappe-chat-bubble-border-radius: @frappe-chat-base-spacing;
|
||||
|
||||
@frappe-chat-bubble-l-color: #EBEFF2;
|
||||
@frappe-chat-bubble-r-color: #EBF7CF;
|
||||
|
||||
@frappe-chat-bubble-author-font-size: @frappe-chat-base-font-size;
|
||||
|
||||
@frappe-chat-bubble-content-margin-bottom: @frappe-chat-base-spacing;
|
||||
|
||||
@frappe-chat-bubble-meta-font-size: @frappe-chat-base-spacing * 2;
|
||||
|
||||
@frappe-chat-bubble-check-font-size: @frappe-chat-base-font-size;
|
||||
|
||||
.chat-form
|
||||
{
|
||||
border-top: @frappe-chat-form-border;
|
||||
|
||||
.input-group-btn
|
||||
{
|
||||
.btn
|
||||
{
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control
|
||||
{
|
||||
line-height: 27px; // HACK: Makes input and placeholder centered within textarea. Also takes care of the input-btn
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
resize: none;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.fa
|
||||
{
|
||||
font-size: @frappe-chat-base-font-size-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-list
|
||||
{
|
||||
background: @frappe-chat-list-bg-color;
|
||||
|
||||
.chat-list-item
|
||||
{
|
||||
.avatar
|
||||
{
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.cursor-pointer;
|
||||
|
||||
border: none !important;
|
||||
padding: @frappe-chat-list-item-padding;
|
||||
background: transparent;
|
||||
|
||||
.chat-bubble
|
||||
{
|
||||
min-width: @frappe-chat-bubble-min-width;
|
||||
max-width: @frappe-chat-bubble-max-width;
|
||||
display: inline-block;
|
||||
padding: @frappe-chat-bubble-padding;
|
||||
border-radius: @frappe-chat-bubble-border-radius;
|
||||
|
||||
-webkit-box-shadow: @frappe-chat-bubble-box-shadow;
|
||||
-moz-box-shadow: @frappe-chat-bubble-box-shadow;
|
||||
box-shadow: @frappe-chat-bubble-box-shadow;
|
||||
|
||||
&.chat-bubble-l
|
||||
{
|
||||
background-color: @frappe-chat-bubble-l-color;
|
||||
|
||||
.chat-bubble-meta
|
||||
{
|
||||
& > .chat-bubble-creation, & > .chat-bubble-check i
|
||||
{
|
||||
color: darken(@frappe-chat-bubble-l-color, 50%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.chat-bubble-r
|
||||
{
|
||||
text-align: right;
|
||||
background-color: @frappe-chat-bubble-r-color;
|
||||
|
||||
.chat-bubble-meta
|
||||
{
|
||||
& > .chat-bubble-creation, & > .chat-bubble-check i
|
||||
{
|
||||
color: darken(@frappe-chat-bubble-r-color, 50%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-bubble-author
|
||||
{
|
||||
font-size: @frappe-chat-bubble-author-font-size;
|
||||
|
||||
a
|
||||
{
|
||||
.font-bold;
|
||||
|
||||
text-decoration: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-bubble-content
|
||||
{
|
||||
margin-bottom: @frappe-chat-bubble-content-margin-bottom;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.chat-bubble-meta
|
||||
{
|
||||
font-size: @frappe-chat-bubble-meta-font-size;
|
||||
|
||||
& > .chat-bubble-check
|
||||
{
|
||||
margin-left: @frappe-chat-base-spacing;
|
||||
|
||||
i
|
||||
{
|
||||
font-size: @frappe-chat-bubble-check-font-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue