Browse Source

FFOS contacts import

Closes #43
master
Igor Zhukov 10 years ago
parent
commit
cb16d9c52f
  1. 47
      app/css/app.css
  2. 4
      app/index.html
  3. 139
      app/js/controllers.js
  4. 2
      app/js/init.js
  5. 3
      app/js/lib/config.js
  6. 113
      app/js/services.js
  7. 7
      app/manifest.webapp
  8. 1
      app/partials/confirm_modal.html
  9. 3
      app/partials/error_modal.html
  10. 2
      app/partials/im.html
  11. 1
      app/partials/import_contact_modal.html
  12. 2
      app/partials/login.html
  13. 61
      app/partials/phonebook_modal.html
  14. 6
      app/vendor/jquery.nanoscroller/nanoscroller.css

47
app/css/app.css

@ -169,6 +169,9 @@ input[type="number"] { @@ -169,6 +169,9 @@ input[type="number"] {
.btn-link:hover {
background: #f2f6fa;
}
.btn-link.dropdown-toggle:hover {
background: none;
}
.tg_page_head .navbar {
min-height: 44px;
@ -879,6 +882,9 @@ a.tg_radio_on:hover i.icon-radio { @@ -879,6 +882,9 @@ a.tg_radio_on:hover i.icon-radio {
color: #808080;
margin-bottom: 18px;
}
.im_dialogs_import_phonebook {
margin-top: 10px;
}
.im_dialogs_col {
margin-right: -7px;
@ -2742,7 +2748,6 @@ a:hover .icon-twitter { @@ -2742,7 +2748,6 @@ a:hover .icon-twitter {
border: 1px solid #F2F2F2;
border-radius: 3px;
padding: 6px 20px 6px 30px;
margin-bottom: 0;
margin: 0;
}
.is_1x .contacts_modal_search_field {
@ -3320,9 +3325,47 @@ ce671b orange @@ -3320,9 +3325,47 @@ ce671b orange
}
.countries_modal_search {
padding: 0 20px 12px;
padding: 0 0 12px;
margin: 0 20px;
position: relative;
}
.countries_modal_search_field {
font-size: 12px;
line-height: normal;
background: #F2F2F2 url(../img/icons/IconsetW.png) -6px -205px no-repeat;
background-size: 42px 710px;
border: 1px solid #F2F2F2;
border-radius: 3px;
padding: 6px 20px 6px 30px;
margin-bottom: 0;
margin: 0;
}
.is_1x .countries_modal_search_field {
background-image: url(../img/icons/IconsetW_1x.png);
}
.countries_modal_search_field:focus,
.countries_modal_search_field:active {
background-color: #FFF;
}
.countries_modal_search_clear {
position: absolute;
right: 9px;
margin-top: -23px;
color: #999;
width: 13px;
height: 13px;
vertical-align: text-top;
background: url(../img/icons/IconsetW.png) -15px -192px no-repeat;
background-size: 42px 710px;
opacity: 0.6;
}
.is_1x .countries_modal_search_clear {
background-image: url(../img/icons/IconsetW_1x.png);
}
.countries_modal_search_clear:hover {
opacity: 1;
}
.countries_modal_wrap .modal-body {
padding: 14px 0;

4
app/index.html

@ -27,8 +27,9 @@ @@ -27,8 +27,9 @@
<!-- build:js js/app.js -->
<script type="text/javascript" src="vendor/console-polyfill/console-polyfill.js"></script>
<script type="text/javascript" src="js/init.js"></script>
<script type="text/javascript" src="vendor/jquery/jquery.min.js"></script>
<script type="text/javascript" src="js/lib/config.js"></script>
<script type="text/javascript" src="js/init.js"></script>
<script type="text/javascript" src="vendor/jquery.nanoscroller/nanoscroller.js"></script>
<script type="text/javascript" src="vendor/jquery.emojiarea/jquery.emojiarea.js"></script>
@ -45,7 +46,6 @@ @@ -45,7 +46,6 @@
<script type="text/javascript" src="vendor/closure/long.js"></script>
<script type="text/javascript" src="js/lib/config.js"></script>
<script type="text/javascript" src="js/lib/mtproto.js"></script>
<script type="text/javascript" src="js/util.js"></script>

139
app/js/controllers.js

@ -285,7 +285,7 @@ angular.module('myApp.controllers', []) @@ -285,7 +285,7 @@ angular.module('myApp.controllers', [])
ChangelogNotifyService.checkUpdate();
})
.controller('AppImDialogsController', function ($scope, $location, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ErrorService) {
.controller('AppImDialogsController', function ($scope, $location, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, PhonebookContactsService, ErrorService) {
// console.log('init controller');
@ -293,6 +293,7 @@ angular.module('myApp.controllers', []) @@ -293,6 +293,7 @@ angular.module('myApp.controllers', [])
$scope.contacts = [];
$scope.search = {};
$scope.contactsLoaded = false;
$scope.phonebookAvailable = PhonebookContactsService.isAvailable();
var offset = 0,
maxID = 0,
@ -347,13 +348,21 @@ angular.module('myApp.controllers', []) @@ -347,13 +348,21 @@ angular.module('myApp.controllers', [])
$scope.$watch('search.query', loadDialogs);
$scope.importContact = function () {
AppUsersManager.openImportContact().then(function () {
if (contactsShown) {
AppUsersManager.openImportContact().then(function (foundContact) {
if (contactsShown && foundContact) {
loadDialogs();
}
});
};
$scope.importPhonebook = function () {
PhonebookContactsService.openPhonebookImport().result.then(function (foundContacts) {
if (contactsShown && foundContacts.length) {
loadDialogs();
}
})
};
function loadDialogs () {
offset = 0;
maxID = 0;
@ -1920,11 +1929,13 @@ angular.module('myApp.controllers', []) @@ -1920,11 +1929,13 @@ angular.module('myApp.controllers', [])
};
})
.controller('ImportContactModalController', function ($scope, $modalInstance, $rootScope, AppUsersManager) {
.controller('ImportContactModalController', function ($scope, $modalInstance, $rootScope, AppUsersManager, ErrorService, PhonebookContactsService) {
if ($scope.importContact === undefined) {
$scope.importContact = {};
}
$scope.phonebookAvailable = PhonebookContactsService.isAvailable();
$scope.doImport = function () {
if ($scope.importContact && $scope.importContact.phone) {
$scope.progress = {enabled: true};
@ -1933,6 +1944,11 @@ angular.module('myApp.controllers', []) @@ -1933,6 +1944,11 @@ angular.module('myApp.controllers', [])
$scope.importContact.first_name || '',
$scope.importContact.last_name || ''
).then(function (foundUserID) {
if (!foundUserID) {
ErrorService.show({
error: {code: 404, type: 'USER_NOT_USING_TELEGRAM'}
});
}
$modalInstance.close(foundUserID);
})['finally'](function () {
delete $scope.progress.enabled;
@ -1940,6 +1956,16 @@ angular.module('myApp.controllers', []) @@ -1940,6 +1956,16 @@ angular.module('myApp.controllers', [])
}
};
$scope.importPhonebook = function () {
PhonebookContactsService.openPhonebookImport().result.then(function (foundContacts) {
if (foundContacts) {
$modalInstance.close(foundContacts[0]);
} else {
$modalInstance.dismiss();
}
})
};
})
.controller('CountrySelectModalController', function ($scope, $modalInstance, $rootScope, SearchIndexManager) {
@ -1976,3 +2002,108 @@ angular.module('myApp.controllers', []) @@ -1976,3 +2002,108 @@ angular.module('myApp.controllers', [])
});
})
.controller('PhonebookModalController', function ($scope, $modalInstance, $rootScope, AppUsersManager, PhonebookContactsService, SearchIndexManager, ErrorService) {
$scope.search = {};
$scope.phonebook = [];
$scope.selectedContacts = {};
$scope.selectedCount = 0;
$scope.slice = {limit: 20, limitDelta: 20};
$scope.progress = {enabled: false};
$scope.multiSelect = true;
var searchIndex = SearchIndexManager.createIndex(),
phonebookReady = false;
PhonebookContactsService.getPhonebookContacts().then(function (phonebook) {
for (var i = 0; i < phonebook.length; i++) {
SearchIndexManager.indexObject(i, phonebook[i].first_name + ' ' + phonebook[i].last_name + ' ' + phonebook[i].phones.join(' '), searchIndex);
}
$scope.phonebook = phonebook;
$scope.toggleSelection(true);
phonebookReady = true;
updateList();
});
function updateList () {
var filtered = false,
results = {};
if (angular.isString($scope.search.query) && $scope.search.query.length) {
filtered = true;
results = SearchIndexManager.search($scope.search.query, searchIndex);
$scope.contacts = [];
for (var i = 0; i < $scope.phonebook.length; i++) {
if (!filtered || results[i]) {
$scope.contacts.push($scope.phonebook[i]);
}
}
} else {
$scope.contacts = $scope.phonebook;
}
$scope.slice.limit = 20;
}
$scope.$watch('search.query', function (newValue) {
if (phonebookReady) {
updateList();
}
});
$scope.contactSelect = function (i) {
if (!$scope.multiSelect) {
return $modalInstance.close($scope.phonebook[i]);
}
if ($scope.selectedContacts[i]) {
delete $scope.selectedContacts[i];
$scope.selectedCount--;
} else {
$scope.selectedContacts[i] = true;
$scope.selectedCount++;
}
};
$scope.toggleSelection = function (fill) {
if (!$scope.selectedCount || fill) {
$scope.selectedCount = $scope.phonebook.length;
for (var i = 0; i < $scope.phonebook.length; i++) {
$scope.selectedContacts[i] = true;
}
} else {
$scope.selectedCount = 0;
$scope.selectedContacts = {};
}
};
$scope.submitSelected = function () {
if ($scope.selectedCount <= 0) {
$modalInstance.dismiss();
}
var selectedContacts = [];
angular.forEach($scope.selectedContacts, function (t, i) {
selectedContacts.push($scope.phonebook[i]);
});
ErrorService.confirm({
type: 'CONTACTS_IMPORT_PERFORM'
}).then(function () {
$scope.progress.enabled = true;
AppUsersManager.importContacts(selectedContacts).then(function (foundContacts) {
if (!foundContacts.length) {
ErrorService.show({
error: {code: 404, type: 'USERS_NOT_USING_TELEGRAM'}
});
}
$modalInstance.close(foundContacts);
})['finally'](function () {
$scope.progress.enabled = false;
});
});
};
})

2
app/js/init.js

@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
setTimeout(function () {callback(result)}, 10);
};
if (!window.applicationCache || !window.addEventListener) {
if (!window.applicationCache || Config.App.packaged || !window.addEventListener) {
return;
}

3
app/js/lib/config.js

@ -21,7 +21,8 @@ Config = window.Config || {}; @@ -21,7 +21,8 @@ Config = window.Config || {};
Config.App = {
id: 2496,
hash: '8da85b0d5bfe62527e5b244c209159c3',
version: '0.1.5'
version: '0.1.5',
packaged: false
};
Config.Modes = {

113
app/js/services.js

@ -130,7 +130,7 @@ angular.module('myApp.services', []) @@ -130,7 +130,7 @@ angular.module('myApp.services', [])
};
})
.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, MtpApiFileManager, MtpApiManager, RichTextProcessor, SearchIndexManager) {
.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, MtpApiFileManager, MtpApiManager, RichTextProcessor, SearchIndexManager, ErrorService) {
var users = {},
cachedPhotoLocations = {},
contactsFillPromise,
@ -307,7 +307,39 @@ angular.module('myApp.services', []) @@ -307,7 +307,39 @@ angular.module('myApp.services', [])
onContactUpdated(foundUserID = importedContact.user_id, true);
});
return foundUserID;
return foundUserID ? 1 : 0;
});
};
function importContacts (contacts) {
var inputContacts = [],
i, j;
for (i = 0; i < contacts.length; i++) {
for (j = 0; j < contacts[i].phones.length; j++) {
inputContacts.push({
_: 'inputPhoneContact',
client_id: (i << 16 | j).toString(10),
phone: contacts[i].phones[j],
first_name: contacts[i].first_name,
last_name: contacts[i].last_name
});
}
}
return MtpApiManager.invokeApi('contacts.importContacts', {
contacts: inputContacts,
replace: false
}).then(function (importedContactsResult) {
saveApiUsers(importedContactsResult.users);
var result = [];
angular.forEach(importedContactsResult.imported, function (importedContact) {
onContactUpdated(importedContact.user_id, true);
result.push(importedContact.user_id);
});
return result;
});
};
@ -348,9 +380,6 @@ angular.module('myApp.services', []) @@ -348,9 +380,6 @@ angular.module('myApp.services', [])
windowClass: 'import_contact_modal_window'
}).result.then(function (foundUserID) {
if (!foundUserID) {
ErrorService.show({
error: {code: 404, type: 'USER_NOT_USING_TELEGRAM'}
});
return $q.reject();
}
return foundUserID;
@ -402,6 +431,7 @@ angular.module('myApp.services', []) @@ -402,6 +431,7 @@ angular.module('myApp.services', [])
getUserSearchText: getUserSearchText,
hasUser: hasUser,
importContact: importContact,
importContacts: importContacts,
deleteContacts: deleteContacts,
wrapForFull: wrapForFull,
openUser: openUser,
@ -409,6 +439,79 @@ angular.module('myApp.services', []) @@ -409,6 +439,79 @@ angular.module('myApp.services', [])
}
})
.service('PhonebookContactsService', function ($q, $modal, $sce) {
var phonebookContactsPromise;
return {
isAvailable: isAvailable,
openPhonebookImport: openPhonebookImport,
getPhonebookContacts: getPhonebookContacts
}
function isAvailable () {
return window.navigator && window.navigator.mozContacts && window.navigator.mozContacts.getAll;
}
function openPhonebookImport () {
return $modal.open({
templateUrl: 'partials/phonebook_modal.html',
controller: 'PhonebookModalController',
windowClass: 'phonebook_modal_window'
});
}
function getPhonebookContacts () {
if (phonebookContactsPromise) {
return phonebookContactsPromise;
}
var deferred = $q.defer(),
contacts = [],
request = window.navigator.mozContacts.getAll({}),
count = 0;
request.onsuccess = function () {
if (this.result) {
var contact = {
id: count,
first_name: (this.result.givenName || []).join(' '),
last_name: (this.result.familyName || []).join(' '),
phones: []
};
for (var i = 0; i < this.result.tel.length; i++) {
contact.phones.push(this.result.tel[i].value);
}
if (this.result.photo) {
contact.photo = URL.createObjectURL(this.result.photo[0]);
} else {
contact.photo = 'img/placeholders/UserAvatar' + ((Math.abs(count) % 8) + 1) + '@2x.png';
}
contact.photo = $sce.trustAsResourceUrl(contact.photo);
count++;
contacts.push(contact);
}
if (!this.result || count >= 1000) {
deferred.resolve(contacts);
return;
}
this.continue();
}
request.onerror = function (e) {
console.log('phonebook error', e, e.type, e.message);
deferred.reject(e);
}
return phonebookContactsPromise = deferred.promise;
}
})
.service('AppChatsManager', function ($rootScope, $modal, MtpApiFileManager, MtpApiManager, AppUsersManager, RichTextProcessor) {
var chats = {},
cachedPhotoLocations = {};

7
app/manifest.webapp

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
"name": "Webogram",
"description": "Webogram – UNOFFICIAL Telegram Web App.\nMore info & source code here: https://github.com/zhukov/webogram",
"version": "0.1.5",
"type": "privileged",
"launch_path": "/index.html",
"developer": {
"name": "Igor Zhukov",
@ -12,7 +13,11 @@ @@ -12,7 +13,11 @@
],
"permissions": {
"desktop-notification": {
"description": "To show new message notifications etc"
"description": "Required to show new message notifications"
},
"contacts": {
"description": "Required to import phonebook contacts",
"access": "readonly"
}
},
"icons": {

1
app/partials/confirm_modal.html

@ -21,6 +21,7 @@ @@ -21,6 +21,7 @@
</span>
<span ng-switch-when="FILE_CLIPBOARD_PASTE">Are you sure to send file(s) from clipboard?</span>
<span ng-switch-when="MESSAGE_DELETE">Are you sure you want to delete the message?</span>
<span ng-switch-when="CONTACTS_IMPORT_PERFORM">We will now send selected contacts to Telegram servers in order to find your friends in Telegram.</span>
<div ng-switch-when="LOGIN_PHONE_CORRECT">
Is this phone number correct?
<div class="confirm_phone_number"> <span ng-bind="country_code"></span> <span ng-bind="phone_number"></span> </div>

3
app/partials/error_modal.html

@ -50,6 +50,9 @@ @@ -50,6 +50,9 @@
<span ng-switch-when="USER_NOT_USING_TELEGRAM">
Sorry, there is no <strong>Telegram</strong> account with the phone number you provided.
</span>
<span ng-switch-when="USERS_NOT_USING_TELEGRAM">
Sorry, there are no <strong>Telegram</strong> accounts with the phone numbers you provided.
</span>
<div ng-switch-default ng-switch="error.code">

2
app/partials/im.html

@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
</a>
<ul class="dropdown-menu">
<li><a ng-click="openGroup()">New Group</a></li>
<li><a ng-click="importContact()">New Contact</a></li>
<li><a ng-click="openContacts()">Contacts</a></li>
<li><a ng-click="openSettings()">Settings</a></li>
</ul>
@ -31,6 +32,7 @@ @@ -31,6 +32,7 @@
<h3 class="im_dialogs_empty_header">No contacts yet</h3>
<p class="im_dialogs_empty_lead">Get started by adding a contact to chat with</p>
<button type="button" class="btn btn-primary btn-sm" ng-click="importContact()">Add contact</button>
<button ng-if="phonebookAvailable" type="button" class="btn btn-primary btn-sm im_dialogs_import_phonebook" ng-click="importPhonebook()">Import phonebook</button>
</div>
<ul class="nav nav-pills nav-stacked">

1
app/partials/import_contact_modal.html

@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
</div>
<div class="modal-footer">
<a class="btn btn-link pull-left" ng-if="phonebookAvailable" ng-click="importPhonebook()">Phonebook</a>
<a class="btn btn-link" ng-click="$dismiss()">Cancel</a>
<button class="btn btn-primary" ng-class="{disabled: progress.enabled}" ng-click="doImport()" ng-bind="progress.enabled ? 'Importing...' : 'Save'" ng-disabled="progress.enabled"></button>
</div>

2
app/partials/login.html

@ -63,7 +63,7 @@ @@ -63,7 +63,7 @@
</div>
<button class="btn btn-primary btn-block" ng-class="{disabled: progress.enabled}" ng-disabled="progress.enabled" type="submit" ng-switch="progress.enabled">
<span ng-switch-when="true">Signing in<span my-loading-dots></span></span>
<span ng-switch-when="true">Signing up<span my-loading-dots></span></span>
<span ng-switch-default>Sign up</span>
</button>
</form>

61
app/partials/phonebook_modal.html

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
<div class="contacts_modal_wrap" my-modal-position>
<div class="modal-body">
<div class="contacts_modal_search">
<input class="form-control contacts_modal_search_field" my-focused type="search" placeholder="Search" ng-model="search.query"/>
<a class="contacts_modal_search_clear" ng-click="search.query = ''" ng-show="search.query.length"></a>
</div>
<div my-contacts-list class="contacts_modal_col">
<div class="contacts_wrap nano" my-infinite-scroller>
<div class="contacts_scrollable_wrap content">
<ul class="contacts_modal_members_list nav nav-pills nav-stacked">
<li class="contacts_modal_contact_wrap clearfix" ng-repeat="contact in contacts | limitTo: slice.limit track by contact.id" ng-class="{active: selectedContacts[contact.id]}">
<a class="contacts_modal_contact" ng-click="contactSelect(contact.id)">
<i ng-if="multiSelect" class="icon icon-contact-tick"></i>
<div class="contacts_modal_contact_photo pull-left">
<img
class="contacts_modal_contact_photo"
ng-src="{{contact.photo}}"
/>
</div>
<div class="contacts_modal_contact_name">
<span ng-bind="contact.first_name"></span>
<span ng-bind="contact.last_name"></span>
</div>
<div class="contacts_modal_contact_status" ng-repeat="phone in contact.phones">
<span ng-bind="phone | phoneNumber"></span>
</div>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="modal-footer" ng-if="multiSelect">
<a class="btn btn-link pull-left" ng-click="toggleSelection()" ng-switch="selectedCount > 0">
<span ng-switch-when="true">Deselect all</span>
<span ng-switch-default>Select all</span>
</a>
<button class="btn btn-primary pull-right" ng-class="{disabled: !selectedCount || progress.enabled}" ng-disabled="!selectedCount || progress.enabled" ng-click="submitSelected()" type="submit" ng-switch="progress.enabled">
<span ng-switch-when="true">Importing<span my-loading-dots></span></span>
<span ng-switch-default>Import contacts</span>
</button>
</div>
</div>

6
app/vendor/jquery.nanoscroller/nanoscroller.css vendored

@ -23,6 +23,12 @@ @@ -23,6 +23,12 @@
.has-scrollbar .content::-webkit-scrollbar {
visibility: visible;
}
.nano .content::-moz-scrollbar {
visibility: hidden;
}
.has-scrollbar .content::-moz-scrollbar {
visibility: visible;
}
.nano > .pane {
background : rgba(0,0,0,.1);
position : absolute;

Loading…
Cancel
Save