Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
75
gaia/apps/communications/contacts/views/details/details.html
Executable file
75
gaia/apps/communications/contacts/views/details/details.html
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<meta name="theme-group" content="theme-communications">
|
||||
<meta name="theme-color" content="var(--header-background)">
|
||||
<title> Contacts </title>
|
||||
|
||||
<!-- Hack for avoiding 'glitch' when loading the view -->
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
background: #00b7b7 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Building Blocks -->
|
||||
<link href="/shared/style/input_areas.css" rel="stylesheet">
|
||||
<link href="/shared/style/lists.css" rel="stylesheet">
|
||||
<link href="/shared/style/scrolling.css" rel="stylesheet">
|
||||
<link href="/shared/style/buttons.css" rel="stylesheet">
|
||||
<link href="/shared/style/progress_activity.css" rel="stylesheet">
|
||||
<link href="/shared/style/status.css" rel="stylesheet">
|
||||
<link href="/shared/style/switches.css" rel="stylesheet">
|
||||
<link href="/shared/style/confirm.css" rel="stylesheet">
|
||||
<link href="/shared/style/action_menu.css" rel="stylesheet">
|
||||
<link href="/shared/style/edit_mode.css" rel="stylesheet">
|
||||
<link href="/shared/style/animations.css" rel="stylesheet">
|
||||
|
||||
<link href="/contacts/style/app.css" rel="stylesheet">
|
||||
<link href="/contacts/style/contacts.css" rel="stylesheet">
|
||||
<link href="/contacts/style/overlay.css" rel="stylesheet">
|
||||
<link href="/shared/pages/import/style/search.css" rel="stylesheet">
|
||||
<link href="/shared/pages/import/style/curtain_frame.css" rel="stylesheet">
|
||||
<link href="/contacts/style/status.css" rel="stylesheet">
|
||||
<link href="/contacts/style/fb_extensions.css" rel="stylesheet">
|
||||
<link href="/shared/style/contacts.css" rel="stylesheet">
|
||||
<link href="/shared/style/contacts/contacts_buttons.css" rel="stylesheet">
|
||||
<link href="/contacts/style/match_service.css" rel="stylesheet">
|
||||
<link href="/contacts/views/details/style/overwrite.css" rel="stylesheet">
|
||||
|
||||
<!-- <link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/gaia-icons.css" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="app://theme.gaiamobile.org/shared/elements/gaia-theme/gaia-theme.css" />
|
||||
|
||||
<!-- Web Components -->
|
||||
<script defer src="/shared/elements/config.js"></script>
|
||||
<script defer src="/shared/js/component_utils.js"></script>
|
||||
<script defer src="/shared/elements/gaia_subheader/script.js"></script>
|
||||
<script defer src="/shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
|
||||
<link rel="localization" href="/shared/elements/gaia_menu/locales/gaia_menu.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/telephony/telephony.{locale}.properties">
|
||||
|
||||
<script defer src="/shared/js/lazy_loader.js"></script>
|
||||
<script defer src="/contacts/js/utilities/performance_helper.js"></script>
|
||||
<script defer src="/contacts/js/cache.js"></script>
|
||||
<script defer src='/contacts/views/details/js/boot.js'></script>
|
||||
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="/contacts/locales/contacts.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/import_contacts/import_contacts.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/date/date.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/phone_types/phone_types.{locale}.properties">
|
||||
|
||||
<link rel="import" href="/contacts/elements/details.html">
|
||||
<link rel="import" href="/shared/elements/confirmation-message.html">
|
||||
</head>
|
||||
|
||||
<body role="application" class="app-contacts skin-comms theme-communications hidden">
|
||||
<section is="view-contact-details" id='view-contact-details' role="region" class="view view-contact-profile view-noscroll current"></section>
|
||||
<iframe src="/shared/pages/import/curtain.html" id="iframe_curtain"></iframe>
|
||||
<form is="confirmation-message" id="confirmation-message" class="hide" role="dialog" data-type="confirm"></form>
|
||||
</body>
|
||||
</html>
|
||||
63
gaia/apps/communications/contacts/views/details/js/boot.js
Executable file
63
gaia/apps/communications/contacts/views/details/js/boot.js
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
/* global DetailsUI, DetailsController, LazyLoader, ContactsService */
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* This class is the one in charge of loading the minimum set of
|
||||
* resources needed for the view to load. Any other JS/CSS/Element
|
||||
* not needed in the critical path *must* be lazy loaded when needed.
|
||||
*
|
||||
* Once localization and all the basic JS/CSS/Elements are loaded,
|
||||
* we will initialize UI and Controller. Both JS classes *must* be
|
||||
* independent and will communicate through events.
|
||||
*/
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
LazyLoader.load(['/shared/js/l10n.js']).then(function() {
|
||||
LazyLoader.load([
|
||||
document.getElementById('view-contact-details')
|
||||
]).then(function() {
|
||||
// TODO Add if needed
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
window.onload = function() {
|
||||
var dependencies = [
|
||||
'/contacts/services/contacts.js',
|
||||
'/shared/js/contact_photo_helper.js',
|
||||
'/shared/js/contacts/contacts_buttons.js',
|
||||
'/shared/js/text_normalizer.js',
|
||||
'/shared/js/contacts/utilities/dom.js',
|
||||
'/shared/js/contacts/utilities/templates.js',
|
||||
'/shared/js/contacts/import/utilities/misc.js',
|
||||
'/contacts/js/match_service.js',
|
||||
'/contacts/views/details/js/details_ui.js',
|
||||
'/contacts/views/details/js/details_controller.js',
|
||||
'/shared/pages/import/js/curtain.js'
|
||||
];
|
||||
|
||||
LazyLoader.load(dependencies).then(function() {
|
||||
DetailsUI.init();
|
||||
DetailsController.init();
|
||||
|
||||
window.addEventListener('renderdone', function fn() {
|
||||
window.removeEventListener('renderdone', fn);
|
||||
document.body.classList.remove('hidden');
|
||||
});
|
||||
|
||||
navigator.mozSetMessageHandler('activity', activity => {
|
||||
DetailsController.setActivity(activity);
|
||||
var id = activity.source.data.params.id;
|
||||
|
||||
// TODO: Implement handler for open Vcards
|
||||
|
||||
ContactsService.get(id, function onSuccess(savedContact) {
|
||||
ContactsService.getCount(count => {
|
||||
DetailsUI.render(savedContact, count, true);
|
||||
});
|
||||
}, function onError() {
|
||||
console.error('Error retrieving contact');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
165
gaia/apps/communications/contacts/views/details/js/details_controller.js
Executable file
165
gaia/apps/communications/contacts/views/details/js/details_controller.js
Executable file
|
|
@ -0,0 +1,165 @@
|
|||
'use strict';
|
||||
|
||||
/* global utils */
|
||||
/* global MatchService */
|
||||
/* global ContactsService */
|
||||
/* global LazyLoader */
|
||||
/* global ContactToVcardBlob */
|
||||
/* global VcardFilename */
|
||||
/* global MozActivity */
|
||||
|
||||
/* exported Details */
|
||||
|
||||
/*
|
||||
* Once the deatils view is loaded, we will listen events dispatched
|
||||
* from the UI. This events will come with the info needed in order
|
||||
* to execute actions related with the UI (back, toggle favorite, share...).
|
||||
*
|
||||
* Controller will *not* contain any code related with the DOM/UI,
|
||||
* and will rely on the info provided by the event.
|
||||
*/
|
||||
(function(exports) {
|
||||
|
||||
var _activity = null;
|
||||
|
||||
function setActivity(activity) {
|
||||
_activity = activity;
|
||||
}
|
||||
|
||||
function findDuplicates(evt) {
|
||||
if (!evt.detail || !evt.detail.contactId) {
|
||||
console.error('Missing parameters in CustomEvent');
|
||||
return;
|
||||
}
|
||||
|
||||
var contactId = evt.detail.contactId;
|
||||
MatchService.match(contactId);
|
||||
}
|
||||
|
||||
function toggleFavorite(evt){
|
||||
if (!evt.detail || typeof evt.detail.contact === 'undefined' ||
|
||||
typeof evt.detail.isFavorite === 'undefined') {
|
||||
console.error('Missing parameters in CustomEvent');
|
||||
return;
|
||||
}
|
||||
|
||||
var contact = evt.detail.contact;
|
||||
var isFavorite = evt.detail.isFavorite;
|
||||
var favorite = !isFavorite;
|
||||
if (favorite) {
|
||||
contact.category = contact.category || [];
|
||||
contact.category.push('favorite');
|
||||
} else {
|
||||
if (!contact.category) {
|
||||
return;
|
||||
}
|
||||
var pos = contact.category.indexOf('favorite');
|
||||
if (pos > -1) {
|
||||
contact.category.splice(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function onError(e) {
|
||||
console.error('Error saving favorite');
|
||||
// We must restore contact in order to update the UX accordingly
|
||||
if (favorite) {
|
||||
var pos = contact.category.indexOf('favorite');
|
||||
if (pos > -1) {
|
||||
contact.category.splice(pos, 1);
|
||||
}
|
||||
} else {
|
||||
contact.category = contact.category || [];
|
||||
contact.category.push('favorite');
|
||||
}
|
||||
|
||||
dispatchEvent('toggleFavoriteDone', {contact: contact});
|
||||
}
|
||||
|
||||
ContactsService.save(
|
||||
utils.misc.toMozContact(contact),
|
||||
function(e) {
|
||||
if (typeof e !== 'undefined') {
|
||||
onError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Listening to oncontactchange event, we can save the evt
|
||||
// and send it via sessionStorage to the main list in order to
|
||||
// update it accordingly with the changes made by the user in this view.
|
||||
|
||||
ContactsService.get(contact.id, function(savedContact) {
|
||||
dispatchEvent('toggleFavoriteDone', {contact: savedContact});
|
||||
}, onError);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function init() {
|
||||
window.addEventListener('backAction', handleBackAction);
|
||||
window.addEventListener('editAction', handleEditAction);
|
||||
window.addEventListener('toggleFavoriteAction', toggleFavorite);
|
||||
window.addEventListener('shareAction', shareContact);
|
||||
window.addEventListener('findDuplicatesAction', findDuplicates);
|
||||
|
||||
// TODO: Need to save the oncontactchange event in order to update
|
||||
// the main list when the user go back from this view. It could be done
|
||||
// by saving the event in sessionStorage
|
||||
}
|
||||
|
||||
function shareContact(evt) {
|
||||
const VCARD_DEPS = [
|
||||
'/shared/js/text_normalizer.js',
|
||||
'/shared/js/contact2vcard.js',
|
||||
'/shared/js/setImmediate.js'
|
||||
];
|
||||
|
||||
if (!evt.detail || typeof evt.detail.contact === 'undefined') {
|
||||
console.error('Missing parameters in CustomEvent');
|
||||
return;
|
||||
}
|
||||
|
||||
var contact = evt.detail.contact;
|
||||
|
||||
LazyLoader.load(VCARD_DEPS,function vcardLoaded() {
|
||||
ContactToVcardBlob([contact], function blobReady(vcardBlob) {
|
||||
VcardFilename(contact).then(filename => {
|
||||
/* jshint nonew: false */
|
||||
new MozActivity({
|
||||
name: 'share',
|
||||
data: {
|
||||
type: 'text/vcard',
|
||||
number: 1,
|
||||
blobs: [new window.File([vcardBlob], filename, {
|
||||
type: 'text/x-vcard'
|
||||
})],
|
||||
filenames: [filename]
|
||||
}
|
||||
});
|
||||
});
|
||||
// The MIME of the blob should be this for some MMS gateways
|
||||
}, { type: 'text/x-vcard'} );
|
||||
});
|
||||
}
|
||||
|
||||
function handleBackAction(evt) {
|
||||
if (_activity) {
|
||||
_activity.postResult({});
|
||||
} else {
|
||||
window.history.back();
|
||||
}
|
||||
}
|
||||
|
||||
function handleEditAction(evt) {
|
||||
// In the future the navigation will change the URL to navigate
|
||||
// to #update view: Bug 1169579
|
||||
}
|
||||
|
||||
function dispatchEvent(name, data) {
|
||||
window.dispatchEvent(new CustomEvent(name, {detail: data}));
|
||||
}
|
||||
|
||||
exports.DetailsController = {
|
||||
'init': init,
|
||||
'setActivity': setActivity
|
||||
};
|
||||
})(window);
|
||||
490
gaia/apps/communications/contacts/views/details/js/details_ui.js
Executable file
490
gaia/apps/communications/contacts/views/details/js/details_ui.js
Executable file
|
|
@ -0,0 +1,490 @@
|
|||
/* global ContactsButtons, LazyLoader, utils, WebrtcClient */
|
||||
/* global Normalizer, ContactPhotoHelper */
|
||||
|
||||
/* exported DetailsUI */
|
||||
|
||||
/*
|
||||
* UI *must* work alone, without any 'Controller' tied. This will
|
||||
* help us to have a well-separated model, where the UI will dispatch
|
||||
* events that a Controller could potentially handle.
|
||||
*
|
||||
* UI will be in charge of rendering the info provided by 'Boot', which
|
||||
* is the only part of the code which knows UI & Controller.
|
||||
*
|
||||
* Every action performed by the user in the UI will dispatch an event that
|
||||
* should be handle by a controller
|
||||
*/
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
const PHOTO_POS = 7;
|
||||
const INIT_MARGIN = 8;
|
||||
const MAX_POSITION = 150;
|
||||
// Scale ratio for different devices
|
||||
var SCALE_RATIO = window.devicePixelRatio || 1;
|
||||
|
||||
var DetailsUI = {
|
||||
init: function() {
|
||||
var self = this;
|
||||
function initContainers() {
|
||||
self.header = document.querySelector('#details-view-header');
|
||||
self.contactDetails = document.querySelector('#contact-detail');
|
||||
self.listContainer = document.querySelector('#details-list');
|
||||
self.detailsName = document.querySelector('#contact-name-title');
|
||||
self.detailsNameText =
|
||||
document.querySelector('#contact-name-title bdi');
|
||||
self.orgTitle = document.querySelector('#org-title');
|
||||
self.datesTemplate = document.querySelector('#dates-template-\\#i\\#');
|
||||
self.addressesTemplate =
|
||||
document.querySelector('#address-details-template-\\#i\\#');
|
||||
self.duplicateTemplate =
|
||||
document.querySelector('#duplicate-contacts-template');
|
||||
self.editContactButton = document.querySelector('#edit-contact-button');
|
||||
self.cover = document.querySelector('#cover-img');
|
||||
self.detailsInner = document.querySelector('#contact-detail-inner');
|
||||
self.favoriteMessage = document.querySelector('#toggle-favorite');
|
||||
self.notesTemplate =
|
||||
document.querySelector('#note-details-template-\\#i\\#');
|
||||
self.socialTemplate =
|
||||
document.querySelector('#social-template-\\#i\\#');
|
||||
}
|
||||
|
||||
function addListeners() {
|
||||
self.initPullEffect(self.cover);
|
||||
|
||||
self.favoriteMessage.addEventListener('click',
|
||||
self.toggleFavorite.bind(self));
|
||||
|
||||
self.header.addEventListener('action',
|
||||
self.handleBackAction.bind(self));
|
||||
self.editContactButton.addEventListener('click',
|
||||
self.handleEditAction.bind(self));
|
||||
|
||||
ContactsButtons.init(self.listContainer, self.contactDetails, null);
|
||||
|
||||
window.addEventListener('toggleFavoriteDone',
|
||||
self.toggleFavoriteHandler.bind(self));
|
||||
}
|
||||
|
||||
initContainers();
|
||||
addListeners();
|
||||
},
|
||||
|
||||
handleBackAction: function(evt) {
|
||||
this.dispatchEvent('backAction');
|
||||
},
|
||||
|
||||
handleEditAction: function(evt) {
|
||||
this.dispatchEvent('editAction');
|
||||
},
|
||||
|
||||
initPullEffect: function(cover) {
|
||||
var maxPosition = Math.round(MAX_POSITION * SCALE_RATIO);
|
||||
var startPosition = 0;
|
||||
var self = this;
|
||||
|
||||
function onTouchStart(e) {
|
||||
if (self.contactDetails.classList.contains('no-photo')) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
startPosition = e.changedTouches[0].clientY;
|
||||
|
||||
self.contactDetails.classList.add('up');
|
||||
cover.classList.add('up');
|
||||
|
||||
window.addEventListener('touchmove', onTouchMove, true);
|
||||
window.addEventListener('touchend', onTouchEnd, true);
|
||||
}
|
||||
|
||||
function onTouchEnd(e) {
|
||||
e.preventDefault();
|
||||
|
||||
self.contactDetails.style.transform = null;
|
||||
self.contactDetails.classList.add('up');
|
||||
|
||||
cover.style.transform = null;
|
||||
cover.classList.add('up');
|
||||
|
||||
window.removeEventListener('touchmove', onTouchMove, true);
|
||||
window.removeEventListener('touchend', onTouchEnd, true);
|
||||
}
|
||||
|
||||
function onTouchMove(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var deltaY = e.changedTouches[0].clientY - startPosition;
|
||||
deltaY = Math.min(maxPosition, Math.max(0, deltaY));
|
||||
|
||||
var calc = 'calc(' + INIT_MARGIN + 'rem + ' + deltaY + 'px)';
|
||||
self.contactDetails.style.transform = 'translateY(' + calc + ')';
|
||||
self.contactDetails.classList.remove('up');
|
||||
|
||||
// Divide by 40 (4 times slower and in rems)
|
||||
var coverPosition = (-PHOTO_POS + (deltaY / 40)) + 'rem';
|
||||
cover.style.transform = 'translateY(' + coverPosition + ')';
|
||||
cover.classList.remove('up');
|
||||
}
|
||||
|
||||
cover.addEventListener('touchstart', onTouchStart, true);
|
||||
},
|
||||
|
||||
render: function(currentContact, contactsCount, readOnly) {
|
||||
if(this.isAFavoriteChange){
|
||||
this.isAFavoriteChange = false;
|
||||
return Promise.resolve(this.isAFavoriteChange);
|
||||
}
|
||||
|
||||
this.dispatchEvent('renderinit');
|
||||
|
||||
this.contactData = currentContact;
|
||||
this.contactsCount = contactsCount;
|
||||
|
||||
// Initially enabled and only disabled if necessary
|
||||
this.editContactButton.removeAttribute('disabled');
|
||||
this.editContactButton.classList.remove('hide');
|
||||
this.header.setAttribute('action', 'back');
|
||||
|
||||
if (readOnly) {
|
||||
this.editContactButton.classList.add('hide');
|
||||
this.header.setAttribute('action', 'close');
|
||||
}
|
||||
|
||||
this.doReloadContactDetails(this.contactData);
|
||||
},
|
||||
|
||||
doReloadContactDetails: function(contact) {
|
||||
this.setupDisplayName(contact);
|
||||
this.contactDetails.classList.remove('no-photo');
|
||||
this.contactDetails.classList.remove('up');
|
||||
utils.dom.removeChildNodes(this.listContainer);
|
||||
|
||||
this.renderFavorite(contact);
|
||||
this.renderOrg(contact);
|
||||
|
||||
ContactsButtons.renderPhones(contact);
|
||||
ContactsButtons.renderEmails(contact);
|
||||
|
||||
this.renderWebrtcClient(contact);
|
||||
|
||||
this.renderAddresses(contact);
|
||||
|
||||
this.renderDates(contact);
|
||||
|
||||
this.renderNotes(contact);
|
||||
|
||||
this.renderShareButton(contact);
|
||||
|
||||
this.renderDuplicate(contact);
|
||||
|
||||
this.renderPhoto(contact);
|
||||
|
||||
this.dispatchEvent('renderdone');
|
||||
},
|
||||
|
||||
setupDisplayName: function(contact) {
|
||||
var name = this.getDisplayName(contact);
|
||||
if (typeof name === 'string') {
|
||||
this.detailsNameText.setAttribute('data-l10n-id', name);
|
||||
} else if (name.raw) {
|
||||
this.detailsNameText.textContent = name.raw;
|
||||
} else if (name.id) {
|
||||
navigator.mozL10n.setAttributes(this.detailsNameText,
|
||||
name.id, name.args);
|
||||
}
|
||||
},
|
||||
|
||||
getDisplayName: function(contact) {
|
||||
var l10nId = 'noName';
|
||||
|
||||
if (this.hasName(contact)) {
|
||||
l10nId = {'raw': contact.name[0]};
|
||||
} else if (this.hasContent(contact.tel)) {
|
||||
l10nId = {'raw': contact.tel[0].value};
|
||||
} else if (this.hasContent(contact.email)) {
|
||||
l10nId = {'raw': contact.email[0].value};
|
||||
}
|
||||
return l10nId;
|
||||
},
|
||||
|
||||
hasContent: function(field) {
|
||||
return (Array.isArray(field) &&
|
||||
field.length > 0 &&
|
||||
field[0].value &&
|
||||
field[0].value.trim());
|
||||
},
|
||||
|
||||
hasName: function(contact) {
|
||||
return (Array.isArray(contact.name) &&
|
||||
contact.name[0] &&
|
||||
contact.name[0].trim());
|
||||
},
|
||||
|
||||
renderFavorite: function(contact) {
|
||||
var favorite = this.isFavorite();
|
||||
this.toggleFavoriteMessage(favorite);
|
||||
|
||||
this.header.classList.toggle('favorite', !!favorite);
|
||||
},
|
||||
|
||||
toggleFavorite: function() {
|
||||
var contact = this.contactData;
|
||||
|
||||
var favorite = !this.isFavorite();
|
||||
this.toggleFavoriteMessage(favorite);
|
||||
|
||||
// Disabling button while saving the contact
|
||||
this.favoriteMessage.style.pointerEvents = 'none';
|
||||
this.dispatchEvent('toggleFavoriteAction',
|
||||
{contact: contact, isFavorite: !favorite});
|
||||
},
|
||||
|
||||
toggleFavoriteMessage: function(isFav) {
|
||||
var cList = this.favoriteMessage.classList;
|
||||
var l10nId = isFav ? 'removeFavorite' : 'addFavorite';
|
||||
this.favoriteMessage.setAttribute('data-l10n-id', l10nId);
|
||||
isFav ? cList.add('on') : cList.remove('on');
|
||||
},
|
||||
|
||||
toggleFavoriteHandler: function(evt) {
|
||||
var savedContact = evt.detail.contact;
|
||||
this.favoriteMessage.style.pointerEvents = 'auto';
|
||||
this.setContact(savedContact);
|
||||
this.renderFavorite(savedContact);
|
||||
this.isAFavoriteChange = true;
|
||||
},
|
||||
|
||||
setContact: function(currentContact) {
|
||||
this.contactData = currentContact;
|
||||
},
|
||||
|
||||
renderOrg: function(contact) {
|
||||
if (contact.org && contact.org.length > 0 && contact.org[0] !== '') {
|
||||
this.orgTitle.textContent = contact.org[0];
|
||||
this.orgTitle.classList.remove('hide');
|
||||
} else {
|
||||
this.orgTitle.classList.add('hide');
|
||||
this.orgTitle.textContent = '';
|
||||
}
|
||||
},
|
||||
|
||||
renderWebrtcClient: function(contact) {
|
||||
LazyLoader.load([
|
||||
'/contacts/style/webrtc-client/webrtc_client.css',
|
||||
'/contacts/js/webrtc-client/webrtc_client.js'
|
||||
]).then(function() {
|
||||
WebrtcClient.start(contact);
|
||||
});
|
||||
},
|
||||
|
||||
renderAddresses: function(contact) {
|
||||
if (!contact.adr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load what we need
|
||||
LazyLoader.load('/contacts/js/utilities/mozContact.js', () => {
|
||||
for (var i = 0; i < contact.adr.length; i++) {
|
||||
var currentAddress = contact.adr[i];
|
||||
// Sanity check
|
||||
if (utils.mozContact.haveEmptyFields(currentAddress,
|
||||
['streetAddress', 'postalCode', 'locality', 'countryName'])) {
|
||||
continue;
|
||||
}
|
||||
var address = currentAddress.streetAddress || '';
|
||||
var escapedStreet = Normalizer.escapeHTML(address, true);
|
||||
var locality = currentAddress.locality;
|
||||
var escapedLocality = Normalizer.escapeHTML(locality, true);
|
||||
var country = currentAddress.countryName || '';
|
||||
var escapedCountry = Normalizer.escapeHTML(country, true);
|
||||
var postalCode = currentAddress.postalCode || '';
|
||||
var escapedPostalCode = Normalizer.escapeHTML(postalCode, true);
|
||||
|
||||
var addressField = {
|
||||
streetAddress: escapedStreet,
|
||||
postalCode: escapedPostalCode,
|
||||
locality: escapedLocality || '',
|
||||
countryName: escapedCountry,
|
||||
'type_l10n_id': currentAddress.type,
|
||||
i: i
|
||||
};
|
||||
var template = utils.templates.render(this.addressesTemplate,
|
||||
addressField);
|
||||
this.listContainer.appendChild(template);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderDates: function(contact) {
|
||||
if (!contact.bday && !contact.anniversary) {
|
||||
return;
|
||||
}
|
||||
|
||||
var dateString = '';
|
||||
|
||||
var fields = ['bday', 'anniversary'];
|
||||
var l10nIds = ['birthday', 'anniversary'];
|
||||
|
||||
var rendered = 0;
|
||||
for (var j = 0; j < fields.length; j++) {
|
||||
var value = contact[fields[j]];
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
dateString = utils.misc.formatDate(value);
|
||||
} catch (err) {
|
||||
console.error('Error parsing date');
|
||||
continue;
|
||||
}
|
||||
var element = utils.templates.render(this.datesTemplate, {
|
||||
i: ++rendered,
|
||||
date: dateString,
|
||||
'type_l10n_id': l10nIds[j]
|
||||
});
|
||||
|
||||
this.listContainer.appendChild(element);
|
||||
}
|
||||
},
|
||||
|
||||
renderNotes: function(contact) {
|
||||
if (!contact.note || contact.note.length === 0) {
|
||||
return;
|
||||
}
|
||||
var container = document.createElement('li');
|
||||
var title = document.createElement('h2');
|
||||
title.setAttribute('data-l10n-id', 'comments');
|
||||
container.appendChild(title);
|
||||
for (var i = 0; i < contact.note.length; i++) {
|
||||
var currentNote = contact.note[i];
|
||||
var noteField = {
|
||||
note: Normalizer.escapeHTML(currentNote, true) || '',
|
||||
i: i
|
||||
};
|
||||
var template = utils.templates.render(this.notesTemplate, noteField);
|
||||
container.appendChild(template);
|
||||
this.listContainer.appendChild(container);
|
||||
}
|
||||
},
|
||||
|
||||
renderShareButton: function(contact) {
|
||||
var social = utils.templates.render(this.socialTemplate, {
|
||||
i: contact.id
|
||||
});
|
||||
var shareButton = social.querySelector('#share_button');
|
||||
|
||||
shareButton.addEventListener('click', () => {
|
||||
this.dispatchEvent('shareAction', {contact: this.contactData});
|
||||
});
|
||||
|
||||
shareButton.classList.remove('hide');
|
||||
|
||||
this.listContainer.appendChild(social);
|
||||
},
|
||||
|
||||
renderDuplicate: function(contact) {
|
||||
var dupItem = utils.templates.render(this.duplicateTemplate, {});
|
||||
var findMergeButton = dupItem.querySelector('#find-merge-button');
|
||||
findMergeButton.disabled = true;
|
||||
|
||||
if (this.contactsCount > 1) {
|
||||
// Only have this active if contact list has more than one entry
|
||||
findMergeButton.disabled = false;
|
||||
findMergeButton.addEventListener('click', () => {
|
||||
this.dispatchEvent('findDuplicatesAction',
|
||||
{contactId: this.contactData.id});
|
||||
});
|
||||
}
|
||||
|
||||
this.listContainer.appendChild(dupItem);
|
||||
},
|
||||
|
||||
renderPhoto: function(contact) {
|
||||
this.contactDetails.classList.remove('up');
|
||||
|
||||
var photo = ContactPhotoHelper.getFullResolution(contact);
|
||||
if (photo) {
|
||||
var currentHash = this.cover.dataset.imgHash;
|
||||
if (!currentHash) {
|
||||
utils.dom.updatePhoto(photo, this.cover);
|
||||
this.updateHash(photo, this.cover);
|
||||
}
|
||||
else {
|
||||
// Need to recalculate the hash and see whether the images changed
|
||||
this.calculateHash(photo, newHash => {
|
||||
if (currentHash !== newHash) {
|
||||
utils.dom.updatePhoto(photo, this.cover);
|
||||
this.cover.dataset.imgHash = newHash;
|
||||
}
|
||||
else {
|
||||
// Only for testing purposes
|
||||
this.cover.dataset.photoReady = 'true';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.contactDetails.classList.add('up');
|
||||
this.cover.classList.add('translated');
|
||||
this.contactDetails.classList.add('translated');
|
||||
var clientHeight = this.contactDetails.clientHeight -
|
||||
(INIT_MARGIN * 10 * SCALE_RATIO);
|
||||
if (this.detailsInner.offsetHeight < clientHeight) {
|
||||
this.cover.style.overflow = 'hidden';
|
||||
} else {
|
||||
this.cover.style.overflow = 'auto';
|
||||
}
|
||||
} else {
|
||||
this.resetPhoto();
|
||||
}
|
||||
},
|
||||
|
||||
updateHash: function(photo, cover) {
|
||||
this.calculateHash(photo, function(hash) {
|
||||
cover.dataset.imgHash = hash;
|
||||
});
|
||||
},
|
||||
|
||||
calculateHash: function(photo, cb) {
|
||||
var START_BYTES = 127;
|
||||
var BYTES_HASH = 16;
|
||||
|
||||
var out = [photo.type, photo.size];
|
||||
|
||||
// We skip the first bytes that typically are headers
|
||||
var chunk = photo.slice(START_BYTES, START_BYTES + BYTES_HASH);
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function() {
|
||||
out.push(reader.result);
|
||||
cb(out.join(''));
|
||||
};
|
||||
reader.onerror = function() {
|
||||
window.console.error('Error while calculating the hash: ',
|
||||
reader.error.name);
|
||||
cb(out.join(''));
|
||||
};
|
||||
reader.readAsDataURL(chunk);
|
||||
},
|
||||
|
||||
resetPhoto: function() {
|
||||
this.cover.classList.remove('translated');
|
||||
this.contactDetails.classList.remove('translated');
|
||||
this.cover.style.backgroundImage = '';
|
||||
this.cover.style.overflow = 'auto';
|
||||
this.contactDetails.style.transform = '';
|
||||
this.contactDetails.classList.add('no-photo');
|
||||
this.cover.dataset.imgHash = '';
|
||||
},
|
||||
|
||||
isFavorite: function() {
|
||||
return this.contactData != null && this.contactData.category != null &&
|
||||
this.contactData.category.indexOf('favorite') != -1;
|
||||
},
|
||||
|
||||
dispatchEvent: function(name, data) {
|
||||
window.dispatchEvent(new CustomEvent(name, {detail: data}));
|
||||
}
|
||||
};
|
||||
|
||||
exports.DetailsUI = DetailsUI;
|
||||
|
||||
})(window);
|
||||
7
gaia/apps/communications/contacts/views/details/style/overwrite.css
Executable file
7
gaia/apps/communications/contacts/views/details/style/overwrite.css
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
body * {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
body.hidden * {
|
||||
opacity: 0;
|
||||
}
|
||||
Loading…
Reference in a new issue