diff --git a/app.js b/app.js
index 0eb80cb7..6430d718 100644
--- a/app.js
+++ b/app.js
@@ -22,20 +22,6 @@ Ext.application({
,autoCreateViewport: 'Rambox.view.main.Main'
});
-// Syncronize with Firebase
-function sync() {
- // Is not logged, Skip
- if ( !localStorage.getItem('id_token') ) return;
-
- var services = [];
- Ext.getStore('Services').each(function(service) {
- services.push(service.data);
- });
- fireRef.database().ref('users/' + Ext.decode(localStorage.getItem('profile')).user_id).set({
- services: services
- });
-}
-
require('electron').ipcRenderer.on('showAbout', function(event, message) {
!Ext.cq1('about') ? Ext.create('Rambox.view.main.About') : '';
});
diff --git a/app/Application.js b/app/Application.js
index 02374eb0..31a18418 100644
--- a/app/Application.js
+++ b/app/Application.js
@@ -3,11 +3,20 @@ Ext.define('Rambox.Application', {
,name: 'Rambox'
+ ,requires: [
+ 'Rambox.ux.Firebase'
+ ]
+
,stores: [
'ServicesList'
,'Services'
]
+ ,profiles: [
+ 'Offline'
+ ,'Online'
+ ]
+
,config: {
totalServicesLoaded: 0
,totalNotifications: 0
diff --git a/app/model/Service.js b/app/model/Service.js
index da0127b9..7f652c9a 100644
--- a/app/model/Service.js
+++ b/app/model/Service.js
@@ -13,9 +13,6 @@ Ext.define('Rambox.model.Service', {
},{
name: 'position'
,type: 'int'
- ,convert: function( value, record ) {
- return value ? value : Ext.getStore('Services').getCount() + 1;
- }
},{
name: 'type'
,type: 'string'
@@ -44,5 +41,8 @@ Ext.define('Rambox.model.Service', {
name: 'js_unread'
,type: 'string'
,defaultValue: ''
+ },{
+ name: 'firebase_key'
+ ,type: 'int'
}]
});
diff --git a/app/profile/Offline.js b/app/profile/Offline.js
new file mode 100644
index 00000000..bffd9015
--- /dev/null
+++ b/app/profile/Offline.js
@@ -0,0 +1,10 @@
+Ext.define('Rambox.profile.Offline', {
+ extend: 'Ext.app.Profile'
+ ,isActive: function() {
+ return !localStorage.getItem('id_token');
+ }
+
+ ,launch: function() {
+ console.warn('USER NOT LOGGED IN');
+ }
+});
diff --git a/app/profile/Online.js b/app/profile/Online.js
new file mode 100644
index 00000000..bce6c502
--- /dev/null
+++ b/app/profile/Online.js
@@ -0,0 +1,12 @@
+Ext.define('Rambox.profile.Online', {
+ extend: 'Ext.app.Profile'
+ ,isActive: function() {
+ return localStorage.getItem('id_token');
+ }
+
+ ,launch: function() {
+ console.info('USER LOGGED IN');
+
+ Rambox.ux.Firebase.createEvents(false);
+ }
+});
diff --git a/app/store/Services.js b/app/store/Services.js
index 4db0a0bb..95cae6ea 100644
--- a/app/store/Services.js
+++ b/app/store/Services.js
@@ -20,10 +20,7 @@ Ext.define('Rambox.store.Services', {
,listeners: {
load: function( store, records, successful ) {
- if ( Ext.isEmpty(records) ) {
- Ext.cq1('app-main').add({ tabConfig : { xtype : 'tbfill' } });
- return;
- }
+ Ext.cq1('app-main').suspendEvent('add');
var servicesLeft = [];
var servicesRight = [];
@@ -45,21 +42,56 @@ Ext.define('Rambox.store.Services', {
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(servicesLeft) ) Ext.cq1('app-main').insert(1, servicesLeft);
+ if ( !Ext.isEmpty(servicesRight) ) Ext.cq1('app-main').add(servicesRight);
- if ( !Ext.isEmpty(servicesRight) ) {
- Ext.cq1('app-main').add(servicesRight);
- }
+ store.suspendEvent('load');
+ Ext.cq1('app-main').resumeEvent('add');
}
,add: function(store, records, index) {
- sync();
+ var record = records[0];
+ if ( !localStorage.getItem('id_token') || (!Ext.isEmpty(record.previousValues) && !Ext.isEmpty(record.previousValues.position)) ) return true;
+
+ console.info('Saving into Firebase...', record.data);
+
+ var ref = fireRef.database().ref('test/' + Ext.decode(localStorage.getItem('profile')).user_id).child('services');
+
+ ref.once('value', function(snap) {
+ // Generate Key
+ var i = 0;
+ while ( snap.child(i).exists() ) { i++; }
+
+ // Save Firebase Key into record
+ record.set('firebase_key', i);
+
+ // Prevent saving local ID and Firebase Key into Firebase
+ var data = Ext.clone(record.data);
+ delete data.id;
+ delete data.firebase_key;
+
+ // Make the call
+ ref.child(i).set(data);
+ });
}
,update: function(store, record, operation, data) {
- if ( operation === 'edit' ) sync();
+ // Is not logged, Skip
+ if ( !localStorage.getItem('id_token') || operation === 'commit' ) return;
+
+ if ( operation === 'edit' && data[0] !== 'firebase_key' ) {
+ var obj = {};
+ Ext.each(data, function(prop) {
+ obj[prop] = record.get(prop);
+ });
+
+ fireRef.database().ref('test/' + Ext.decode(localStorage.getItem('profile')).user_id + '/services').child(record.get('firebase_key')).update(obj);
+ }
}
,remove: function(store, records, index, isMove) {
- sync();
+ if ( !localStorage.getItem('id_token') ) return;
+
+ Ext.each(records, function(rec) {
+ fireRef.database().ref('test/' + Ext.decode(localStorage.getItem('profile')).user_id).child('services').child(rec.get('firebase_key')).remove();
+ });
}
}
});
diff --git a/app/ux/Firebase.js b/app/ux/Firebase.js
new file mode 100644
index 00000000..83d4d3d5
--- /dev/null
+++ b/app/ux/Firebase.js
@@ -0,0 +1,107 @@
+Ext.define('Rambox.ux.Firebase', {
+ singleton: true
+
+ // private
+ ,eventsDefined: false
+
+ ,createEvents: function(createTabs) {
+ if ( this.eventsDefined || !localStorage.getItem('id_token') ) return;
+
+ console.log('Define listeners for Firebase');
+
+ var ref = fireRef.database().ref('test/' + Ext.decode(localStorage.getItem('profile')).user_id).child('services');
+
+ // Attach an asynchronous callback to read the data at our posts reference
+ ref.on("child_changed", function(snapshot, prevChildKey) {
+ console.info('Firebase - Child Changed', snapshot.val(), snapshot.key, prevChildKey);
+
+ Ext.getStore('Services').suspendEvent('update');
+ var rec = Ext.getStore('Services').findRecord('firebase_key', snapshot.key);
+
+
+ Ext.getCmp('tab_'+rec.get('id')).setTitle(snapshot.val().name);
+
+ if ( rec.get('position') !== snapshot.val().position ) {
+ var pos = parseInt(snapshot.val().position);
+ if ( rec.get('align') === 'right' ) pos++;
+ Ext.cq1('app-main').move(Ext.getCmp('tab_'+rec.get('id')), pos);
+ }
+
+ rec.set(snapshot.val());
+ rec.save();
+ Ext.getStore('Services').resumeEvent('update');
+ Ext.getStore('Services').load();
+ }, function (errorObject) {
+
+ });
+
+ ref.on("child_added", function(snapshot, prevChildKey) {
+ console.info('Firebase - Child Added', snapshot.val(), snapshot.key, prevChildKey);
+
+ Ext.getStore('Services').suspendEvents(['add', 'update']);
+ var rec = Ext.getStore('Services').findRecord('firebase_key', snapshot.key);
+
+ // Update current services
+ if ( rec ) {
+ rec.set(snapshot.val());
+ rec.save();
+ } else { // Add new ones if exist
+ var data = snapshot.val();
+ data.firebase_key = snapshot.key;
+ rec = Ext.create('Rambox.model.Service', data);
+ rec.save();
+ Ext.getStore('Services').add(rec);
+
+ var tabData = {
+ xtype: 'webview'
+ ,id: 'tab_'+rec.get('id')
+ ,title: rec.get('name')
+ ,icon: rec.get('type') !== 'custom' ? 'resources/icons/'+rec.get('logo') : ( rec.get('logo') === '' ? 'resources/icons/custom.png' : rec.get('logo'))
+ ,src: rec.get('url')
+ ,type: rec.get('type')
+ ,align: rec.get('align')
+ ,notifications: rec.get('notifications')
+ ,muted: rec.get('muted')
+ ,record: rec
+ ,tabConfig: {
+ service: rec
+ }
+ };
+
+ if ( rec.get('align') === 'left' ) {
+ var tbfill = Ext.cq1('app-main').getTabBar().down('tbfill');
+ Ext.cq1('app-main').insert(Ext.cq1('app-main').getTabBar().items.indexOf(tbfill), tabData);
+ } else {
+ Ext.cq1('app-main').add(tabData);
+ }
+ }
+
+ Ext.getStore('Services').resumeEvents(['add', 'update']);
+ Ext.getStore('Services').load();
+ //rec.commit();
+ }, function (errorObject) {
+
+ });
+
+ ref.on("child_removed", function(snapshot) {
+ console.info('Firebase - Child Removed', snapshot.val(), snapshot.key);
+
+ var rec = Ext.getStore('Services').findRecord('firebase_key', snapshot.key);
+
+ // Remove record from localStorage
+ if ( rec ) {
+ Ext.getStore('Services').suspendEvent('remove');
+ Ext.getStore('Services').remove(rec);
+ Ext.cq1('app-main').suspendEvent('remove');
+ Ext.getCmp('tab_'+rec.get('id')).close();
+ Ext.cq1('app-main').resumeEvent('remove');
+ Ext.getStore('Services').resumeEvent('remove');
+ Ext.getStore('Services').load();
+ }
+ }, function (errorObject) {
+
+ });
+
+ this.eventsDefined = true;
+ }
+});
diff --git a/app/view/main/Main.js b/app/view/main/Main.js
index 9f54787f..c5e8a012 100644
--- a/app/view/main/Main.js
+++ b/app/view/main/Main.js
@@ -18,20 +18,6 @@ Ext.define('Rambox.view.main.Main', {
,plugins: [
{
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
- t.card.record.set('position', idx);
- } else if ( t.xtype === 'tbfill' ) {
- idx--;
- }
- idx++;
- });
- }
- }
}
]
@@ -41,6 +27,7 @@ Ext.define('Rambox.view.main.Main', {
,items: [
{
icon: 'resources/IconTray@2x.png'
+ ,id: 'ramboxTab'
,closable: false
,reorderable: false
,autoScroll: true
@@ -289,6 +276,9 @@ Ext.define('Rambox.view.main.Main', {
]
,listeners: {
- tabchange: 'onTabChange'
+ tabchange: 'onTabChange'
+ ,add: 'updatePositions'
+ ,remove: 'updatePositions'
+ ,childmove: 'updatePositions'
}
});
diff --git a/app/view/main/MainController.js b/app/view/main/MainController.js
index 690af35b..095955c5 100644
--- a/app/view/main/MainController.js
+++ b/app/view/main/MainController.js
@@ -1,9 +1,6 @@
Ext.define('Rambox.view.main.MainController', {
extend: 'Ext.app.ViewController'
- ,requires: [
- ]
-
,alias: 'controller.main'
// Make focus on webview every time the user change tabs, to enable the autofocus in websites
@@ -14,6 +11,26 @@ Ext.define('Rambox.view.main.MainController', {
if ( webview ) webview.focus();
}
+ ,updatePositions: function(tabPanel, tab) {
+ if ( tab.id === 'ramboxTab' || tab.id === 'tbfill' ) return true;
+
+ console.log('Updating Tabs positions...');
+
+ var store = Ext.getStore('Services');
+ store.suspendEvent('remove');
+ Ext.each(tabPanel.items.items, function(t, i) {
+ if ( t.id !== 'ramboxTab' && t.id !== 'tbfill' ) {
+ if ( t.record.get('align') === 'right' ) i--;
+ var rec = store.getById(t.record.get('id'));
+ rec.set('position', i);
+ rec.save();
+ }
+ });
+
+ store.load();
+ store.resumeEvent('remove');
+ }
+
,showSimpleModal: function(record, edit) {
var me = this;
@@ -76,10 +93,10 @@ Ext.define('Rambox.view.main.MainController', {
}
,{
xtype: 'container'
- ,hidden: record.get('note') === ''
- ,data: { note: record.get('note') }
+ ,hidden: (edit ? Ext.getStore('ServicesList').getById(record.get('type')).get('note') === '' : record.get('note') === '')
+ ,data: { note: (edit ? Ext.getStore('ServicesList').getById(record.get('type')).get('note') : record.get('note')) }
,margin: '10 0 0 0'
- ,style: 'background-color:#93CFE0;color:#053767;'
+ ,style: 'background-color:#93CFE0;color:#053767;border-radius:6px;'
,tpl: [
''
,'{note}'
@@ -122,7 +139,6 @@ Ext.define('Rambox.view.main.MainController', {
,align: formValues.align
,notifications: formValues.notifications
,muted: formValues.muted
- ,js_unread: record.get('js_unread')
});
service.save();
Ext.getStore('Services').add(service);
@@ -131,7 +147,7 @@ Ext.define('Rambox.view.main.MainController', {
xtype: 'webview'
,id: 'tab_'+service.get('id')
,title: service.get('name')
- ,icon: 'resources/icons/'+record.get('logo')
+ ,icon: 'resources/icons/'+service.get('logo')
,src: service.get('url')
,type: service.get('type')
,align: formValues.align
@@ -201,6 +217,7 @@ Ext.define('Rambox.view.main.MainController', {
,submitEmptyText: false
,emptyText: record.get('url') === '___' ? 'http://' : ''
,vtype: record.get('url') === '___' ? 'url' : ''
+ ,width: 220
,listeners: {
specialkey: function(field, e) {
if(e.getKey() == e.ENTER && field.up('form').isValid()) {
@@ -247,10 +264,10 @@ Ext.define('Rambox.view.main.MainController', {
}
,{
xtype: 'container'
- ,hidden: record.get('note') === ''
- ,data: { note: record.get('note') }
+ ,hidden: (edit ? Ext.getStore('ServicesList').getById(record.get('type')).get('note') === '' : record.get('note') === '')
+ ,data: { note: (edit ? Ext.getStore('ServicesList').getById(record.get('type')).get('note') : record.get('note')) }
,margin: '10 0 0 0'
- ,style: 'background-color:#93CFE0;color:#053767;'
+ ,style: 'background-color:#93CFE0;color:#053767;border-radius:6px;'
,tpl: [
''
,'{note}'
@@ -293,7 +310,6 @@ Ext.define('Rambox.view.main.MainController', {
,align: formValues.align
,notifications: formValues.notifications
,muted: formValues.muted
- ,js_unread: record.get('js_unread')
});
service.save();
Ext.getStore('Services').add(service);
@@ -302,7 +318,7 @@ Ext.define('Rambox.view.main.MainController', {
xtype: 'webview'
,id: 'tab_'+service.get('id')
,title: service.get('name')
- ,icon: 'resources/icons/'+record.get('logo')
+ ,icon: 'resources/icons/'+service.get('logo')
,src: service.get('url')
,type: service.get('type')
,align: formValues.align
@@ -352,11 +368,11 @@ Ext.define('Rambox.view.main.MainController', {
tab.down('component').el.dom.getWebContents().session.clearCache(Ext.emptyFn);
tab.down('component').el.dom.getWebContents().session.clearStorageData({}, Ext.emptyFn);
- // Close tab
- tab.close();
-
// Remove record from localStorage
Ext.getStore('Services').remove(Ext.getStore('Services').getById(serviceId));
+
+ // Close tab
+ tab.close();
}
,removeService: function( gridView, rowIndex, colIndex, col, e, rec, rowEl ) {
@@ -376,17 +392,23 @@ Ext.define('Rambox.view.main.MainController', {
if ( btn ) {
Ext.Msg.confirm('Please confirm...', 'Are you sure you want to remove all services?', function(btnId) {
if ( btnId === 'yes' ) {
+ Ext.cq1('app-main').suspendEvent('remove');
Ext.Array.each(Ext.getStore('Services').collect('id'), function(serviceId) {
me.removeServiceFn(serviceId);
});
+ Ext.getStore('Services').load();
if ( Ext.isFunction(callback) ) callback();
+ Ext.cq1('app-main').resumeEvent('remove');
}
});
} else {
+ Ext.cq1('app-main').suspendEvent('remove');
Ext.Array.each(Ext.getStore('Services').collect('id'), function(serviceId) {
me.removeServiceFn(serviceId);
});
+ Ext.getStore('Services').load();
if ( Ext.isFunction(callback) ) callback();
+ Ext.cq1('app-main').resumeEvent('remove');
}
}
@@ -564,7 +586,7 @@ Ext.define('Rambox.view.main.MainController', {
xtype: 'webview'
,id: 'tab_'+service.get('id')
,title: service.get('name')
- ,icon: formValues.logo
+ ,icon: 'resources/icons/'+service.get('logo')
,src: service.get('url')
,type: service.get('type')
,align: formValues.align
@@ -745,7 +767,8 @@ Ext.define('Rambox.view.main.MainController', {
icon: 'resources/Icon.png'
}, function(err, profile, id_token) {
// There was an error logging the user in
- if (err) return console.error(err);
+ //if (err) return console.error(err);
+ console.log('LOGIN', err, profile, id_token);
// Display a spinner while waiting
Ext.Msg.wait('Please wait until we get your configuration.', 'Connecting...');
@@ -763,62 +786,80 @@ Ext.define('Rambox.view.main.MainController', {
if ( !err ) {
// Exchange the delegate token for a Firebase auth token
firebase.auth().signInWithCustomToken(result.id_token).then(function(snapshot) {
- fireRef.database().ref('users/' + profile.user_id).once('value').then(function(snapshot) {
- me.removeAllServices(false, function() {
- if ( snapshot.val() === null || Ext.isEmpty(snapshot.val().services) ) return;
-
- Ext.each(snapshot.val().services, function(s) {
- var service = Ext.create('Rambox.model.Service', {
- id: s.id
- ,position: s.position
- ,type: s.type
- ,logo: s.logo
- ,name: s.name
- ,url: s.url
- ,align: s.align
- ,notifications: s.notifications
- ,muted: s.muted
- ,js_unread: s.js_unread
- });
+ fireRef.database().ref('test/' + profile.user_id).child('services').orderByChild('position').once('value', function(snapshot2) {
+ Ext.Msg.hide();
+
+ // Import Services function
+ var importServices = function(snap) {
+ snap.forEach(function(data) {
+ var s = data.val();
+ s.firebase_key = data.key;
+ var service = Ext.create('Rambox.model.Service', s);
service.save();
Ext.getStore('Services').add(service);
+ });
+ Ext.getStore('Services').resumeEvent('load');
+ Ext.getStore('Services').load();
+
+ // User is logged in
+ // Save the profile and JWT.
+ localStorage.setItem('profile', JSON.stringify(profile));
+ localStorage.setItem('id_token', id_token);
+ Rambox.ux.Firebase.createEvents();
+ }
- var tabData = {
- xtype: 'webview'
- ,id: 'tab_'+service.get('id')
- ,title: service.get('name')
- ,icon: 'resources/icons/' + s.logo
- ,src: service.get('url')
- ,type: service.get('type')
- ,align: s.align
- ,notifications: s.notifications
- ,muted: s.muted
- ,record: service
- ,tabConfig: {
- service: service
- }
- };
-
- if ( s.align === 'left' ) {
- var tbfill = Ext.cq1('app-main').getTabBar().down('tbfill');
- Ext.cq1('app-main').insert(Ext.cq1('app-main').getTabBar().items.indexOf(tbfill), tabData);
+ // Firebase empty and Have Services
+ if ( !snapshot2.hasChildren() && Ext.getStore('Services').getCount() > 0 ) {
+ Ext.Msg.confirm('Import', 'You don\'t have any service saved. Do you want to import your current services?', function(btnId) {
+ if ( btnId === 'yes' ) {
+ var services = [];
+ Ext.getStore('Services').each(function(service, index) {
+ service.set('firebase_key', index);
+ // Prevent saving local ID into Firebase
+ var data = Ext.clone(service.data);
+ delete data.id;
+
+ services.push(data);
+ });
+ fireRef.database().ref('test/' + profile.user_id).set({
+ services: services
+ });
+
+ // User is logged in
+ // Save the profile and JWT.
+ localStorage.setItem('profile', JSON.stringify(profile));
+ localStorage.setItem('id_token', id_token);
+ Rambox.ux.Firebase.createEvents();
} else {
- Ext.cq1('app-main').add(tabData);
+ Ext.Msg.confirm('Clear services', 'Do you want to remove all your current services to start over?
If NO, you will be logged out.', function(btnId) {
+ if ( btnId === 'yes' ) {
+ me.removeAllServices(false);
+ } else {
+ me.logout();
+ }
+ });
}
});
-
- Ext.Msg.hide();
- });
+ // Firebase not empty and Have Services
+ } else if ( snapshot2.hasChildren() && Ext.getStore('Services').getCount() > 0 ) {
+ Ext.Msg.confirm('Confirm', 'To import your configuration, I need to remove all your current services. Do you want to continue?
If NO, you will be logged out.', function(btnId) {
+ if ( btnId === 'yes' ) {
+ me.removeAllServices(false, function() {
+ importServices(snapshot2);
+ });
+ } else {
+ me.logout();
+ }
+ });
+ // Firebase not empty and Have no Services
+ } else if ( snapshot2.hasChildren() && Ext.getStore('Services').getCount() === 0 ) {
+ importServices(snapshot2);
+ }
});
});
}
});
- // User is logged in
- // Save the profile and JWT.
- localStorage.setItem('profile', JSON.stringify(profile));
- localStorage.setItem('id_token', id_token);
-
Ext.cq1('app-main').getViewModel().set('username', profile.name);
Ext.cq1('app-main').getViewModel().set('avatar', profile.picture);
}, function() {
@@ -829,22 +870,34 @@ Ext.define('Rambox.view.main.MainController', {
,logout: function(btn) {
var me = this;
- Ext.Msg.confirm('Logout', 'Are you sure you want to logout?', function(btnId) {
- if ( btnId === 'yes' ) {
+ var logoutFn = function(callback) {
+ Ext.Msg.wait('Closing you session...', 'Logout');
+
+ firebase.auth().signOut().then(function() {
localStorage.removeItem('profile');
localStorage.removeItem('id_token');
Ext.cq1('app-main').getViewModel().set('username', '');
Ext.cq1('app-main').getViewModel().set('avatar', '');
- firebase.auth().signOut().then(function() {
- Ext.Array.each(Ext.getStore('Services').collect('id'), function(serviceId) {
- me.removeServiceFn(serviceId);
+ if ( Ext.isFunction(callback) ) callback();
+
+ Ext.Msg.hide();
+ }, function(error) {
+ console.error(error);
+ });
+ }
+
+ if ( btn ) {
+ Ext.Msg.confirm('Logout', 'Are you sure you want to logout?', function(btnId) {
+ if ( btnId === 'yes' ) {
+ logoutFn(function() {
+ me.removeAllServices();
});
- }, function(error) {
- console.error(error);
- });
- }
- })
+ }
+ });
+ } else {
+ logoutFn();
+ }
}
});