Restore two column layout
Establishes basic functionality for viewing conversations in two column mode, including message area and message list resizing, and maintaining scroll position. Various subviews need to be retooled but are more or less still functional, i.e., new message, message detail, key verification, etc...
This commit is contained in:
parent
00dfcbb462
commit
d6a4e6e496
10 changed files with 134 additions and 75 deletions
|
@ -16,12 +16,29 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<script type='text/x-tmpl-mustache' id='two-column'>
|
||||
<div class='title-bar' id='header'>
|
||||
<div class='menu'>
|
||||
<button class='hamburger'></button>
|
||||
<ul class='menu-list'>
|
||||
<li><a class='new-group'>Create Group</a></li>
|
||||
<li><a class='settings'>Settings</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<span class='conversation-title'>Signal</span>
|
||||
<div class='socket-status'></div>
|
||||
</div>
|
||||
<div class='gutter'>
|
||||
<div class='conversations scrollable'></div>
|
||||
<span class='fab'></span>
|
||||
</div>
|
||||
<div class='conversation-stack'></div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='conversation'>
|
||||
<div class='title-bar' id='header'>
|
||||
<div class='menu'>
|
||||
<div class='conversation-header'>
|
||||
<div class='conversation-menu menu'>
|
||||
<button class='hamburger'></button>
|
||||
<ul class='menu-list'>
|
||||
<li><a class='openInbox'>Open Inbox</a></li>
|
||||
{{#group}}
|
||||
<li><a class='view-members'>Members</a></li>
|
||||
<li><a class='new-group-update'>Update group</a></li>
|
||||
|
|
|
@ -117,45 +117,7 @@
|
|||
conversation.fetchContacts();
|
||||
});
|
||||
conversation.fetchMessages();
|
||||
|
||||
var windowId = windowMap.windowIdFrom(modelId);
|
||||
|
||||
// prevent multiple copies of the same conversation from being opened
|
||||
if (!windowId) {
|
||||
// open the panel
|
||||
extension.windows.open({
|
||||
id: modelId,
|
||||
url: 'conversation.html',
|
||||
type: 'panel',
|
||||
frame: 'none',
|
||||
focused: true,
|
||||
width: 300,
|
||||
height: 440,
|
||||
minWidth: 230,
|
||||
minHeight: 73
|
||||
}, function (windowInfo) {
|
||||
windowId = windowInfo.id;
|
||||
windowMap.add({ windowId: windowId, modelId: modelId });
|
||||
|
||||
windowInfo.onClosed.addListener(function () {
|
||||
onWindowClosed(windowId);
|
||||
});
|
||||
|
||||
// close the panel if background.html is refreshed
|
||||
extension.windows.beforeUnload(function() {
|
||||
// TODO: reattach after reload instead of closing.
|
||||
extension.windows.remove(windowId);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// focus the panel
|
||||
extension.windows.focus(windowId, function (error) {
|
||||
if (error) {
|
||||
closeConversation(windowId); // panel isn't actually open...
|
||||
openConversation(modelId); // ...and so we try again.
|
||||
}
|
||||
});
|
||||
}
|
||||
return conversation;
|
||||
};
|
||||
|
||||
/* Inbox window controller */
|
||||
|
@ -170,7 +132,7 @@
|
|||
type: 'panel',
|
||||
frame: 'none',
|
||||
focused: true,
|
||||
width: 260,
|
||||
width: 580,
|
||||
height: 440,
|
||||
minWidth: 230,
|
||||
minHeight: 150
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
className: function() {
|
||||
return [ 'conversation', this.model.get('type') ].join(' ');
|
||||
},
|
||||
id: function() {
|
||||
return 'conversation-' + this.model.cid;
|
||||
},
|
||||
template: $('#conversation').html(),
|
||||
render_attributes: function() {
|
||||
return {
|
||||
|
@ -36,10 +39,6 @@
|
|||
this.render();
|
||||
|
||||
this.appWindow = options.appWindow;
|
||||
new Whisper.WindowControlsView({
|
||||
appWindow: this.appWindow
|
||||
}).$el.insertAfter(this.$('.menu'));
|
||||
|
||||
this.fileInput = new Whisper.FileInputView({
|
||||
el: this.$('.attachments'),
|
||||
window: this.appWindow.contentWindow
|
||||
|
@ -85,7 +84,8 @@
|
|||
'click .hamburger': 'toggleMenu',
|
||||
'click .openInbox' : 'openInbox',
|
||||
'click' : 'onClick',
|
||||
'select .entry': 'messageDetail'
|
||||
'select .entry': 'messageDetail',
|
||||
'force-resize': 'forceUpdateMessageFieldSize'
|
||||
},
|
||||
|
||||
viewMembers: function() {
|
||||
|
@ -235,12 +235,15 @@
|
|||
|
||||
$bottomBar.outerHeight(this.$messageField.outerHeight() + 1);
|
||||
var $bottomBarNewHeight = $bottomBar.outerHeight();
|
||||
$discussionContainer.outerHeight(this.$el.outerHeight() - $bottomBarNewHeight - this.$('#header').outerHeight());
|
||||
$discussionContainer.outerHeight(this.$el.outerHeight() - $bottomBarNewHeight - this.$('.conversation-header').outerHeight());
|
||||
|
||||
this.view.scrollToBottomIfNeeded();
|
||||
},
|
||||
|
||||
forceUpdateMessageFieldSize: function (event) {
|
||||
if (this.$el.css('display') === 'none') {
|
||||
return;
|
||||
}
|
||||
this.view.scrollToBottomIfNeeded();
|
||||
window.autosize.update(this.$messageField);
|
||||
this.updateMessageFieldSize(event);
|
||||
|
|
|
@ -56,11 +56,32 @@
|
|||
}
|
||||
});
|
||||
|
||||
Whisper.ConversationStack = Whisper.View.extend({
|
||||
className: 'conversation-stack',
|
||||
open: function(conversation) {
|
||||
var $el = this.$('#conversation-' + conversation.cid);
|
||||
if ($el === null || $el.length === 0) {
|
||||
var view = new Whisper.ConversationView({
|
||||
model: conversation,
|
||||
appWindow: this.model.appWindow
|
||||
});
|
||||
$el = view.$el;
|
||||
}
|
||||
$el.prependTo(this.el);
|
||||
$el.find('.message-list').trigger('reset-scroll');
|
||||
$el.trigger('force-resize');
|
||||
}
|
||||
});
|
||||
|
||||
Whisper.InboxView = Whisper.View.extend({
|
||||
template: $('#inbox').html(),
|
||||
template: $('#two-column').html(),
|
||||
className: 'inbox',
|
||||
initialize: function (options) {
|
||||
this.render();
|
||||
this.conversation_stack = new Whisper.ConversationStack({
|
||||
el: this.$('.conversation-stack'),
|
||||
model: { appWindow: options.appWindow }
|
||||
});
|
||||
|
||||
this.newConversationView = new Whisper.NewConversationView({
|
||||
appWindow: options.appWindow
|
||||
|
@ -91,7 +112,8 @@
|
|||
'select .contact': 'openConversation',
|
||||
},
|
||||
openConversation: function(e, data) {
|
||||
bg.openConversation(data.modelId);
|
||||
var conversation = bg.openConversation(data.modelId);
|
||||
this.conversation_stack.open(conversation);
|
||||
this.hideCompose();
|
||||
},
|
||||
showCompose: function() {
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
'use strict';
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
var scrollPosition,
|
||||
scrollHeight,
|
||||
shouldStickToBottom;
|
||||
|
||||
Whisper.MessageListView = Whisper.ListView.extend({
|
||||
tagName: 'ul',
|
||||
className: 'message-list',
|
||||
|
@ -28,16 +24,24 @@
|
|||
events: {
|
||||
'add': 'scrollToBottom',
|
||||
'update *': 'scrollToBottom',
|
||||
'scroll': 'measureScrollPosition'
|
||||
'scroll': 'measureScrollPosition',
|
||||
'reset-scroll': 'resetScrollPosition'
|
||||
},
|
||||
measureScrollPosition: function() {
|
||||
scrollPosition = this.$el.scrollTop() + this.$el.outerHeight();
|
||||
scrollHeight = this.el.scrollHeight;
|
||||
shouldStickToBottom = scrollPosition === scrollHeight;
|
||||
this.scrollPosition = this.$el.scrollTop() + this.$el.outerHeight();
|
||||
this.scrollHeight = this.el.scrollHeight;
|
||||
this.shouldStickToBottom = this.scrollPosition === this.scrollHeight;
|
||||
},
|
||||
resetScrollPosition: function() {
|
||||
var scrollPosition = this.scrollPosition;
|
||||
if (this.scrollHeight !== this.el.scrollHeight) {
|
||||
scrollPosition = this.el.scrollHeight * this.scrollPosition / this.scrollHeight;
|
||||
}
|
||||
this.$el.scrollTop(scrollPosition - this.$el.outerHeight());
|
||||
},
|
||||
scrollToBottomIfNeeded: function() {
|
||||
if (shouldStickToBottom) {
|
||||
this.$el.scrollTop(scrollHeight);
|
||||
if (this.shouldStickToBottom) {
|
||||
this.$el.scrollTop(this.scrollHeight);
|
||||
}
|
||||
},
|
||||
scrollToBottom: function() {
|
||||
|
|
|
@ -341,10 +341,7 @@
|
|||
.bottom-bar {
|
||||
$button-width: 36px;
|
||||
|
||||
position: fixed;
|
||||
bottom: 1px; // offset 1 for window frame.
|
||||
height: 36px;
|
||||
width: calc(100% - 2px);
|
||||
border-top: 1px solid $grey_l;
|
||||
background: white;
|
||||
|
||||
|
|
|
@ -91,7 +91,8 @@ button.back {
|
|||
padding-right: 8px;
|
||||
|
||||
.hamburger {
|
||||
width: 36px;
|
||||
width: $header-height;
|
||||
height: $header-height;
|
||||
background: url('/images/menu.png') no-repeat center;
|
||||
}
|
||||
.menu-list {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
padding: $header-height 0 0;
|
||||
}
|
||||
|
||||
.conversation-stack,
|
||||
.new-conversation, .inbox, .gutter {
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -14,6 +15,11 @@
|
|||
// TODO: spinner
|
||||
}
|
||||
|
||||
.gutter {
|
||||
float: left;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.socket-status {
|
||||
float: left;
|
||||
padding: 6px;
|
||||
|
@ -40,6 +46,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
.conversation-stack {
|
||||
padding-left: 300px;
|
||||
padding-top: $header-height;
|
||||
.conversation {
|
||||
display: none;
|
||||
}
|
||||
.conversation:first-child {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation-header {
|
||||
background: $grey_l;
|
||||
}
|
||||
.menu.conversation-menu {
|
||||
float: right;
|
||||
padding-left: 8px;
|
||||
padding-right: 0;
|
||||
|
||||
.menu-list {
|
||||
right: 0;
|
||||
left: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.contact {
|
||||
.number, .checkbox {
|
||||
display: none;
|
||||
|
@ -56,7 +87,7 @@ input.search {
|
|||
.fab {
|
||||
z-index: 1;
|
||||
position: fixed;
|
||||
right: 25px;;
|
||||
left: 215px;;
|
||||
bottom: 22px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
|
@ -99,7 +130,7 @@ input.search {
|
|||
color: $grey_d;
|
||||
background: #eee;
|
||||
|
||||
.new-group-update-form {
|
||||
.gutter .new-group-update-form {
|
||||
display: none;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
@ -110,7 +141,7 @@ input.search {
|
|||
font-weight: 300;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
.gutter .timestamp {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 12px;
|
||||
|
|
|
@ -90,6 +90,7 @@ button.back {
|
|||
padding-right: 8px; }
|
||||
.menu .hamburger {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: url("/images/menu.png") no-repeat center; }
|
||||
.menu .menu-list {
|
||||
display: none;
|
||||
|
@ -330,6 +331,7 @@ img.emoji {
|
|||
.gutter {
|
||||
padding: 36px 0 0; }
|
||||
|
||||
.conversation-stack,
|
||||
.new-conversation, .inbox, .gutter {
|
||||
height: 100%; }
|
||||
|
||||
|
@ -337,6 +339,10 @@ img.emoji {
|
|||
height: 100%;
|
||||
overflow: auto; }
|
||||
|
||||
.gutter {
|
||||
float: left;
|
||||
max-width: 300px; }
|
||||
|
||||
.socket-status {
|
||||
float: left;
|
||||
padding: 6px;
|
||||
|
@ -355,6 +361,25 @@ img.emoji {
|
|||
.socket-status .closed {
|
||||
background: url("/images/error_red.png") no-repeat left center; }
|
||||
|
||||
.conversation-stack {
|
||||
padding-left: 300px;
|
||||
padding-top: 36px; }
|
||||
.conversation-stack .conversation {
|
||||
display: none; }
|
||||
.conversation-stack .conversation:first-child {
|
||||
display: block; }
|
||||
|
||||
.conversation-header {
|
||||
background: #f3f3f3; }
|
||||
|
||||
.menu.conversation-menu {
|
||||
float: right;
|
||||
padding-left: 8px;
|
||||
padding-right: 0; }
|
||||
.menu.conversation-menu .menu-list {
|
||||
right: 0;
|
||||
left: initial; }
|
||||
|
||||
.contact .number, .contact .checkbox {
|
||||
display: none; }
|
||||
|
||||
|
@ -367,7 +392,7 @@ input.search {
|
|||
.fab {
|
||||
z-index: 1;
|
||||
position: fixed;
|
||||
right: 25px;
|
||||
left: 215px;
|
||||
bottom: 22px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
|
@ -401,14 +426,14 @@ input.search {
|
|||
.index {
|
||||
color: #454545;
|
||||
background: #eee; }
|
||||
.index .new-group-update-form {
|
||||
.index .gutter .new-group-update-form {
|
||||
display: none;
|
||||
padding: 0.5em; }
|
||||
.index .last-message {
|
||||
margin: 6px 0;
|
||||
font-size: small;
|
||||
font-weight: 300; }
|
||||
.index .timestamp {
|
||||
.index .gutter .timestamp {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 12px;
|
||||
|
@ -679,10 +704,7 @@ input.search {
|
|||
cursor: pointer; }
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 1px;
|
||||
height: 36px;
|
||||
width: calc(100% - 2px);
|
||||
border-top: 1px solid #f3f3f3;
|
||||
background: white; }
|
||||
.bottom-bar button, .bottom-bar input, .bottom-bar textarea {
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue