Render Frappe Chat to Website

This commit is contained in:
Achilles Rasquinha 2018-03-13 17:03:25 +05:30
parent c060a52423
commit bc712066cc
9 changed files with 206 additions and 136 deletions

View file

@ -280,9 +280,9 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, indicator=None,
import inspect
if inspect.isclass(raise_exception) and issubclass(raise_exception, Exception):
raise raise_exception(encode(msg))
raise raise_exception(as_unicode(msg))
else:
raise ValidationError(encode(msg))
raise ValidationError(as_unicode(msg))
if flags.mute_messages:
_raise_exception()

View file

@ -3,7 +3,9 @@
"public/css/font-awesome.css",
"public/css/octicons/octicons.css",
"public/less/website.less",
"public/less/avatar.less"
"public/less/avatar.less",
"public/less/chat.less"
],
"js/frappe-web.min.js": [
"public/js/frappe/class.js",
@ -19,7 +21,9 @@
"public/js/lib/microtemplate.js",
"public/js/frappe/query_string.js",
"website/js/website.js",
"public/js/frappe/misc/rating_icons.html"
"public/js/frappe/misc/rating_icons.html",
"public/js/frappe/chat.js"
],
"js/control.min.js": [
"public/js/frappe/ui/capture.js",
@ -122,8 +126,7 @@
"public/less/form.less",
"public/less/mobile.less",
"public/less/kanban.less",
"public/less/controls.less",
"public/less/chat.less"
"public/less/controls.less"
],
"css/frappe-rtl.css": [
"public/css/bootstrap-rtl.css",
@ -147,10 +150,7 @@
"public/js/lib/leaflet/leaflet.js",
"public/js/lib/leaflet/leaflet.draw.js",
"public/js/lib/leaflet/L.Control.Locate.js",
"public/js/lib/leaflet/easy-button.js",
"public/js/lib/hyper.min.js",
"public/js/lib/fuse.min.js"
"public/js/lib/leaflet/easy-button.js"
],
"js/desk.min.js": [
"public/js/frappe/class.js",

View file

@ -1,6 +1,11 @@
// Frappe Chat
// Author - Achilles Rasquinha <achilles@frappe.io>
import Fuse from 'fuse.js'
import hyper from '../lib/hyper.min'
import '../../less/chat.less'
/* eslint semi: "never" */
// Fuck semicolons - https://mislav.net/2010/05/semicolons
@ -598,16 +603,20 @@ frappe.chat.profile.on.update = function (fn) {
}
frappe.chat.profile.STATUSES
=
[ {
[
{
name: "Online",
color: "green"
}, {
},
{
name: "Away",
color: "yellow"
}, {
},
{
name: "Busy",
color: "red"
}, {
},
{
name: "Offline",
color: "darkgrey"
}
@ -1002,7 +1011,8 @@ class extends Component {
}
}
frappe.components.Button.SIZE
= {
=
{
small: {
class: "btn-sm"
},
@ -1011,7 +1021,8 @@ frappe.components.Button.SIZE
}
}
frappe.components.Button.defaultProps
= {
=
{
type: "default",
block: false
}
@ -1036,15 +1047,19 @@ class extends frappe.components.Button {
}
}
frappe.components.FAB.defaultProps
= {
=
{
icon: "octicon octicon-plus"
}
frappe.components.FAB.SIZE
= {
small: {
=
{
small:
{
class: "frappe-fab-sm"
},
large: {
large:
{
class: "frappe-fab-lg"
}
}
@ -1077,7 +1092,8 @@ class extends Component {
}
}
frappe.components.FontAwesome.defaultProps
= {
=
{
fixed: false
}
@ -1123,14 +1139,18 @@ class extends Component {
}
}
frappe.components.Avatar.SIZE
= {
small: {
=
{
small:
{
class: "avatar-small"
},
large: {
large:
{
class: "avatar-large"
},
medium: {
medium:
{
class: "avatar-medium"
}
}
@ -1248,11 +1268,12 @@ class {
}
}
frappe.Chat.Layout
= {
=
{
PAGE: "page", POPPER: "popper"
}
frappe.Chat.OPTIONS
= {
={
layout: frappe.Chat.Layout.POPPER
}
@ -1346,21 +1367,22 @@ class extends Component {
}
make ( ) {
frappe.chat.profile.create([
"status", "message_preview", "notification_tones", "conversation_tones"
]).then(profile => {
this.set_state({ profile })
if ( frappe.session.user != 'Guest' )
frappe.chat.profile.create([
"status", "message_preview", "notification_tones", "conversation_tones"
]).then(profile => {
this.set_state({ profile })
frappe.chat.room.get(rooms => {
rooms = frappe._.as_array(rooms)
frappe.log.info(`User ${frappe.session.user} is subscribed to ${rooms.length} ${frappe._.pluralize('room', rooms.length)}.`)
frappe.chat.room.get(rooms => {
rooms = frappe._.as_array(rooms)
frappe.log.info(`User ${frappe.session.user} is subscribed to ${rooms.length} ${frappe._.pluralize('room', rooms.length)}.`)
if ( !frappe._.is_empty(rooms) )
this.room.add(rooms)
if ( !frappe._.is_empty(rooms) )
this.room.add(rooms)
})
this.bind()
})
this.bind()
})
}
bind ( ) {
@ -1521,9 +1543,11 @@ class extends Component {
}
})
const contacts = Object.keys(frappe.boot.user_info).map(key => {
return { owner: frappe.session.user, users: [frappe.boot.user_info[key].email] }
})
var contacts = [ ]
if ( 'user_info' in frappe.boot )
contacts = Object.keys(frappe.boot.user_info).map(key => {
return { owner: frappe.session.user, users: [frappe.boot.user_info[key].email] }
})
const rooms = state.query ? frappe.chat.room.search(state.query, state.rooms.concat(contacts)) : frappe.chat.room.sort(state.rooms)
const RoomList = frappe._.is_empty(rooms) && !state.query ?
@ -1626,7 +1650,7 @@ class extends Component {
return !state.destroy ?
(
h("div", { class: "frappe-chat-popper" },
h("div", { class: "frappe-chat-popper", style: !props.target ? { "margin-bottom": "80px" } : null },
!props.target ?
h(frappe.components.FAB, {
class: "frappe-fab",
@ -1651,7 +1675,8 @@ class extends Component {
}
}
frappe.Chat.Widget.Popper.defaultState
= {
=
{
active: false,
destroy: false
}
@ -1843,7 +1868,8 @@ class extends Component {
}
}
frappe.Chat.Widget.MediaProfile.POSITION
= {
=
{
left: { class: "media-left" }, right: { class: "media-right" }
}
@ -2291,7 +2317,8 @@ class extends Component {
}
}
frappe.chat.component.ChatForm.defaultState
= {
=
{
content: null,
hints: [ ],
}
@ -2390,4 +2417,79 @@ frappe.notify = (string, options) =>
const notification = new Notification(string, options)
}
})
}
}
// $(document).ready(() =>
// {
// if ( frappe.boot.user != 'Guest' )
// {
// const chat = new frappe.Chat()
// chat.render()
// }
// })
frappe.chat.render = (render = true, force = false) =>
{
frappe.log.info(`${render ? "Enable" : "Disable"} Chat for User.`);
// With the assumption, that there's only one navbar.
const $placeholder = $('.navbar .frappe-chat-dropdown');
// Render if frappe-chat-toggle doesn't exist.
if ( frappe.utils.is_empty($placeholder.has('.frappe-chat-toggle')) ) {
const $template = $(`
<a class="dropdown-toggle frappe-chat-toggle" data-toggle="dropdown">
<div>
<i class="octicon octicon-comment-discussion"/>
</div>
</a>
`);
$placeholder.addClass('dropdown hidden');
$placeholder.html($template);
}
if ( render ) {
$placeholder.removeClass('hidden');
} else {
$placeholder.addClass('hidden');
}
// Avoid re-renders. Once is enough.
if ( !frappe.chatter || force ) {
frappe.chatter = new frappe.Chat({ target: '.navbar .frappe-chat-toggle' });
frappe.chatter.render();
}
}
frappe.chat.setup = () =>
{
frappe.log = frappe.Logger.get('frappe.chat');
frappe.log.info('Setting up frappe.chat');
frappe.log.warn('TODO: Handle realtime System Settings update.');
frappe.log.warn('TODO: frappe.chat.<object> requires a storage.');
// Create/Get Chat Profile for session User, retrieve enable_chat
frappe.log.info('Creating a Chat Profile.');
frappe.chat.profile.create('enable_chat').then(({ enable_chat }) => {
frappe.log.info(`Chat Profile created for User ${frappe.session.user}.`)
const should_render = frappe.sys_defaults.enable_chat && enable_chat;
frappe.chat.render(should_render);
});
// Triggered when a User updates his/her Chat Profile.
// Don't worry, enable_chat is broadcasted to this user only. No overhead. :)
frappe.chat.profile.on.update((user, profile) => {
if ( user === frappe.session.user && 'enable_chat' in profile ) {
frappe.log.warn(`Chat Profile update (Enable Chat - ${Boolean(profile.enable_chat)})`);
const should_render = frappe.sys_defaults.enable_chat && profile.enable_chat;
frappe.chat.render(should_render);
}
});
}
$(document).on('ready toolbar_setup', () =>
{
frappe.chat.setup()
})

View file

@ -22,10 +22,6 @@ frappe.ui.toolbar.Toolbar = Class.extend({
this.setup_sidebar();
this.setup_help();
// frappe.chat (added to toolbar as per rushabh@frappe.io request)
this.setup_frappe_chat();
// end frappe.chat
this.setup_modules_dialog();
this.setup_progress_dialog();
this.bind_events();
@ -39,65 +35,6 @@ frappe.ui.toolbar.Toolbar = Class.extend({
$('.navbar-set-desktop-icons').on('click', () => {
this.modules_select.show();
});
},
setup_frappe_chat ( ) {
frappe.log = frappe.Logger.get('frappe.chat');
frappe.log.info('Setting up frappe.chat');
frappe.log.warn('TODO: Handle realtime System Settings update.');
frappe.log.warn('TODO: frappe.chat.<object> requires a storage.');
// Create/Get Chat Profile for session User, retrieve enable_chat
frappe.log.info('Creating a Chat Profile.');
frappe.chat.profile.create('enable_chat').then(({ enable_chat }) => {
frappe.log.info(`Chat Profile created for User ${frappe.session.user}.`)
const should_render = frappe.sys_defaults.enable_chat && enable_chat;
this.render_frappe_chat(should_render);
});
// Triggered when a User updates his/her Chat Profile.
// Don't worry, enable_chat is broadcasted to this user only. No overhead. :)
frappe.chat.profile.on.update((user, profile) => {
if ( user === frappe.session.user && 'enable_chat' in profile ) {
frappe.log.warn(`Chat Profile update (Enable Chat - ${Boolean(profile.enable_chat)})`);
const should_render = frappe.sys_defaults.enable_chat && profile.enable_chat;
this.render_frappe_chat(should_render);
}
});
},
render_frappe_chat (render = true, force = false) {
frappe.log.info(`${render ? "Enable" : "Disable"} Chat for User.`);
// With the assumption, that there's only one navbar.
const $placeholder = $('.navbar .frappe-chat-dropdown');
// Render if frappe-chat-toggle doesn't exist.
if ( frappe.utils.is_empty($placeholder.has('.frappe-chat-toggle')) ) {
const $template = $(`
<a class="dropdown-toggle frappe-chat-toggle" data-toggle="dropdown">
<div>
<i class="octicon octicon-comment-discussion"/>
</div>
</a>
`);
$placeholder.addClass('dropdown hidden');
$placeholder.html($template);
}
if ( render ) {
$placeholder.removeClass('hidden');
} else {
$placeholder.addClass('hidden');
}
// Avoid re-renders. Once is enough.
if ( !frappe.chatter || force ) {
frappe.chatter = new frappe.Chat({ target: '.navbar .frappe-chat-toggle' });
frappe.chatter.render();
}
},
bind_events: function() {

File diff suppressed because one or more lines are too long

View file

@ -10,14 +10,18 @@
@frappe-chat-toggle-height: 40px;
@frappe-chat-popper-margin: 15px;
@frappe-fab-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
@frappe-fab-size-lg: 56px;
@frappe-fab-margin: 15px;
@frappe-chat-popper-margin: @frappe-fab-margin;
@frappe-chat-popper-panel-width: 350px;
@frappe-chat-popper-panel-height: 500px;
// z-index greater than FAB, lesser than modal.
@frappe-chat-popper-z-index: 1035;
// BS modal's box-shadow
@frappe-chat-popper-panel-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
@frappe-chat-popper-panel-box-shadow: @frappe-fab-box-shadow;
// https://github.com/twbs/bootstrap/blob/v3.3.7/less/variables.less#L278
// Keep z-index of the ChatPopper higher than others, lower than modal background.
@ -39,6 +43,22 @@
// suggested by rushabh@frappe.io. Thanks, Rushabh!
.avatar { padding: 2px; }
.frappe-fab
{
position: fixed;
bottom: 0;
right: 0;
border-radius: 50%;
box-shadow: @frappe-fab-box-shadow;
margin: @frappe-fab-margin;
&.frappe-fab-lg
{
width: @frappe-fab-size-lg;
height: @frappe-fab-size-lg;
}
}
.navbar
{
.frappe-chat-toggle
@ -54,20 +74,20 @@
{
& > .frappe-chat-popper
{
position: fixed;
position: fixed;
bottom: 0px;
right: 0px;
margin: @frappe-chat-popper-margin;
z-index: @frappe-chat-popper-z-index;
right: 0px;
margin: @frappe-chat-popper-margin;
z-index: @frappe-chat-popper-z-index;
& > .frappe-chat-popper-collapse
{
& > .panel
{
position: relative;
display: flex;
display: flex;
flex-direction: column;
width: @frappe-chat-popper-panel-width;
width: @frappe-chat-popper-panel-width;
height: @frappe-chat-popper-panel-height;
box-shadow: @frappe-chat-popper-panel-box-shadow;
@ -75,7 +95,7 @@
{
position: absolute;
top: 50%;
left: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@ -88,8 +108,8 @@
.media-heading
{
font-size: 12px;
margin: 0px;
padding: 0px;
margin: 0px;
padding: 0px;
}
.media-subtitle
@ -143,13 +163,13 @@
& > .panel.panel-span
{
position: fixed;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
overflow: auto;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
overflow: auto;
border-radius: 0px;
.panel-heading

View file

@ -8,6 +8,7 @@ import frappe.sessions
from frappe.utils import cstr
import os, mimetypes, json
import six
from six import iteritems
from werkzeug.wrappers import Response
from werkzeug.routing import Map, Rule, NotFound
@ -276,7 +277,7 @@ def clear_cache(path=None):
frappe.get_attr(method)(path)
def render_403(e, pathname):
frappe.local.message = cstr(e.message)
frappe.local.message = cstr(e.message if six.PY2 else e)
frappe.local.message_title = _("Not Permitted")
frappe.local.response['context'] = dict(
indicator_color = 'red',

View file

@ -20,6 +20,7 @@
"express": "^4.16.2",
"frappe-datatable": "frappe/datatable",
"frappe-gantt": "^0.1.0",
"fuse.js": "^3.2.0",
"moment": "^2.20.1",
"redis": "^2.8.0",
"showdown": "^1.8.6",

View file

@ -629,7 +629,6 @@ frappe-datatable@frappe/datatable:
resolved "https://codeload.github.com/frappe/datatable/tar.gz/b3f281b2146085226ec6741ce30cb9c6cbd6b00c"
dependencies:
clusterize.js "^0.18.0"
lodash "^4.17.5"
sortablejs "^1.7.0"
frappe-gantt@^0.1.0:
@ -658,6 +657,10 @@ fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
fuse.js@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.2.0.tgz#f0448e8069855bf2a3e683cdc1d320e7e2a07ef4"
get-caller-file@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
@ -1005,10 +1008,6 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
lodash@^4.17.5:
version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
lru-cache@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55"
@ -1435,6 +1434,12 @@ rimraf@^2.2.8:
dependencies:
glob "^7.0.5"
rollup-plugin-alias@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-alias/-/rollup-plugin-alias-1.4.0.tgz#120cba7c46621c03138f0ca6fd5dd2ade9872db9"
dependencies:
slash "^1.0.0"
rollup-plugin-buble@^0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/rollup-plugin-buble/-/rollup-plugin-buble-0.19.2.tgz#c0590c7d3d475b5ed59f129764ec93710cc6e8dd"
@ -1574,6 +1579,10 @@ signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
sntp@1.x.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"