diff --git a/app/model/Service.js b/app/model/Service.js index ec4618a8..a7d4a703 100644 --- a/app/model/Service.js +++ b/app/model/Service.js @@ -5,14 +5,26 @@ Ext.define('Rambox.model.Service', { ,proxy: { type: 'localstorage' ,id: 'services' - } - + } + ,fields: [{ name: 'id' ,type: 'int' + },{ + name: 'position' + ,type: 'int' + ,convert: function( value, record ) { + return value ? value : Ext.getStore('Services').getCount() + 1; + } },{ name: 'type' ,type: 'string' + },{ + name: 'logo' + ,type: 'string' + ,convert: function( value, record ) { + return value ? value : record.get('type') + '.png'; + } },{ name: 'name' ,type: 'string' diff --git a/app/store/Services.js b/app/store/Services.js index fc212e74..af26ecae 100644 --- a/app/store/Services.js +++ b/app/store/Services.js @@ -11,33 +11,44 @@ Ext.define('Rambox.store.Services', { ,autoLoad: true ,autoSync: true + ,sorters: [ + { + property: 'position' + ,direction: 'ASC' + } + ] + ,listeners: { load: function( store, records, successful ) { if ( Ext.isEmpty(records) ) { Ext.get('spinner').destroy(); + Ext.cq1('app-main').add({ tabConfig : { xtype : 'tbfill' } }); return; } var servicesLeft = []; var servicesRight = []; - Ext.each(records, function(service) { + store.each(function(service) { var cfg = { xtype: 'webview' ,id: 'tab_'+service.get('id') ,title: service.get('name') - ,icon: 'resources/icons/'+service.get('type')+'.png' + ,icon: service.get('type') !== 'custom' ? 'resources/icons/'+service.get('logo') : service.get('logo') ,src: service.get('url') ,type: service.get('type') ,muted: service.get('muted') + ,tabConfig: { + service: service + } }; service.get('align') === 'left' ? servicesLeft.push(cfg) : servicesRight.push(cfg); }); Ext.cq1('app-main').add(servicesLeft); + Ext.cq1('app-main').add({ tabConfig : { xtype : 'tbfill' } }); if ( !Ext.isEmpty(servicesRight) ) { - Ext.cq1('app-main').add({ tabConfig : { xtype : 'tbfill' } }); Ext.cq1('app-main').add(servicesRight); } } diff --git a/app/ux/WebView.js b/app/ux/WebView.js index a5ae2fd9..2f67a527 100644 --- a/app/ux/WebView.js +++ b/app/ux/WebView.js @@ -52,13 +52,20 @@ Ext.define('Rambox.ux.WebView',{ var me = this; var webview = me.down('component').el.dom; + // Show and hide spinner when is loading + webview.addEventListener("did-start-loading", function() { + console.info('Start loading...', me.src); + me.mask('Loading...'); + }); + webview.addEventListener("did-stop-loading", function() { + me.unmask(); + }); webview.addEventListener("did-finish-load", function(e) { - console.info('finish load'); + Rambox.app.setTotalServicesLoaded( Rambox.app.getTotalServicesLoaded() + 1 ); if ( Rambox.app.getTotalServicesLoaded() === Ext.getStore('Services').getCount() ) { Ext.get('spinner').destroy(); } - Rambox.app.setTotalServicesLoaded( Rambox.app.getTotalServicesLoaded() + 1 ); }); webview.addEventListener("dom-ready", function(e) { diff --git a/app/view/main/Main.js b/app/view/main/Main.js index d29fb56a..5d5dd976 100644 --- a/app/view/main/Main.js +++ b/app/view/main/Main.js @@ -17,14 +17,25 @@ Ext.define('Rambox.view.main.Main', { ,plugins: [ { - ptype: 'tabreorderer' + ptype: 'tabreorderer' + ,listeners: { + // I put the code here because it cannot be listened into the Controller + Drop: function( box, tabBar, tab, startIdx, index ) { + var idx = 0; + Ext.each(tabBar.items.items, function(t) { + if ( idx > 0 && t.xtype !== 'tbfill' ) { // Skip first tab because is the configuration tab + console.log(t, idx); + t.service.set('position', idx); + } else if ( t.xtype === 'tbfill' ) { + idx--; + } + idx++; + }); + } + } } ] - ,tabBar: { - cls: 'allow-overflow' - } - ,hideMode: 'visibility' ,autoRender: true ,autoShow: true ,deferredRender: false @@ -32,8 +43,9 @@ Ext.define('Rambox.view.main.Main', { { icon: 'resources/logo_32.png' ,closable: false - ,layout: 'center' + ,reorderable: false ,autoScroll: true + ,layout: 'center' ,items: [ { xtype: 'container' @@ -50,9 +62,30 @@ Ext.define('Rambox.view.main.Main', { xtype: 'templatecolumn' ,width: 50 ,variableRowHeight: true - ,tpl: '' + ,tpl: '' } ,{ text: 'Name', dataIndex: 'name', variableRowHeight: true, flex: 1 } + ,{ + xtype: 'actioncolumn' + ,width: 60 + ,align: 'right' + ,items: [ + { + glyph: 0xf1f7 + ,tooltip: 'Prevent notifications' + ,getClass: function( value, metaData, record, rowIndex, colIndex, store, view ){ + if ( record.get('notifications') ) return 'x-hidden'; + } + } + ,{ + glyph: 0xf026 + ,tooltip: 'Muted' + ,getClass: function( value, metaData, record, rowIndex, colIndex, store, view ){ + if ( !record.get('muted') ) return 'x-hidden'; + } + } + ] + } ,{ xtype: 'actioncolumn' ,width: 60 @@ -111,7 +144,8 @@ Ext.define('Rambox.view.main.Main', { } ,{ type: 'plus' - ,tooltip: 'Add a custom service (soon...)' + ,tooltip: 'Add a custom service' + ,handler: 'addCustomService' } ] ,items: [ diff --git a/app/view/main/MainController.js b/app/view/main/MainController.js index e7931e43..c1482b91 100644 --- a/app/view/main/MainController.js +++ b/app/view/main/MainController.js @@ -6,16 +6,17 @@ Ext.define('Rambox.view.main.MainController', { ,alias: 'controller.main' - ,showSimpleModal: function(record) { + ,showSimpleModal: function(record, edit) { var me = this; var win = Ext.create('Ext.window.Window', { - title: 'Add '+record.get('name') + title: (edit ? 'Edit ' : 'Add ') + record.get('name') ,modal: true ,width: 400 ,resizable: false ,draggable: false ,bodyPadding: 20 + ,icon: 'resources/icons/' + record.get('logo') ,items: [ { xtype: 'form' @@ -25,6 +26,14 @@ Ext.define('Rambox.view.main.MainController', { ,fieldLabel: 'Name' ,value: record.get('name') ,name: 'serviceName' + ,allowBlank: true + ,listeners: { + specialkey: function(field, e) { + if(e.getKey() == e.ENTER && field.up('form').isValid()) { + field.up('window').down('#submit').handler(); + } + } + } } ,{ xtype: 'fieldset' @@ -34,7 +43,7 @@ Ext.define('Rambox.view.main.MainController', { { xtype: 'checkbox' ,boxLabel: 'Separate' - ,checked: false + ,checked: edit ? (record.get('align') === 'right' ? true : false) : false ,name: 'align' ,uncheckedValue: 'left' ,inputValue: 'right' @@ -43,7 +52,7 @@ Ext.define('Rambox.view.main.MainController', { xtype: 'checkbox' ,boxLabel: 'Show notifications' ,name: 'notifications' - ,checked: true + ,checked: edit ? record.get('notifications') : true ,uncheckedValue: false ,inputValue: true } @@ -51,7 +60,7 @@ Ext.define('Rambox.view.main.MainController', { xtype: 'checkbox' ,boxLabel: 'Mute all sounds' ,name: 'muted' - ,checked: false + ,checked: edit ? record.get('muted') : false ,uncheckedValue: false ,inputValue: true } @@ -70,36 +79,61 @@ Ext.define('Rambox.view.main.MainController', { } ,'->' ,{ - text: 'Add service' + text: edit ? 'Save' : 'Add service' + ,itemId: 'submit' ,handler: function() { var formValues = win.down('form').getValues(); - var service = Ext.getStore('Services').add({ - type: record.get('id') - ,name: formValues.serviceName - ,url: record.get('url') - ,align: formValues.align - ,notifications: formValues.notifications - ,muted: formValues.muted - })[0]; + if ( edit ) { + record.set({ + name: formValues.serviceName + ,align: formValues.align + ,notifications: formValues.notifications + ,muted: formValues.muted + }); + Ext.getCmp('tab_'+record.get('id')).setTitle(formValues.serviceName); + } else { + var service = Ext.create('Rambox.model.Service', { + type: record.get('id') + ,name: formValues.serviceName + ,url: record.get('url') + ,align: formValues.align + ,notifications: formValues.notifications + ,muted: formValues.muted + }); + service.save(); - me.getView().add({ - xtype: 'webview' - ,id: 'tab_'+service.get('id') - ,title: service.get('name') - ,icon: 'resources/icons/'+record.get('logo') - ,src: service.get('url') - ,type: service.get('type') - ,align: formValues.align - ,notifications: formValues.notifications - ,muted: formValues.muted - }).show(); + var tabData = { + xtype: 'webview' + ,id: 'tab_'+service.get('id') + ,title: service.get('name') + ,icon: 'resources/icons/'+record.get('logo') + ,src: service.get('url') + ,type: service.get('type') + ,align: formValues.align + ,notifications: formValues.notifications + ,muted: formValues.muted + ,tabConfig: { + service: service + } + }; + + if ( formValues.align === 'left' ) { + var tbfill = me.getView().getTabBar().down('tbfill'); + me.getView().insert(me.getView().getTabBar().items.indexOf(tbfill), tabData).show(); + } else { + me.getView().add(tabData).show(); + } + } win.close(); } } ] }).show(); + + // Make focus to the name field + win.down('textfield[name="serviceName"]').focus(true, 100); } ,showCustomModal: function(record) { @@ -112,6 +146,7 @@ Ext.define('Rambox.view.main.MainController', { ,resizable: false ,draggable: false ,bodyPadding: 20 + ,icon: 'resources/icons/' + record.get('logo') ,items: [ { xtype: 'form' @@ -121,6 +156,14 @@ Ext.define('Rambox.view.main.MainController', { ,fieldLabel: 'Name' ,value: record.get('name') ,name: 'serviceName' + ,allowBlank: true + ,listeners: { + specialkey: function(field, e) { + if(e.getKey() == e.ENTER && field.up('form').isValid()) { + field.up('window').down('#submit').handler(); + } + } + } } ,{ xtype: 'container' @@ -129,6 +172,14 @@ Ext.define('Rambox.view.main.MainController', { xtype: 'textfield' ,fieldLabel: record.get('name') + ' team' ,name: 'url' + ,allowBlank: false + ,listeners: { + specialkey: function(field, e) { + if(e.getKey() == e.ENTER && field.up('form').isValid()) { + field.up('window').down('#submit').handler(); + } + } + } },{ xtype: 'displayfield' ,value: record.get('url').split('___')[1].slice(0, -1) // Get the URL and remove the final slash (/) @@ -180,19 +231,21 @@ Ext.define('Rambox.view.main.MainController', { ,'->' ,{ text: 'Add service' + ,itemId: 'submit' ,handler: function() { var formValues = win.down('form').getValues(); - var service = Ext.getStore('Services').add({ + var service = Ext.create('Rambox.model.Service', { type: record.get('id') ,name: formValues.serviceName ,url: record.get('url').replace('___', formValues.url) ,align: formValues.align ,notifications: formValues.notifications ,muted: formValues.muted - })[0]; + }); + service.save(); - me.getView().add({ + var tabData = { xtype: 'webview' ,id: 'tab_'+service.get('id') ,title: service.get('name') @@ -202,20 +255,33 @@ Ext.define('Rambox.view.main.MainController', { ,align: formValues.align ,notifications: formValues.notifications ,muted: formValues.muted - }).show(); + ,tabConfig: { + service: service + } + }; + + if ( formValues.align === 'left' ) { + var tbfill = me.getView().getTabBar().down('tbfill'); + me.getView().insert(me.getView().getTabBar().items.indexOf(tbfill), tabData).show(); + } else { + me.getView().add(tabData).show(); + } win.close(); } } ] }).show(); + + // Make focus to the name field + win.down('textfield[name="serviceName"]').focus(true, 100); } ,onNewServiceSelect: function( view, record, item, index, e ) { if ( record.get('url').indexOf('___') >= 0 ) { this.showCustomModal(record); } else { - this.showSimpleModal(record); + this.showSimpleModal(record, false); } } @@ -223,11 +289,16 @@ Ext.define('Rambox.view.main.MainController', { Ext.Msg.confirm('Please confirm...', 'Are you sure you want to remove '+rec.get('name')+'?', function(btnId) { if ( btnId === 'yes' ) { gridView.getStore().remove(rec); + //webview.webContents.session.clearCache(); Ext.getCmp('tab_'+rec.get('id')).close(); } }); } + ,configureService: function( gridView, rowIndex, colIndex, col, e, rec, rowEl ) { + this.showSimpleModal(rec, true); + } + ,doFilter: function( cg, newValue, oldValue ) { var values = Ext.cq1('checkboxgroup').getValue(); Ext.getStore('ServicesList').getFilters().replaceAll({ @@ -236,4 +307,151 @@ Ext.define('Rambox.view.main.MainController', { } }); } + + ,addCustomService: function( event, toolEl, owner, tool ) { + var me = this; + + var win = Ext.create('Ext.window.Window', { + title: 'Add Custom Service' + ,modal: true + ,width: 400 + ,resizable: false + ,draggable: false + ,bodyPadding: 20 + ,items: [ + { + xtype: 'form' + ,items: [ + { + xtype: 'textfield' + ,fieldLabel: 'Name' + ,name: 'serviceName' + ,allowBlank: true + ,listeners: { + specialkey: function(field, e) { + if(e.getKey() == e.ENTER && field.up('form').isValid()) { + field.up('window').down('#submit').handler(); + } + } + } + } + ,{ + xtype: 'textfield' + ,fieldLabel: 'URL' + ,emptyText: 'http://service.url.com' + ,name: 'url' + ,vtype: 'url' + ,listeners: { + specialkey: function(field, e) { + if(e.getKey() == e.ENTER && field.up('form').isValid()) { + field.up('window').down('#submit').handler(); + } + } + } + } + ,{ + xtype: 'textfield' + ,fieldLabel: 'Logo' + ,emptyText: 'http://image.url.com/image.png' + ,name: 'logo' + ,vtype: 'url' + ,listeners: { + specialkey: function(field, e) { + if(e.getKey() == e.ENTER && field.up('form').isValid()) { + field.up('window').down('#submit').handler(); + } + } + } + } + ,{ + xtype: 'fieldset' + ,title: 'Options' + ,margin: '10 0 0 0' + ,items: [ + { + xtype: 'checkbox' + ,boxLabel: 'Separate' + ,checked: false + ,name: 'align' + ,uncheckedValue: 'left' + ,inputValue: 'right' + } + ,{ + xtype: 'checkbox' + ,boxLabel: 'Show notifications' + ,name: 'notifications' + ,checked: true + ,uncheckedValue: false + ,inputValue: true + } + ,{ + xtype: 'checkbox' + ,boxLabel: 'Mute all sounds' + ,name: 'muted' + ,checked: false + ,uncheckedValue: false + ,inputValue: true + } + ] + } + ] + } + ] + ,buttons: [ + { + text: 'Cancel' + ,ui: 'decline' + ,handler: function() { + win.close(); + } + } + ,'->' + ,{ + text: 'Add service' + ,itemId: 'submit' + ,handler: function() { + var formValues = win.down('form').getValues(); + + var service = Ext.create('Rambox.model.Service', { + type: 'custom' + ,logo: formValues.logo + ,name: formValues.serviceName + ,url: formValues.url + ,align: formValues.align + ,notifications: formValues.notifications + ,muted: formValues.muted + }); + service.save(); + + var tabData = { + xtype: 'webview' + ,id: 'tab_'+service.get('id') + ,title: service.get('name') + ,icon: formValues.logo + ,src: service.get('url') + ,type: service.get('type') + ,align: formValues.align + ,notifications: formValues.notifications + ,muted: formValues.muted + ,tabConfig: { + service: service + } + }; + + if ( formValues.align === 'left' ) { + var tbfill = me.getView().getTabBar().down('tbfill'); + me.getView().insert(me.getView().getTabBar().items.indexOf(tbfill), tabData).show(); + } else { + me.getView().add(tabData).show(); + } + + win.close(); + } + } + ] + }).show(); + + // Make focus to the name field + win.down('textfield[name="serviceName"]').focus(true, 100); + } }); diff --git a/index.html b/index.html index b649b85f..ce660ba0 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,7 @@
+ Rambox
diff --git a/packages/local/rambox-default-theme/sass/etc/all.scss b/packages/local/rambox-default-theme/sass/etc/all.scss index 34b2ff42..0a2ea3ef 100644 --- a/packages/local/rambox-default-theme/sass/etc/all.scss +++ b/packages/local/rambox-default-theme/sass/etc/all.scss @@ -15,15 +15,28 @@ body { z-index: 999999; position: absolute; background-color: $base-color; + cursor: wait; + font { + font-family: 'Josefin Sans', sans-serif; + font-size: 150px; + top: 30%; + color: #FFF; + position: absolute; + width: 100%; + height: 120px; + line-height: 132px; + text-align: center; + } } .sk-folding-cube { - top: 50%; - margin: -20px auto 20px auto; - width: 80px; - height: 80px; - position: relative; - -webkit-transform: rotateZ(45deg); - transform: rotateZ(45deg); + top: 50%; + left: 50%; + width: 80px; + height: 80px; + margin-left: -40px; + position: absolute; + -webkit-transform: rotateZ(45deg); + transform: rotateZ(45deg); } .sk-folding-cube .sk-cube { @@ -107,57 +120,57 @@ body { // Badge Plugin for Tabs .x-badge { - position: relative; - overflow: visible; + position: relative; + overflow: visible; } .x-badge[data-badge-text]:after { - content: attr(data-badge-text); - position: absolute; - font-size: 10px; - top: -6px; - right: 2px; - width: auto; - font-weight: bold; - color: white; - text-shadow: rgba(0, 0, 0, 0.5) 0 -0.08em 0; - -webkit-border-radius: 3px; - border-radius: 3px; - padding: 0 4px; - background-image: none; - background-color: #C00; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff1a1a), color-stop(3%, #e60000), color-stop(100%, #b30000)); - background-image: -webkit-linear-gradient(top, #ff1a1a,#e60000 3%,#b30000); - background-image: linear-gradient(top, #ff1a1a,#e60000 3%,#b30000); - -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 0.1em 0.1em; - box-shadow: rgba(0, 0, 0, 0.3) 0 0.1em 0.1em; + content: attr(data-badge-text); + position: absolute; + font-size: 10px; + top: 0px; + right: 2px; + width: auto; + font-weight: bold; + color: white; + text-shadow: rgba(0, 0, 0, 0.5) 0 -0.08em 0; + -webkit-border-radius: 3px; + border-radius: 3px; + padding: 0 4px; + background-image: none; + background-color: #C00; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff1a1a), color-stop(3%, #e60000), color-stop(100%, #b30000)); + background-image: -webkit-linear-gradient(top, #ff1a1a,#e60000 3%,#b30000); + background-image: linear-gradient(top, #ff1a1a,#e60000 3%,#b30000); + -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 0.1em 0.1em; + box-shadow: rgba(0, 0, 0, 0.3) 0 0.1em 0.1em; } .x-badge.green-badge[data-badge-text]:after { - background-color: #0C0; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #1AFF1A), color-stop(3%, #00E600), color-stop(100%, #00B300)); - background-image: -webkit-linear-gradient(top, #1AFF1A,#00E600 3%,#00B300); - background-image: linear-gradient(top, #1AFF1A,#00E600 3%,#00B300); + background-color: #0C0; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #1AFF1A), color-stop(3%, #00E600), color-stop(100%, #00B300)); + background-image: -webkit-linear-gradient(top, #1AFF1A,#00E600 3%,#00B300); + background-image: linear-gradient(top, #1AFF1A,#00E600 3%,#00B300); } .x-badge.blue-badge[data-badge-text]:after { - background-color: #00C; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #1A1AFF), color-stop(3%, #0000E6), color-stop(100%, #0000B3)); - background-image: -webkit-linear-gradient(top, #1A1AFF,#0000E6 3%,#0000B3); - background-image: linear-gradient(top, #1A1AFF,#0000E6 3%,#0000B3); + background-color: #00C; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #1A1AFF), color-stop(3%, #0000E6), color-stop(100%, #0000B3)); + background-image: -webkit-linear-gradient(top, #1A1AFF,#0000E6 3%,#0000B3); + background-image: linear-gradient(top, #1A1AFF,#0000E6 3%,#0000B3); } /* Additional classes needed for tab panels */ .allow-overflow .x-box-layout-ct, .allow-overflow .x-box-inner, .allow-overflow .x-box-item { - overflow: visible; + overflow: visible; } .x-tab-closable.x-badge[data-badge-text]:after { - right: 16px; + right: 16px; } // Allow Glyphs in Action Columns -.x-action-col-glyph {font-size:20px; line-height:20px; color:#000; width:20px; margin: 0 5px;} +.x-action-col-glyph {font-size:16px; line-height:16px; color:#CFCFCF; width:16px; margin: 0 5px;} .x-action-col-glyph:hover{color:$base-color} .x-grid-item-over .x-hidden-display, .x-grid-item-selected .x-hidden-display{display:inline-block !important} .x-grid-cell-inner { @@ -168,6 +181,11 @@ body { } } .x-tab-icon-el-default { background-size: 24px; } +.x-title-icon { background-size: 16px; } + +// Fix for scroller tab bar +.x-box-scroller-body-horizontal { width: auto !important; margin-right: 18px;} +.x-box-scroller-tab-bar { z-index: 9999; } .service { width:250px;