core-apps: Add com.palm.app.messaging from HP webOS SDK
Since it's not available anywhere anymore, adding it here for now for testing IM/Messaging in LuneOS Signed-off-by: Herman van Hazendonk <github.com@herrie.org>
No files matched your search
@@ -0,0 +1 @@
|
||||
rvm use ruby-1.9.2-p180@palm
|
||||
@@ -0,0 +1,5 @@
|
||||
source "http://rubygems.org"
|
||||
|
||||
gem "eris"
|
||||
gem "jasmine"
|
||||
gem "rake"
|
||||
@@ -0,0 +1,52 @@
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
childprocess (0.2.0)
|
||||
ffi (~> 1.0.6)
|
||||
curb (0.7.15)
|
||||
diff-lcs (1.1.2)
|
||||
eris (0.0.3)
|
||||
curb
|
||||
erubis
|
||||
json
|
||||
rack
|
||||
sinatra
|
||||
thor
|
||||
erubis (2.7.0)
|
||||
ffi (1.0.9)
|
||||
jasmine (1.0.2.1)
|
||||
json_pure (>= 1.4.3)
|
||||
rack (>= 1.1)
|
||||
rspec (>= 1.3.1)
|
||||
selenium-webdriver (>= 0.1.3)
|
||||
json (1.5.3)
|
||||
json_pure (1.5.3)
|
||||
rack (1.3.2)
|
||||
rake (0.9.2)
|
||||
rspec (2.6.0)
|
||||
rspec-core (~> 2.6.0)
|
||||
rspec-expectations (~> 2.6.0)
|
||||
rspec-mocks (~> 2.6.0)
|
||||
rspec-core (2.6.4)
|
||||
rspec-expectations (2.6.0)
|
||||
diff-lcs (~> 1.1.2)
|
||||
rspec-mocks (2.6.0)
|
||||
rubyzip (0.9.4)
|
||||
selenium-webdriver (2.2.0)
|
||||
childprocess (>= 0.1.9)
|
||||
ffi (>= 1.0.7)
|
||||
json_pure
|
||||
rubyzip
|
||||
sinatra (1.2.6)
|
||||
rack (~> 1.1)
|
||||
tilt (< 2.0, >= 1.2.2)
|
||||
thor (0.14.6)
|
||||
tilt (1.3.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
eris
|
||||
jasmine
|
||||
rake
|
||||
@@ -0,0 +1,32 @@
|
||||
require 'jasmine'
|
||||
|
||||
namespace :jasmine do
|
||||
task :server do
|
||||
require 'jasmine'
|
||||
require 'eris/lib/jasmine_config_overrides'
|
||||
|
||||
puts "your tests are here:"
|
||||
puts " http://localhost:8888/"
|
||||
|
||||
Jasmine::Config.new.start_server
|
||||
end
|
||||
|
||||
desc "Run continuous integration tests"
|
||||
task :ci do
|
||||
require 'jasmine'
|
||||
require "rspec"
|
||||
require "rspec/core/rake_task"
|
||||
require 'eris'
|
||||
ENV['JASMINE_BROWSER'] = 'chrome'
|
||||
|
||||
RSpec::Core::RakeTask.new(:jasmine_continuous_integration_runner) do |t|
|
||||
t.rspec_opts = ["--color", "--format", "progress"]
|
||||
t.verbose = true
|
||||
t.pattern = ["#{Eris::FILE_PATH}/eris/lib/jasmine_rspec_runner.rb"]
|
||||
end
|
||||
Rake::Task["jasmine_continuous_integration_runner"].invoke
|
||||
end
|
||||
end
|
||||
|
||||
desc "Run specs with local config"
|
||||
task :jasmine => ['jasmine:server']
|
||||
@@ -0,0 +1,288 @@
|
||||
enyo.kind({
|
||||
name: "AddBuddyDialog",
|
||||
kind: "ModalDialog",
|
||||
caption: $L("Add Buddy"),
|
||||
style: "height: 402px",
|
||||
contentHeight: "402px",
|
||||
events: {
|
||||
onclosed: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "ApplicationEvents", onWindowHidden: "close"},
|
||||
{name: "addBuddyPane", kind: "Pane", components: [
|
||||
{name: "defaultView", layoutKind: "enyo.VFlexLayout", components:[
|
||||
{kind: "RowGroup", caption: $L("IM Service"), style: "margin:5px;", components: [
|
||||
{name: "service", kind: "AccountSelector", onChange: "serviceChanged"}
|
||||
]},
|
||||
|
||||
{name: "skypeBuddyNameRow", kind: "RowGroup", style: "height:25px;margin:5px;", caption: $L("Buddy Name"), components: [
|
||||
{name: "skypeName", kind: "CustomButton", caption: $L("Search for a contact"), onclick: "startSkypeSearch"}
|
||||
]},
|
||||
|
||||
{name: "buddyNameRow", kind: "RowGroup", style: "height:25px;margin:5px;", caption: $L("Buddy Name"), components: [
|
||||
{name: "name", kind: "Input", hint: $L("Enter a buddy name"), oninput: "updateAddButton", onchange: "focusMessage", changeOnKeypress: true, autoCapitalize: "lowercase", autocorrect: false, inputType: "email"}
|
||||
]},
|
||||
/*
|
||||
{kind: "RowGroup", caption: $L("Add To Buddy Group"), components: [
|
||||
{name: "group", kind: "ListSelector", items: [
|
||||
{caption: "Buddies", value:"buddies"},
|
||||
{caption: "Family", value:"family"},
|
||||
{caption: "Friends", value: "friends"}
|
||||
]}
|
||||
]},
|
||||
*/
|
||||
{kind: "RowGroup", caption: $L("Invitation Message"), style: "margin:5px;", components: [
|
||||
{name: "message", kind: "Input", onkeypress: "messageKeyPressed", changeOnKeypress: true, hint: $L("Enter a message")}
|
||||
]},
|
||||
|
||||
{layoutKind: "enyo.HFlexLayout", style: "margin:5px;", components: [
|
||||
{name: "cancelButton", kind: "Button", caption: $L("Cancel"), flex:1, onclick: "cancelClicked"},
|
||||
{name: "addButton", kind: "Button", caption: $L("Add Buddy"), flex:1, className:"enyo-button-dark", onclick: "addBuddyClicked"}
|
||||
]}
|
||||
]},
|
||||
|
||||
{name: "skypeSearchUI", kind: "CrossAppUI", lazy: true, layoutKind: "enyo.VFlexLayout", onResult: "handleSkypeSearchResult"}
|
||||
|
||||
]},
|
||||
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{name: "buddyAdder", dbKind: "com.palm.imcommand:1", method: "put", onSuccess: "addedBuddy"}
|
||||
]}
|
||||
|
||||
],
|
||||
renderOpen: function() {
|
||||
this.inherited(arguments);
|
||||
this.registerAccountService();
|
||||
this.updateAccountSelector();
|
||||
if (enyo.g11n.currentLocale().language == "de") {
|
||||
this.$.addButton.flex = 1.75;
|
||||
}
|
||||
},
|
||||
accountsChanged: function() {
|
||||
this.accounts = this.accountService.getImAccounts();
|
||||
|
||||
this.updateAccountSelector();
|
||||
this.updateInputs();
|
||||
this.updateAddButton();
|
||||
},
|
||||
updateAccountSelector: function() {
|
||||
var items = [];
|
||||
|
||||
//enyo.log("List of IM accounts: ", this.accounts);
|
||||
for (var i = 0; i < this.accounts.length; i++) {
|
||||
items.push({
|
||||
caption: this.accounts[i].loc_shortName,
|
||||
value: this.accounts[i]._id,
|
||||
username: this.accounts[i].username
|
||||
});
|
||||
}
|
||||
|
||||
this.$.service.setItems(items);
|
||||
this.updateNameHintText(this.$.service.getValue());
|
||||
},
|
||||
updateInputs: function() {
|
||||
var disabled = this.accounts.length === 0;
|
||||
this.$.name.setDisabled(disabled);
|
||||
this.$.message.setDisabled(disabled);
|
||||
},
|
||||
updateAddButton: function() {
|
||||
var nameValue = this.$.name.getValue();
|
||||
|
||||
nameValue = nameValue.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // trim name value
|
||||
if (nameValue.length === 0 || this.accounts.length === 0) {
|
||||
this.disableAddButton();
|
||||
} else {
|
||||
this.enableAddButton();
|
||||
}
|
||||
},
|
||||
serviceChanged: function(inSender, inValue, inOldValue) {
|
||||
this.updateNameHintText(inValue);
|
||||
},
|
||||
updateNameHintText: function(accountId) {
|
||||
var account = this.getAccount(accountId);
|
||||
|
||||
if (account && account.serviceName === "type_skype") {
|
||||
this.$.buddyNameRow.hide();
|
||||
this.$.skypeBuddyNameRow.show();
|
||||
} else {
|
||||
this.$.skypeBuddyNameRow.hide();
|
||||
this.$.buddyNameRow.show();
|
||||
}
|
||||
|
||||
if (account && account.serviceName === "type_gtalk") {
|
||||
// gtalk requires entire email address for adding a buddy, so change the hint text to reflect that
|
||||
this.$.name.setHint($L("Email address..."));
|
||||
} else {
|
||||
this.$.name.setHint($L("Enter a buddy name"));
|
||||
}
|
||||
},
|
||||
focusMessage: function(inSender, inEvent) {
|
||||
this.$.message.forceFocus();
|
||||
},
|
||||
disableAddButton: function() {
|
||||
this.$.addButton.setDisabled(true);
|
||||
},
|
||||
enableAddButton: function() {
|
||||
this.$.addButton.setDisabled(false);
|
||||
},
|
||||
messageEntered: function() {
|
||||
this.$.message.forceBlur();
|
||||
this.addBuddy();
|
||||
this.closeDialog();
|
||||
},
|
||||
messageKeyPressed: function(inSender, inEvent) {
|
||||
if (inEvent.keyCode === 13 && (this.$.name.getValue() && this.$.name.getValue().replace(/\s*$/, "").length > 0)) {
|
||||
this.messageEntered();
|
||||
}
|
||||
},
|
||||
addBuddyClicked: function(inSender, inResponse) {
|
||||
this.addBuddy();
|
||||
this.closeDialog();
|
||||
},
|
||||
getData: function() {
|
||||
this.data = {};
|
||||
this.data.accountId = this.$.service.getValue();
|
||||
this.data.buddyName = this.$.name.getValue();
|
||||
this.data.message = this.$.message.getValue();
|
||||
},
|
||||
addBuddy: function() {
|
||||
this.getData();
|
||||
|
||||
if (!this.data.buddyName || this.data.buddyName.replace(/\s*$/, "").length === 0) {
|
||||
enyo.warn("@@@@@@@@@@@@@@ Cannot add a buddy if no name is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
var account = this.getAccount(this.data.accountId);
|
||||
if (!account) {
|
||||
enyo.warn("@@@@@@@@@@@@@@ Cannot find account to add buddy");
|
||||
return;
|
||||
}
|
||||
|
||||
enyo.log("Adding a buddy to account ", this.data.accountId, " using service ", account.serviceName);
|
||||
var imcommand = enyo.application.accountService.getImCommandKind(account.serviceName);
|
||||
this.$.buddyAdder.call({
|
||||
objects:[{
|
||||
_kind: imcommand,
|
||||
command: "sendBuddyInvite",
|
||||
params: {
|
||||
message: this.data.message
|
||||
},
|
||||
handler: "transport",
|
||||
serviceName: account.serviceName,
|
||||
fromUsername: account.username,
|
||||
targetUsername: this.data.buddyName
|
||||
}]
|
||||
});
|
||||
},
|
||||
getAccount: function(id) {
|
||||
var account;
|
||||
for (var i = 0; i < this.accounts.length; i++) {
|
||||
if (this.accounts[i]._id === id) {
|
||||
account = this.accounts[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return account;
|
||||
},
|
||||
cancelClicked: function(inSender, inResponse) {
|
||||
this.closeDialog();
|
||||
},
|
||||
closeDialog: function() {
|
||||
this.$.name.setValue("");
|
||||
this.$.message.setValue("");
|
||||
this.$.skypeName.setCaption($L("Search for a contact"));
|
||||
this.disableAddButton();
|
||||
this.unregisterAccountService();
|
||||
|
||||
this.close();
|
||||
},
|
||||
registerAccountService: function() {
|
||||
this.accountService = enyo.application.accountService;
|
||||
if (this.accountService) {
|
||||
this.accountService.register(this, this.accountsChanged.bind(this));
|
||||
}
|
||||
if (this.$.service.items.length > 0) {
|
||||
// reset selection position
|
||||
this.$.service.setValue(this.$.service.items[0].value);
|
||||
}
|
||||
},
|
||||
unregisterAccountService: function() {
|
||||
if (this.accountService) {
|
||||
this.accountService.unregister(this);
|
||||
this.accountService = undefined;
|
||||
}
|
||||
},
|
||||
startSkypeSearch: function() {
|
||||
var account = this.getAccount(this.$.service.getValue());
|
||||
if(account && account.serviceName === "type_skype") {
|
||||
// create skype search UI view
|
||||
if (!this.$.skypeSearchUI) {
|
||||
this.$.addBuddyPane.createView("skypeSearchUI");
|
||||
}
|
||||
|
||||
this.$.skypeSearchUI.setApp("com.palm.app.skype");
|
||||
this.$.skypeSearchUI.setPath("directorySearch.html");
|
||||
this.$.skypeSearchUI.setParams({});
|
||||
this.$.addBuddyPane.selectViewByName("skypeSearchUI");
|
||||
this.setCaption($L("Search for a contact"));
|
||||
this.$.addBuddyPane.addClass("skype-directory-search");
|
||||
|
||||
}
|
||||
},
|
||||
handleSkypeSearchResult: function(inSender, inResult) {
|
||||
if(inResult.value !== undefined && inResult.value !== "") {
|
||||
|
||||
this.$.name.setValue(inResult.value);
|
||||
this.$.skypeName.setCaption(inResult.value);
|
||||
this.enableAddButton();
|
||||
}
|
||||
this.$.addBuddyPane.removeClass("skype-directory-search");
|
||||
this.setCaption($L("Add Buddy"));
|
||||
this.$.addBuddyPane.selectViewByName("defaultView");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
enyo.kind({
|
||||
name: "AccountSelector",
|
||||
kind: enyo.CustomListSelector,
|
||||
itemKind: "AccountItem",
|
||||
setItemProps: function(inItem) {
|
||||
this.item.setCaption(inItem.caption);
|
||||
this.item.setUsername(inItem.username);
|
||||
this.item.setValue(inItem._id);
|
||||
}
|
||||
});
|
||||
|
||||
enyo.kind({
|
||||
name: "AccountItem",
|
||||
kind: enyo.MenuCheckItem,
|
||||
published: {
|
||||
caption: "",
|
||||
username: ""
|
||||
},
|
||||
chrome: [
|
||||
{name: "item", kind: enyo.Item, tapHighlight: true, align: "center",
|
||||
className: "enyo-menuitem", layoutKind: "HFlexLayout", onclick: "itemClick",
|
||||
components: [
|
||||
{name: "icon", kind: "Image", className: "enyo-menuitem-icon"},
|
||||
{name: "account", layoutKind: "VFlexLayout", components: [
|
||||
{name: "caption"},
|
||||
{name: "user", className: "account-username-text"}
|
||||
]},
|
||||
{name: "arrow", kind: enyo.CustomButton, toggling: true, className: "enyo-menucheckitem-arrow"}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.captionChanged();
|
||||
this.usernameChanged();
|
||||
},
|
||||
captionChanged: function() {
|
||||
this.$.caption.setContent(this.caption);
|
||||
},
|
||||
usernameChanged: function() {
|
||||
this.$.user.setContent(this.username);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
enyo.kind({
|
||||
name: "AppInit",
|
||||
kind: "enyo.Component",
|
||||
init: function() {
|
||||
// set up first launch handler
|
||||
enyo.application.firstLaunchHandler = new FirstLaunchHandler();
|
||||
// set up preferences handler
|
||||
enyo.application.prefsHandler = new PrefsHandler();
|
||||
|
||||
// set up account list
|
||||
enyo.application.accountService = new accountService();
|
||||
enyo.application.accountService.getAccounts();
|
||||
|
||||
// set up notification managers
|
||||
enyo.application.messageDashboardManager = new MessageDashboardManager();
|
||||
enyo.application.inviteDashboardManager = new InviteDashboardManager();
|
||||
enyo.application.class0AlertManager = new Class0AlertManager();
|
||||
|
||||
// set up watchers
|
||||
//enyo.application.buddyWatcher = new BuddyWatcher();
|
||||
enyo.application.inviteWatcher = new InviteWatcher();
|
||||
|
||||
enyo.application.telephonyWatcher = new TelephonyService();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
enyo.kind({
|
||||
name: "ChatButton",
|
||||
kind: enyo.CustomButton,
|
||||
className: "enyo-button-icon chat-button",
|
||||
components: [
|
||||
{name: "caption", className: "chat-button-text"}
|
||||
],
|
||||
captionChanged: function() {
|
||||
this.$.caption.setContent(this.caption);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,238 @@
|
||||
enyo.kind({
|
||||
name: "ChatView",
|
||||
kind: "Pane",
|
||||
className: "conversationList",
|
||||
transitionKind: "enyo.transitions.Simple",
|
||||
events: {
|
||||
onSelectThread: "",
|
||||
onClearUnreadCount: ""
|
||||
},
|
||||
published: {
|
||||
chatThread: "",
|
||||
params:"",
|
||||
loginStates:[]//,
|
||||
// deletedChatThread: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "ApplicationEvents", onWindowHidden:"windowHiddenHandler"},
|
||||
{name: "defaultView", kind: DefaultView},
|
||||
{name: "dbGetById", kind: "DbService", dbKind: "com.palm.db:1", method: "get", onSuccess: "gotRecordById"},
|
||||
{name: "chatThreadServiceFind", kind: "DbService", dbKind: enyo.messaging.thread.dbKind, method: "find", onSuccess: "gotConversationByPersonId", onFailure: "conversationFailure"},
|
||||
{name: "chatThreadServiceCreate", kind: "DbService", dbKind: enyo.messaging.thread.dbKind, method: "put", onSuccess: "threadCreated", onFailure: "conversationFailure"},
|
||||
{name: "errorDialog", kind: "PopupDialog", onAccept: "retryMessage"},
|
||||
{name: "composeView", kind: "ComposeView", onOpenConversation: "openConversationById"},
|
||||
{name: "conversationList", kind: "ConversationList", onCloseConversationList:"closeConversationList",onSelectThread: "selectThread", onClearUnreadCount: "doClearUnreadCount", onOpenComposeView:"openComposeView"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
},
|
||||
chatThreadChanged: function(inOldChatThread) {
|
||||
if(this.chatThread){
|
||||
// Making 'selectViewByName' to be synchronous by passing 'true' as the second
|
||||
// parameter to fix bug DFISH-26620.
|
||||
this.selectViewByName("conversationList", true);
|
||||
}
|
||||
this.$.conversationList.setChatThread(this.chatThread);
|
||||
},
|
||||
selectThread: function(inSender, inThread) {
|
||||
this.doSelectThread(inThread);
|
||||
},
|
||||
setDeletedChatThread: function(inThread){
|
||||
this.$.conversationList.setDeletedChatThread(inThread);
|
||||
},
|
||||
threadLocked: function(inThread){
|
||||
this.$.conversationList.threadLocked(inThread);
|
||||
},
|
||||
loginStatesChanged: function(oldLoginStates){
|
||||
this.$.conversationList.setLoginStates(this.loginStates);
|
||||
this.$.composeView.setLoginStates(this.loginStates);
|
||||
},
|
||||
resize: function() {
|
||||
if (this.getViewName() === "conversationList") {
|
||||
// reset adjusts the scroller and tries to maintain the scroll position
|
||||
this.$.conversationList.resize();
|
||||
}
|
||||
},
|
||||
closeConversationList: function() {
|
||||
if (this.getViewName() === "conversationList") {
|
||||
this.selectViewByName("defaultView");
|
||||
}
|
||||
},
|
||||
//launchParams.compose or launchParams from MessagingApp.js
|
||||
preHandleLaunch: function(params) {
|
||||
if (this.$.conversationList) {
|
||||
// Messaging can be launched from the detailsDialog popup. So close the popup
|
||||
// and allow the user to compose and send their message.
|
||||
this.$.conversationList.closeDetailsDialog();
|
||||
|
||||
}
|
||||
if (params.ims && params.ims.length > 0) {
|
||||
var im = params.ims[0];
|
||||
var imIsOk = this.validateAccountAndDisplayWarning(im);
|
||||
if (imIsOk){
|
||||
this.handleLaunch(params);
|
||||
}
|
||||
}
|
||||
else if(params.phoneNumbers && params.phoneNumbers.length > 0){
|
||||
var phone = params.phoneNumbers[0];
|
||||
var textIsOk = this.validateAccountAndDisplayWarning(phone);
|
||||
if (textIsOk){
|
||||
this.handleLaunch(params);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.handleLaunch(params);
|
||||
}
|
||||
},
|
||||
handleLaunch: function(params) {
|
||||
if (params && params.personId) {
|
||||
enyo.log("ChatView::handleLaunch::should be conversation view");
|
||||
this.composeParameter = params;
|
||||
var whereClause = [{ prop: "personId", op: "=", val: params.personId }];
|
||||
this.$.chatThreadServiceFind.cancel();
|
||||
this.$.chatThreadServiceFind.call({
|
||||
query: {where: whereClause}
|
||||
});//gotConversationByPersonId
|
||||
}
|
||||
else {
|
||||
if (this.getViewName() === "conversationList" && this.$.conversationList) {
|
||||
this.$.conversationList.closeConversation();
|
||||
}
|
||||
this.doSelectThread(undefined);
|
||||
this.openComposeView(this, params);
|
||||
}
|
||||
},
|
||||
openComposeView: function(inSender, params){
|
||||
if (params) {
|
||||
this.$.composeView.setParams(params);
|
||||
}
|
||||
this.selectViewByName("composeView");
|
||||
},
|
||||
gotConversationByPersonId: function(inSender, inResponse){
|
||||
//todo: found more than one converstaion by personId, both are not locked, bug in chatthread?
|
||||
if (inResponse.returnValue && inResponse.results && inResponse.results.length > 0) {
|
||||
//show found thread
|
||||
if (!this.chatThread || (this.chatThread && this.chatThread._id !== inResponse.results[0]._id)) {
|
||||
this.chatThread = inResponse.results[0];
|
||||
this.doSelectThread(this.chatThread);
|
||||
}
|
||||
if (this.composeParameter) {
|
||||
var overrideReplyAddress = {};
|
||||
if (this.composeParameter.ims && this.composeParameter.ims.length > 0) {
|
||||
overrideReplyAddress = this.composeParameter.ims[0];
|
||||
}
|
||||
else
|
||||
if (this.composeParameter.phoneNumbers && this.composeParameter.phoneNumbers.length > 0) {
|
||||
overrideReplyAddress = this.composeParameter.phoneNumbers[0];
|
||||
}
|
||||
|
||||
var addr = overrideReplyAddress.value || overrideReplyAddress.addr;
|
||||
var serviceName = overrideReplyAddress.serviceName || overrideReplyAddress.type;
|
||||
enyo.log("ChatView::gotConversationByPersonId::Overriding reply address serviceName:", serviceName, " addr:", addr);
|
||||
this.chatThread.replyAddress = addr;
|
||||
this.chatThread.normalizedAddress = enyo.messaging.utils.normalizeAddress(addr, serviceName);
|
||||
this.chatThread.replyService = serviceName;
|
||||
}
|
||||
this.composeParameter = undefined;
|
||||
this.chatThreadChanged();
|
||||
}
|
||||
//case that start chat from contact's im, which has personId, but no chatthread with this person
|
||||
else if (this.composeParameter.personId) {
|
||||
this.$.dbGetById.call({"ids":[this.composeParameter.personId]});//gotRecordById()
|
||||
}
|
||||
},
|
||||
|
||||
//thread is created based on composeParameter.personId
|
||||
threadCreated : function(inSender, inResponse){
|
||||
if (inResponse.returnValue && inResponse.results && inResponse.results.length > 0) {
|
||||
this.$.dbGetById.call({"ids":[inResponse.results[0].id]});//gotRecordById()
|
||||
this.composeParameter = undefined;
|
||||
}
|
||||
},
|
||||
gotRecordById: function(inSender, inResponse){
|
||||
if (inResponse.returnValue && inResponse.results && inResponse.results.length > 0) {
|
||||
if (inResponse.results[0]._kind === enyo.messaging.thread.dbKind) {
|
||||
//case that compose view create a chatthread, now need open that chatthread, also thread is created in this file for case that composeParamter has personId
|
||||
if (!this.chatThread || (this.chatThread && this.chatThread._id !== inResponse.results[0]._id)) {
|
||||
this.chatThread = inResponse.results[0];
|
||||
this.chatThreadChanged();
|
||||
this.doSelectThread(this.chatThread);
|
||||
}
|
||||
}
|
||||
else if (inResponse.results[0]._kind === "com.palm.person:1") {
|
||||
//creat chatthread based on person info, which only displayName which generated from person will be used
|
||||
|
||||
var person = inResponse.results[0];
|
||||
var address;
|
||||
if (this.composeParameter.phoneNumbers) {
|
||||
address = this.composeParameter.phoneNumbers[0];
|
||||
}
|
||||
else if (this.composeParameter.ims){
|
||||
address = this.composeParameter.ims[0];
|
||||
}
|
||||
var replyAddress = address.value;
|
||||
var serviceName = address.type;
|
||||
var displayName = enyo.messaging.person.getDisplayName(person);
|
||||
this.$.chatThreadServiceCreate.call({
|
||||
objects: [{
|
||||
"_kind": enyo.messaging.thread.dbKind,
|
||||
"timestamp": new Date().getTime(),
|
||||
"personId": this.composeParameter.personId,
|
||||
"displayName": enyo.messaging.person.isNotBlank(displayName) ? displayName : undefined,
|
||||
"replyAddress": replyAddress,
|
||||
"normalizedAddress": enyo.messaging.utils.normalizeAddress(replyAddress, serviceName),
|
||||
"replyService": serviceName,
|
||||
"summary": "",
|
||||
"flags": {
|
||||
"visible": false
|
||||
}
|
||||
}]
|
||||
});//threadCreated();
|
||||
}
|
||||
}
|
||||
else {//edge case for non-valid person id, launch compose view instead
|
||||
enyo.warn("ChatView::launch parameter has a nonvalid personid");
|
||||
this.composeParameter.personId = undefined;
|
||||
this.$.composeView.setParams(this.composeParameter);
|
||||
this.composeParameter = undefined;
|
||||
this.selectViewByName("composeView");
|
||||
}
|
||||
},
|
||||
validateAccountAndDisplayWarning: function(addressObj) {
|
||||
var serviceName = addressObj.serviceName || addressObj.type;
|
||||
var isSupported = enyo.application.accountService.getAccount(serviceName);
|
||||
if (!isSupported) {
|
||||
var errorMessage;
|
||||
if (enyo.messaging.utils.isTextMessage(serviceName)) {
|
||||
errorMessage = $L("SMS capability is required.");
|
||||
}
|
||||
else {
|
||||
errorMessage = $L("You have not set up an IM account of this type.");
|
||||
}
|
||||
|
||||
this.$.errorDialog.openAtCenter();
|
||||
this.$.errorDialog.setTitle($L("Unable to launch chat"));
|
||||
this.$.errorDialog.setMessage(errorMessage);
|
||||
this.$.errorDialog.setCancelButtonCaption($L("OK"));
|
||||
this.$.errorDialog.hideAcceptButton();
|
||||
}
|
||||
|
||||
return isSupported;
|
||||
},
|
||||
rendered: function() {
|
||||
this.inherited(arguments);
|
||||
},
|
||||
openConversationById: function(inSender, inThreadId){
|
||||
if(inThreadId)
|
||||
{ this.$.dbGetById.call({
|
||||
"ids": [inThreadId]
|
||||
});//gotRecordById()
|
||||
}
|
||||
},
|
||||
windowHiddenHandler: function(){
|
||||
this.selectViewByName("defaultView");
|
||||
},
|
||||
conversationFailure: function(inSender, inResponse){
|
||||
enyo.error("ConverstaionList::conversationFailure::inResponse:", inResponse);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
enyo.kind({
|
||||
name: "DefaultView",
|
||||
kind: enyo.VFlexBox,
|
||||
className: "composeview",
|
||||
components: [
|
||||
{kind: "VFlexBox", flex: 1, components: [
|
||||
{kind: "Toolbar", className:"enyo-toolbar-light conversation-header", layoutKind: "HFlexLayout"},
|
||||
{className:"header-shadow"},
|
||||
{kind: "VFlexBox", className: "body-placeholder", flex: 1, pack: "center", align: "center", components: [
|
||||
{kind:"Spacer", flex:1},
|
||||
{kind: "Image", src: "images/icon-165x165.png"},
|
||||
{kind:"Spacer"}
|
||||
]}
|
||||
]},
|
||||
{className:"footer-shadow footer-app-shadow"},
|
||||
{kind: "Toolbar", className:"enyo-toolbar-light conversation-bottom", layoutKind: "HFlexLayout", components: [
|
||||
{name: "slidingDrag", slidingHandler: true, kind: "GrabButton" }/*,
|
||||
{name: "richText", kind: "RichText", hint: $L("Enter message here..."), flex: 1, disabled: true}*/
|
||||
]}
|
||||
]
|
||||
});
|
||||
@@ -0,0 +1,191 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "Messaging",
|
||||
kind: enyo.VFlexBox,
|
||||
events: {
|
||||
onSelectThread: "",
|
||||
onDeleteThread: "",
|
||||
onThreadLocked: "",
|
||||
onOpenComposeView: "",
|
||||
onNewBuddy: "",
|
||||
onNewFavorite: "",
|
||||
onLoginStatesChange: "",
|
||||
onAddAccount: ""
|
||||
},
|
||||
components: [
|
||||
{kind:"Toolbar", className:"enyo-toolbar-light messaging-availability", components: [
|
||||
{name: "imStatus", kind: "ImStatus", onLoginStatesChange: "loginStatesChange", onAddAccount: "doAddAccount"}
|
||||
]},
|
||||
{kind: "TabGroup", onChange: "menuToggle", className:"messaging-radiobuttons", components: [
|
||||
{value: 0, components: [{name: "unreadCountButton", kind: "ChatButton"}]},
|
||||
{value: 1, icon: "images/menu-icon-buddies.png"},
|
||||
{value: 2, icon: "images/menu-icon-favorites.png"}
|
||||
]},
|
||||
{kind: "Pane", flex: 1, transitionKind: "enyo.transitions.Simple", components: [
|
||||
{kind: "ThreadList", onSelectThread: "doSelectThread", onDeleteThread: "doDeleteThread", onThreadLocked: "doThreadLocked", onUnreadCountChanged: "updateUnreadCount"},
|
||||
{kind: "BuddyList", onSelectBuddy: "personSelected", onDeleteThread: "doDeleteThread"},
|
||||
{kind: "FavoriteList", onSelectFavorite: "personSelected"}
|
||||
]},
|
||||
{className:"footer-shadow footer-app-shadow"},
|
||||
{kind: "Toolbar", className:"enyo-toolbar-light", components: [
|
||||
{name: "conversationBtn", kind: "Button", label:$L("New Conversation"), onclick: "doOpenComposeView"},
|
||||
{name: "buddyBtn", kind: "Button", label:$L("Add Buddy"), onclick: "doNewBuddy", showing: false},
|
||||
{name: "favoriteBtn", kind: "Button", label:$L("Add Favorite"), onclick: "doNewFavorite", showing: false}
|
||||
]},
|
||||
{kind: "DbService", dbKind: "com.palm.chatthread:1", components: [
|
||||
{name: "findThread", method: "find", onSuccess: "threadFound"},
|
||||
{name: "createThread", method: "put", onSuccess: "threadCreated"},
|
||||
{name: "threadGetter", method: "get", onSuccess: "threadFound"}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
|
||||
this.buttons = [];
|
||||
this.buttons.push(this.$.conversationBtn);
|
||||
this.buttons.push(this.$.buddyBtn);
|
||||
this.buttons.push(this.$.favoriteBtn);
|
||||
},
|
||||
loginStatesChange: function(inSender, inStates){
|
||||
this.doLoginStatesChange(inStates);
|
||||
this.$.buddyList.setLoginStates(inStates);
|
||||
},
|
||||
workaroundListVisibilityBug: function(inValue) {
|
||||
// this object loses scrollTop when it goes invisible, this hack restores scrollTop
|
||||
if (inValue === undefined || inValue === 0) {
|
||||
this.$.threadList.updateList();
|
||||
}
|
||||
// this one has never rendered at startup (it was invisible), we need more power for this one
|
||||
if (inValue === undefined || inValue === 1) {
|
||||
this.$.buddyList.updateList();
|
||||
}
|
||||
//this.$.aggregator.findThreads();
|
||||
if (inValue === undefined || inValue === 2) {
|
||||
this.$.favoriteList.updateList();
|
||||
}
|
||||
},
|
||||
menuToggle: function(inSender, inValue) {
|
||||
this.$.pane.selectViewByIndex(inValue, true);
|
||||
this.updateButtons(inValue);
|
||||
// this.updateDashboard(inValue);
|
||||
// FIXME: should no longer be needed.
|
||||
this.workaroundListVisibilityBug(inValue);
|
||||
},
|
||||
highlightTabButton: function(inValue) {
|
||||
this.$.tabGroup.setValue(inValue);
|
||||
},
|
||||
updateButtons: function(index) {
|
||||
for (var i = 0; i < this.buttons.length; i++) {
|
||||
this.buttons[i].setShowing(i === index);
|
||||
}
|
||||
},
|
||||
updateDashboard: function(pane) {
|
||||
var dashboardMgr = enyo.application.messageDashboardManager;
|
||||
var filter = dashboardMgr.getFilter();
|
||||
|
||||
if (pane === 0) {
|
||||
// this is the conversation view
|
||||
if (filter) {
|
||||
filter = {};
|
||||
}
|
||||
filter.filterAll = true;
|
||||
} else {
|
||||
filter.filterAll = false;
|
||||
}
|
||||
|
||||
dashboardMgr.setFilter(filter);
|
||||
},
|
||||
personSelected: function(inSender, inPerson) {
|
||||
this.person = inPerson;
|
||||
this.findThread();
|
||||
},
|
||||
findThread: function() {
|
||||
this.$.findThread.cancel();
|
||||
if (this.person.personId) {
|
||||
this.$.findThread.call({
|
||||
query: {
|
||||
where: [{
|
||||
prop: "personId",
|
||||
op: "=",
|
||||
val: this.person.personId
|
||||
}]
|
||||
}
|
||||
//parseQuery("where personId='" + this.person.personId + "'")
|
||||
});
|
||||
} else {
|
||||
// if person Id is not found, use username and service name to search
|
||||
// for a chat thread
|
||||
this.$.findThread.call({
|
||||
query: {
|
||||
where: [{
|
||||
prop: "normalizedAddress",
|
||||
op: "=",
|
||||
val: enyo.messaging.utils.normalizeAddress(this.person.username, this.person.serviceName)
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
threadFound: function(inSender, inResponse) {
|
||||
var threads = inResponse.results;
|
||||
if (threads.length > 0) {
|
||||
enyo.log("got chat thread record: ", threads[0]._id);
|
||||
this.doSelectThread(threads[0]);
|
||||
} else {
|
||||
this.createThread();
|
||||
}
|
||||
},
|
||||
createThread: function() {
|
||||
var p = this.person; // there can be a race condition here if user tap on different buddies or favorites quick. we may want to use an array to handle pending person selection.
|
||||
this.$.createThread.call({
|
||||
objects: [{
|
||||
"_kind": "com.palm.chatthread:1",
|
||||
"timestamp": new Date().getTime(),
|
||||
"personId": p.personId,
|
||||
"displayName": p.displayName || p.username,
|
||||
"replyAddress": p.username,
|
||||
"normalizedAddress": p.username,
|
||||
"replyService": p.serviceName,
|
||||
"summary": "",
|
||||
"flags": {"visible":false}
|
||||
}]
|
||||
});
|
||||
},
|
||||
threadCreated: function(inSender, inResponse) {
|
||||
//enyo.log("*********************************** created thread: ", inResponse);
|
||||
if (inResponse.returnValue && inResponse.results && inResponse.results.length > 0) {
|
||||
this.$.threadGetter.call({"ids": [inResponse.results[0].id]});
|
||||
}
|
||||
},
|
||||
setOfflinePrefs: function(prefs) {
|
||||
this.$.buddyList.setShowOffline(!prefs.showOnlineBuddiesOnly);
|
||||
},
|
||||
updateUnreadCount: function(inSender, inCount) {
|
||||
//enyo.log("*********** unread count: ", inCount);
|
||||
this.$.unreadCountButton.setClassName("enyo-button-icon chat-button"+ (inCount > 99?"-plus":""));
|
||||
this.$.unreadCountButton.setCaption(inCount > 99?"99":(inCount || ""));
|
||||
},
|
||||
setSelection: function(chatThread){
|
||||
this.$.threadList.setSelection(chatThread);
|
||||
this.$.buddyList.setSelection(chatThread);
|
||||
this.$.favoriteList.setSelection(chatThread);
|
||||
},
|
||||
windowHiddenHandler: function(){
|
||||
// show chat list
|
||||
this.menuToggle(this, 0);
|
||||
// highlights the radio button for conversations view
|
||||
this.$.tabGroup.setValue(0);
|
||||
|
||||
this.$.threadList.windowHiddenHandler();
|
||||
this.$.buddyList.windowHiddenHandler();
|
||||
this.$.favoriteList.windowHiddenHandler();
|
||||
this.$.imStatus.windowHiddenHandler();
|
||||
},
|
||||
windowShownHandler:function() {
|
||||
// reset all lists so that their db service restarted
|
||||
this.$.threadList.resetList();
|
||||
this.$.buddyList.resetList();
|
||||
this.$.favoriteList.resetList();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,482 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "enyo.MessagingApp",
|
||||
kind: "Pane",
|
||||
transitionKind: "enyo.transitions.Simple",
|
||||
className: "enyo-bg",
|
||||
components: [
|
||||
{kind: "ApplicationEvents", onOpenAppMenu: "openAppMenuHandler", onBack:"backHandler", onWindowParamsChange:"windowParamsChangeHandler",
|
||||
onResize:"resizeHandler", onUnload:"unloadHandler", onWindowHidden:"windowHiddenHandler", onWindowShown:"windowShownHandler",
|
||||
onWindowActivated:"windowActivatedHandler", onWindowDeactivated:"windowDeactivatedHandler"},
|
||||
{name: "slidingGroup", kind: enyo.SlidingPane, className: "loading-background", components: [
|
||||
{width: "320px", name: "messagingSliding", fixedWidth: true, components: [{
|
||||
flex: 1,
|
||||
kind: "Messaging",
|
||||
onSelectThread: "selectThread",
|
||||
onDeleteThread: "deleteThread",
|
||||
onThreadLocked: "threadLocked",
|
||||
onOpenComposeView: "openComposeView",
|
||||
onNewBuddy: "addBuddy",
|
||||
onNewFavorite: "showAddFavoriteList",
|
||||
onLoginStatesChange: "LoginStatesChange",
|
||||
onAddAccount: "showAddAccountDialog"
|
||||
}]},
|
||||
{name: "conversationSliding", flex: 1, className:"conversationSliding-right", components: [{
|
||||
kind: "ChatView",
|
||||
onSelectThread: "selectThread",
|
||||
onClearUnreadCount: "clearUnreadCount",
|
||||
flex: 1
|
||||
}]}
|
||||
]},
|
||||
{name: "firstLaunch", kind: "FirstLaunch", onFirstLaunchDone: "doneFirstLaunch", lazy: true},
|
||||
{name: "preferencesView", kind: "PreferencesView", onClosePreferences: "closePreferences", lazy: true},
|
||||
{name: "threadServiceMerge", kind: "DbService", dbKind: "com.palm.chatthread:1", method: "merge"/*, onSuccess: "setUnreadcount"*/},
|
||||
{name: "threadFinder", kind: "DbService", dbKind: "com.palm.chatthread:1", method: "get", onSuccess: "gotThread"},
|
||||
{name: "readMessageMerger", kind: "DbService", dbKind: "com.palm.message:1", method: "merge"},
|
||||
{name: "addFavoriteList", kind: "com.palm.library.contactsui.personListDialog", onBeforeOpen: "onBeforeOpenFavoriteList", onContactClick: "favoriteSelected", onCancelClick: "closeAddFavoriteList", mode: "noFavoritesOnly", showFavStars: false, showIMStatuses: false},
|
||||
{name: "buddyDialog", kind: "AddBuddyDialog"},
|
||||
{name: "noAccountDialog", kind: "ModalDialog", autoClose: true, style: "width: 20em; height: 8em;", contentHeight: "8em", components: [
|
||||
{content: $L("Add a messaging account to set your online status."), style:"padding-bottom:22px;text-align:center;"},
|
||||
{layoutKind: "HFlexLayout", components: [
|
||||
{kind: "Button", flex:1, caption: $L("OK"), onclick: "closeDialog"},
|
||||
{kind: "Button", flex:1, caption: $L("Add an Account"), onclick: "addAccountClicked"}
|
||||
]}
|
||||
]},
|
||||
{name: "appMenu", kind: "AppMenu", automatic: false, onOpen: "menuOnOpen", components: [
|
||||
{name: "addBuddyMenuItem", caption: $L("Add Buddy"), onclick: "addBuddy"},
|
||||
{name: "showOfflineMenuItem", caption: $L("Show Offline Buddies"), onclick: "updateOfflinePrefs", showOffline: true},
|
||||
{name: "prefsAccntsMenuItem", caption: $L("Preferences & Accounts"), onclick: "openPreferences"},
|
||||
{name: "helpMenuItem", kind: enyo.HelpMenu, target: "http://help.palm.com/messaging/index.html"}
|
||||
]},
|
||||
{name: "firstUseAppMenu", kind: "AppMenu", automatic: false, components: []},
|
||||
{name: "buddyInviteDelete", kind: "DbService", dbKind: "com.palm.iminvitation:1", method: "del"},
|
||||
{name: "buddyInviteFind", kind: "DbService", dbKind: "com.palm.iminvitation:1", method: "find", onSuccess: "invitesToDelete"}
|
||||
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
|
||||
// Mark main card as cachable off-screen.
|
||||
// This means it will usually not be closed when the user throws it away.
|
||||
if (window.PalmSystem) {
|
||||
PalmSystem.keepAlive(true);
|
||||
}
|
||||
|
||||
this.viewInitialized = false;
|
||||
this.registerServices();
|
||||
|
||||
this.resize();
|
||||
if (window.PalmSystem && enyo.application.firstLaunchHandler.shouldShowFirstLaunch()) {
|
||||
this.handleFirstLaunch();
|
||||
}
|
||||
|
||||
// register for account changes
|
||||
enyo.application.accountService.register(this, this.accountsChanged.bind(this));
|
||||
},
|
||||
menuOnOpen: function(){
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
},
|
||||
accountsChanged: function() {
|
||||
var removedAccount = this.getRemovedAccount(enyo.application.accountService.getImAccounts());
|
||||
if (removedAccount) {
|
||||
// Inform the DashboardManagers that the account has been removed so any
|
||||
// messages related to this account can be removed from the dashboard.
|
||||
enyo.application.messageDashboardManager.accountRemoved(removedAccount);
|
||||
enyo.application.inviteDashboardManager.accountRemoved(removedAccount);
|
||||
|
||||
// Query for all the invite messages associated with this account so they
|
||||
// can be deleted.
|
||||
this.$.buddyInviteFind.cancel();
|
||||
this.$.buddyInviteFind.call({
|
||||
query: {
|
||||
where: [{ prop: "accountId",
|
||||
op: "=",
|
||||
val: removedAccount.accountId
|
||||
}],
|
||||
select: ["_id"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
invitesToDelete: function(inSender, inResponse) {
|
||||
if (inResponse && inResponse.results) {
|
||||
for (var i = 0; i < inResponse.results.length; i++) {
|
||||
this.$.buddyInviteDelete.call({
|
||||
ids: [inResponse.results[i]._id]
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
LoginStatesChange: function(inSender, inStates){
|
||||
if (this.$.chatView) {
|
||||
this.$.chatView.setLoginStates(inStates);
|
||||
}
|
||||
},
|
||||
prefsUpdated: function(inPrefs) {
|
||||
this.prefs = inPrefs;
|
||||
if (!this.prefs) {
|
||||
// app is still initializing
|
||||
return;
|
||||
}
|
||||
|
||||
this.setOffline();
|
||||
},
|
||||
updatePrefs: function() {
|
||||
this.prefsHandler.setPrefs(this.prefs);
|
||||
},
|
||||
selectThread: function(inSender, inThread){
|
||||
//inThread could be null
|
||||
this.checkWideLayout();
|
||||
enyo.application.selectedThread = inThread;
|
||||
if (inSender === "Messaging") {
|
||||
this.$.chatView.setChatThread(inThread);
|
||||
}
|
||||
else if (inSender === "ChatView") {
|
||||
//update highlight for threadlist, buddylist and favorite list
|
||||
this.$.messaging.setSelection(inThread);
|
||||
}
|
||||
else {
|
||||
this.$.chatView.setChatThread(inThread);
|
||||
this.$.messaging.setSelection(inThread);
|
||||
}
|
||||
},
|
||||
deleteThread: function(inSender, inThread) {
|
||||
this.$.chatView.setDeletedChatThread(inThread);
|
||||
},
|
||||
threadLocked:function(inSender, inThread) {
|
||||
if (this.$.chatView.getViewName() === "chatView") {
|
||||
this.$.chatView.threadLocked(inThread);
|
||||
}
|
||||
},
|
||||
openComposeView: function(inSender) {
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
this.checkWideLayout();
|
||||
if (enyo.application.selectedThread) {
|
||||
this.selectThread(undefined, undefined);
|
||||
}
|
||||
if (this.$.chatView.getViewName() !== "composeView") {
|
||||
this.$.chatView.selectViewByName("composeView");
|
||||
}
|
||||
},
|
||||
checkWideLayout: function() {
|
||||
/*
|
||||
if (!this.$.slidingGroup.getWideLayout()) {
|
||||
this.$.slidingGroup.setSelected(this.$.conversationSliding);
|
||||
}
|
||||
*/
|
||||
},
|
||||
clearUnreadCount: function(inSender, inThreadId){
|
||||
this.clearUnreadCountForThread(inThreadId);
|
||||
this.setThreadMessagesRead(inThreadId);
|
||||
},
|
||||
clearUnreadCountForThread: function(inThreadId) {
|
||||
this.$.threadServiceMerge.call({objects: [
|
||||
{_id: inThreadId, unreadCount: 0}
|
||||
]});
|
||||
},
|
||||
setThreadMessagesRead: function(inThreadId){
|
||||
//enyo.log("MessagingApp: updating inbox messages to be read for thread '", inThreadId, "'");
|
||||
//TODO: may want to use the 'unreadRevSet' value to limit to only new
|
||||
//inbox messages' read flag to be updated
|
||||
this.$.readMessageMerger.call({
|
||||
query: {
|
||||
from: "com.palm.message:1",
|
||||
where: [
|
||||
{
|
||||
"prop": "conversations",
|
||||
"op": "=",
|
||||
"val": inThreadId
|
||||
},
|
||||
{
|
||||
"prop": "folder",
|
||||
"op": "=",
|
||||
"val": enyo.messaging.message.FOLDERS.INBOX
|
||||
}
|
||||
]
|
||||
},
|
||||
props: {
|
||||
flags: {read: true}
|
||||
}
|
||||
});
|
||||
},
|
||||
addBuddy: function(inSender, inEvent) {
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
this.$.buddyDialog.openAtCenter();
|
||||
},
|
||||
showAddAccountDialog: function() {
|
||||
this.$.noAccountDialog.openAtCenter();
|
||||
},
|
||||
addAccountClicked: function() {
|
||||
this.$.noAccountDialog.close();
|
||||
this.openPreferences();
|
||||
},
|
||||
closeDialog: function() {
|
||||
this.$.noAccountDialog.close();
|
||||
},
|
||||
onBeforeOpenFavoriteList: function() {
|
||||
this.$.addFavoriteList.removeClass("enyo-view");
|
||||
},
|
||||
showAddFavoriteList: function() {
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
this.$.addFavoriteList.openAtCenter();
|
||||
},
|
||||
favoriteSelected: function (inSender, inParams) {
|
||||
ContactsLib.PersonFactory.createPersonDisplay(inParams).makeFavorite();
|
||||
this.$.addFavoriteList.closeDialog();
|
||||
},
|
||||
closeAddFavoriteList: function () {
|
||||
this.$.addFavoriteList.closeDialog();
|
||||
},
|
||||
updateOfflinePrefs: function() {
|
||||
this.prefs.showOnlineBuddiesOnly = !this.prefs.showOnlineBuddiesOnly;
|
||||
this.updatePrefs();
|
||||
},
|
||||
setOffline: function() {
|
||||
this.setOfflineMenuItem();
|
||||
this.$.messaging.setOfflinePrefs(this.prefs);
|
||||
},
|
||||
setOfflineMenuItem: function() {
|
||||
if (this.$.showOfflineMenuItem) {
|
||||
if (this.prefs.showOnlineBuddiesOnly) {
|
||||
this.$.showOfflineMenuItem.setCaption($L("Show Offline Buddies"));
|
||||
}
|
||||
else {
|
||||
this.$.showOfflineMenuItem.setCaption($L("Hide Offline Buddies"));
|
||||
}
|
||||
}
|
||||
},
|
||||
handleFirstLaunch: function() {
|
||||
if (!this.$.firstLaunch) {
|
||||
this.createView("firstLaunch");
|
||||
}
|
||||
this.selectView(this.$.firstLaunch, true);
|
||||
|
||||
// show buddy list after first launch
|
||||
this.$.messaging.menuToggle(this, 1);
|
||||
// highlights the radio button for conversations view
|
||||
this.$.messaging.highlightTabButton(1);
|
||||
},
|
||||
doneFirstLaunch: function() {
|
||||
enyo.application.firstLaunchHandler.passedFirstLaunch();
|
||||
|
||||
// change view
|
||||
this.selectViewByName("slidingGroup");
|
||||
this.$.messaging.workaroundListVisibilityBug();
|
||||
},
|
||||
openPreferences: function() {
|
||||
if (!this.$.preferencesView) {
|
||||
this.createView("preferencesView");
|
||||
}
|
||||
this.selectView(this.$.preferencesView, true);
|
||||
// should not filter any messages from dashboards
|
||||
enyo.application.messageDashboardManager.setFilter({filterAll: false, thread: undefined});
|
||||
enyo.application.inviteDashboardManager.setFilter({filterAll: false, thread: undefined});
|
||||
},
|
||||
closePreferences: function(){
|
||||
this.$.messaging.workaroundListVisibilityBug();
|
||||
this.selectViewByName("slidingGroup");
|
||||
// re-enable filtering in dashboards
|
||||
var selected = enyo.application.selectedThread && enyo.application.selectedThread._id;
|
||||
enyo.application.messageDashboardManager.setFilter({filterAll: false, thread: selected});
|
||||
enyo.application.inviteDashboardManager.setFilter({filterAll: false, thread: selected});
|
||||
},
|
||||
backHandler: function() {
|
||||
this.back();
|
||||
},
|
||||
openAppMenuHandler: function() {
|
||||
var menu = !this.prefs || this.prefs.firstUseMode ? this.$.firstLaunchAppMenu : this.$.appMenu;
|
||||
|
||||
if (menu) {
|
||||
menu.removeClass("enyo-view");
|
||||
menu.open();
|
||||
|
||||
if (this.prefs && !this.prefs.firstUseMode) {
|
||||
// initialize offline menu item with respect to preference setttings
|
||||
this.setOfflineMenuItem();
|
||||
}
|
||||
}
|
||||
},
|
||||
/* closeAppMenuHandler: function() {
|
||||
this.$.appMenu.close();
|
||||
},*/
|
||||
resize: function() {
|
||||
this.$.slidingGroup.resize();
|
||||
// instruct the conversation list to resize itself.
|
||||
this.$.chatView.resize();
|
||||
},
|
||||
resizeHandler: function() {
|
||||
this.inherited(arguments);
|
||||
this.resize();
|
||||
},
|
||||
windowParamsChangeHandler: function(inSender, event) {
|
||||
enyo.log("in windowParamsChangeHandler in MessagingApp");
|
||||
enyo.log("event.params: ", event.params);
|
||||
enyo.error("*&*&*&*&* window reactivated: ", event.params);
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
this.checkWideLayout();
|
||||
var launchHandled = this.handleDisplayMessageLaunch(event && event.params);
|
||||
return launchHandled;
|
||||
},
|
||||
handleDisplayMessageLaunch: function (launchParams) {
|
||||
enyo.log("in MessagingApp handleDisplayMessageLaunch");
|
||||
enyo.log("launchParams: ", launchParams);
|
||||
|
||||
if (window.PalmSystem && enyo.application.firstLaunchHandler.shouldShowFirstLaunch()) {
|
||||
this.handleFirstLaunch();
|
||||
return true;
|
||||
}
|
||||
|
||||
this.selectViewByName("slidingGroup");
|
||||
this.registerServices();
|
||||
|
||||
if (launchParams.compose) {
|
||||
this.$.chatView.preHandleLaunch(launchParams.compose);
|
||||
return true;
|
||||
} else if (launchParams.message) {
|
||||
// case launch from taping on notification, find the chat thread and launch it
|
||||
this.findThread(launchParams.message.conversations[0]);
|
||||
return true;
|
||||
} else if (launchParams.messageText || launchParams.attachment|| launchParams.personId || (launchParams.composeRecipients && launchParams.composeRecipients.length)) {
|
||||
enyo.warn("*** Warning, using deprecated launch params:",launchParams);
|
||||
if(launchParams.attachment){
|
||||
enyo.error("*** ERROR:attachment is not supported");
|
||||
}
|
||||
this.$.chatView.preHandleLaunch(launchParams);
|
||||
} else {
|
||||
// show threadsList in Messaging
|
||||
this.showThreadsList();
|
||||
return true;
|
||||
}
|
||||
},
|
||||
findThread: function(threadId) {
|
||||
var tIds = [threadId];
|
||||
this.$.threadFinder.call({ids: tIds});
|
||||
},
|
||||
gotThread: function(inSender, inResponse) {
|
||||
//case that taping on notifications, so, need update both chatView(open or update with new chatThread) and Messaging (for selection of threadList, buddyList)
|
||||
this.selectThread(this, inResponse.results[0]);
|
||||
},
|
||||
showThreadsList: function() {
|
||||
// show threadsList
|
||||
this.$.messaging.menuToggle(this, 0);
|
||||
// highlights the radio button for threadsList view
|
||||
this.$.messaging.highlightTabButton(0);
|
||||
},
|
||||
windowHiddenHandler: function() {
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
if (this.prefs && !this.prefs.firstUseMode) {
|
||||
// close addFavoriteList if it is opened
|
||||
if (this.$.addFavoriteList && this.$.addFavoriteList.isOpen) {
|
||||
this.$.addFavoriteList.closeDialog();
|
||||
}
|
||||
|
||||
// let enclosed components to handle window hidden state themselves
|
||||
this.$.messaging.windowHiddenHandler();
|
||||
|
||||
// clear other app states
|
||||
this.unloadHandler();
|
||||
|
||||
// close menu
|
||||
this.$.appMenu.close();
|
||||
}
|
||||
},
|
||||
windowShownHandler:function() {
|
||||
if (this.prefs && !this.prefs.firstUseMode) {
|
||||
// switch back to default view
|
||||
this.$.slidingGroup.selectViewImmediate(this.$.messagingSliding);
|
||||
this.selectView(this.$.slidingGroup);
|
||||
|
||||
// call windows show handler to child components
|
||||
this.$.messaging.windowShownHandler();
|
||||
}
|
||||
},
|
||||
windowActivatedHandler: function() {
|
||||
//enyo.log("***************** messaging app window activated");
|
||||
// clear new message notifications
|
||||
enyo.application.messageDashboardManager.setAppDeactivated(false);
|
||||
|
||||
if (enyo.application.selectedThread) {
|
||||
this.clearUnreadCount(this, enyo.application.selectedThread._id);
|
||||
}
|
||||
},
|
||||
windowDeactivatedHandler: function() {
|
||||
//enyo.log("***************** messaging app window deactivated");
|
||||
// allow new message notifications to show
|
||||
enyo.application.messageDashboardManager.setAppDeactivated(true);
|
||||
if (enyo.application.selectedThread) {
|
||||
this.clearUnreadCount(this, enyo.application.selectedThread._id);
|
||||
}
|
||||
|
||||
},
|
||||
unloadHandler: function(){
|
||||
if (enyo.application.selectedThread) {
|
||||
// clear out selected thread
|
||||
enyo.application.selectedThread = undefined;
|
||||
}
|
||||
|
||||
this.unregisterServices();
|
||||
// this.inherited(arguments);
|
||||
},
|
||||
registerServices: function() {
|
||||
this.prefsHandler = enyo.application.prefsHandler;
|
||||
if (this.prefsHandler) {
|
||||
this.prefsHandler.register(this, this.prefsUpdated.bind(this));
|
||||
}
|
||||
},
|
||||
unregisterServices: function() {
|
||||
if (this.prefsHandler) {
|
||||
this.prefsHandler.unregister(this);
|
||||
if (this.$.preferencesView) {
|
||||
this.$.preferencesView.unloadHandler();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.$.firstLaunch) {
|
||||
this.$.firstLaunch.unregisterServices();
|
||||
}
|
||||
},
|
||||
|
||||
/***********************************
|
||||
* Functions below are unit tested *
|
||||
***********************************/
|
||||
getRemovedAccount: function(imAccounts) {
|
||||
var removedAccount = null;
|
||||
|
||||
function imAccountsContain(account) {
|
||||
var i = imAccounts.length;
|
||||
while (i--) {
|
||||
if (imAccounts[i].accountId === account.accountId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.accountsInfo === undefined) {
|
||||
this.accountsInfo = [];
|
||||
}
|
||||
|
||||
if (imAccounts && (imAccounts.length > this.accountsInfo.length)) {
|
||||
// Clear out the array so it can refreshed.
|
||||
this.accountsInfo.splice(0, this.accountsInfo.length);
|
||||
|
||||
for (var i = 0; i < imAccounts.length; i++) {
|
||||
this.accountsInfo.push(imAccounts[i]);
|
||||
}
|
||||
} else if (imAccounts && (imAccounts.length < this.accountsInfo.length)) {
|
||||
// Determine which account that was removed.
|
||||
var x = this.accountsInfo.length;
|
||||
while (x--) {
|
||||
if (!imAccountsContain(this.accountsInfo[x])) {
|
||||
removedAccount = this.accountsInfo[x];
|
||||
this.accountsInfo.splice(x, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return removedAccount;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
enyo.kind({
|
||||
name: "PopupDialog",
|
||||
kind: "ModalDialog",
|
||||
height: "190px",
|
||||
events: {
|
||||
onAccept: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "ApplicationEvents", onWindowHidden: "close"},
|
||||
{layoutKind: "VFlexLayout", components:[
|
||||
{name: "message", content: "", className:"enyo-paragraph"},
|
||||
{name: "buttonOrientation", layoutKind: "HFlexLayout", components: [
|
||||
{name: "cancelButton", kind: "Button", flex:1, caption: $L("Cancel"), onclick: "close"},
|
||||
{name: "acceptButton", kind: "Button", flex:1, caption: $L("OK"), className:"enyo-button-negative", onclick: "acceptButtonClick"}
|
||||
]}
|
||||
]}
|
||||
],
|
||||
setTitle: function(title) {
|
||||
if (title !== undefined) {
|
||||
this.setCaption(title);
|
||||
}
|
||||
},
|
||||
setMessage: function(message) {
|
||||
if (message !== undefined) {
|
||||
this.$.message.setContent(message);
|
||||
}
|
||||
},
|
||||
setAcceptButtonCaption: function(caption) {
|
||||
if (caption !== undefined) {
|
||||
this.$.acceptButton.setCaption(caption);
|
||||
}
|
||||
},
|
||||
setCancelButtonCaption: function(caption) {
|
||||
if (caption !== undefined) {
|
||||
this.$.cancelButton.setCaption(caption);
|
||||
}
|
||||
},
|
||||
hideAcceptButton: function() {
|
||||
this.$.acceptButton.hide();
|
||||
},
|
||||
hideCancelButton: function() {
|
||||
this.$.cancelButton.hide();
|
||||
},
|
||||
acceptButtonClick: function() {
|
||||
this.doAccept();
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
BuddyConstants= {
|
||||
OFFLINE_MESSAGE: $L("Please sign in to an IM service to see your buddies."),
|
||||
NO_ONLINE_BUDDY_MESSAGE: $L("You do not have any online buddies."),
|
||||
NO_BUDDY_MESSAGE: $L("You do not have any buddies."),
|
||||
DELETE_BUDDY_PROMPT: $L("Are you sure you want to delete #{name} from your buddies list?")
|
||||
};
|
||||
@@ -0,0 +1,105 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "BuddyItem",
|
||||
kind: "SwipeableItem",
|
||||
confirmCaption: $L("Delete"),
|
||||
layoutKind: "HFlexLayout",
|
||||
components: [
|
||||
{name: "imageContainer", style: "display: inline-block;", components: [
|
||||
{className: "contact-image-border"},
|
||||
{name: "contactImage", kind: "Image", className: "contact-image"}
|
||||
]},
|
||||
{name: "status", className: "status status-buddy"},
|
||||
{className:"message-summary", components: [
|
||||
{layoutKind: "HFlexLayout", align: "center", className:"contact-name-box", components: [
|
||||
{name: "contactName", className: "contact-name"},
|
||||
{name: "favorite", kind: "Image", showing: false, className: "favorite-icon"},
|
||||
{name: "unreadCount", className: "unread-count", showing: false}
|
||||
]},
|
||||
{name: "statusMessage", className: "status-message"}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.addClass("contactItem");
|
||||
},
|
||||
updateContactName: function(inBuddy) {
|
||||
this.$.contactName.setContent(this.getContactName(inBuddy));
|
||||
},
|
||||
updateStatus: function(inAvailability) {
|
||||
this.$.status.setClassName(this.getStatus(inAvailability));
|
||||
},
|
||||
updateStatusMessage: function(inBuddy) {
|
||||
this.$.statusMessage.setContent(this.getStatusMessage(inBuddy));
|
||||
},
|
||||
updateContactImage: function(inPerson) {
|
||||
//enyo.log("#@#@ inPerson: ", inPerson);
|
||||
this.$.contactImage.setAttribute("src", enyo.messaging.person.getDisplayImage(inPerson));
|
||||
},
|
||||
updateContactFavorite: function(inPerson) {
|
||||
this.$.favorite.setShowing(inPerson && inPerson.favorite);
|
||||
},
|
||||
updateUnreadCount: function(inThread, inPerson) {
|
||||
//enyo.log("#@#@ buddy item's chat thread: ", inThread);
|
||||
var count = this.getUnreadCount(inThread);
|
||||
var hasUnread = !this.isThreadSelected(inThread) && Number(count) > 0;
|
||||
|
||||
this.$.contactName.setClassName(this.getContactNameClass(hasUnread, inPerson));
|
||||
this.$.unreadCount.setShowing(hasUnread);
|
||||
this.$.unreadCount.setContent(count);
|
||||
},
|
||||
isThreadSelected: function(inThread) {
|
||||
return inThread && enyo.application.selectedThread && (inThread._id === enyo.application.selectedThread._id);
|
||||
},
|
||||
setBuddy: function(inBuddy) {
|
||||
//enyo.log(inBuddy);
|
||||
this.updateContactName(inBuddy);
|
||||
this.updateUnreadCount(inBuddy.thread, inBuddy.person);
|
||||
this.updateStatus(inBuddy.personAvailability !== undefined ? inBuddy.personAvailability : inBuddy.availability);
|
||||
this.updateStatusMessage(inBuddy);
|
||||
this.updateContactImage(inBuddy.person);
|
||||
this.updateContactFavorite(inBuddy.person);
|
||||
},
|
||||
|
||||
/***********************************
|
||||
* Functions below are unit tested *
|
||||
***********************************/
|
||||
getContactName: function(inBuddy) {
|
||||
var displayName = inBuddy.person ? enyo.messaging.person.getDisplayName(inBuddy.person) : inBuddy.displayName || inBuddy.username;
|
||||
return enyo.messaging.message.removeHtml(displayName);
|
||||
},
|
||||
getStatus: function(inAvailability) {
|
||||
var statusClass = "status-" + enyo.messaging.im.availabilityClasses[inAvailability];
|
||||
return "status status-buddy " + statusClass;
|
||||
},
|
||||
getStatusMessage: function(inBuddy) {
|
||||
var inStatus = inBuddy.status || "";
|
||||
|
||||
if (inBuddy._kind === "com.palm.imbuddystatus.libpurple:1") {
|
||||
// needs to unescape & ' < and > from status messages
|
||||
// that are synced by libpurple transport since the libpurple
|
||||
// library escapes these characters.
|
||||
inStatus = enyo.messaging.message.unescapeText(inStatus);
|
||||
}
|
||||
return enyo.messaging.message.removeHtml(inStatus);
|
||||
},
|
||||
getUnreadCount: function(inThread) {
|
||||
return inThread && inThread.unreadCount || 0;
|
||||
},
|
||||
getContactNameClass: function(hasUnread, inPerson) {
|
||||
var isFavorite = inPerson && inPerson.favorite;
|
||||
var className = "contact-name";
|
||||
|
||||
if (hasUnread && isFavorite) {
|
||||
className += " contact-name-unread-favorite";
|
||||
} else if (hasUnread) {
|
||||
className += " contact-name-unread";
|
||||
} else if (isFavorite) {
|
||||
className += " contact-name-favorite";
|
||||
}
|
||||
|
||||
return className;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,265 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "BuddyList",
|
||||
kind: "VFlexBox",
|
||||
published: {
|
||||
showOffline: false,
|
||||
loginStates: null
|
||||
},
|
||||
events: {
|
||||
onSelectBuddy: "",
|
||||
onShowOfflineBuddies: "",
|
||||
onDeleteThread: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "BuddyService", onSuccess: "gotBuddies", onWatch: "buddiesWatch"},
|
||||
{name: "search", kind: "SearchInput", hint: $L("Search"), className:"enyo-middle", onchange: "filterList", onCancel: "filterList", changeOnInput: true, autoCapitalize: "lowercase", onfocus: "disableKeyboardMannualMode"},
|
||||
{className:"header-shadow header-app-shadow"},
|
||||
{name: "emptyMessage", content: BuddyConstants.OFFLINE_MESSAGE, className: "messageTexts", showing: false},
|
||||
{flex: 1, name: "list", kind: "DbList", desc: false, onQuery: "listQuery", className:"messaging-listsDivider", onSetupRow: "listSetupRow", components: [
|
||||
{kind: "Divider", className:"buddyList-Divider"},
|
||||
{name: "buddyItem", tapHighlight:true, kind: "BuddyItem", onclick: "buddyClicked", onConfirm: "showDeleteConfirmDialog", onCancel: "disableKeyboardMannualMode"}
|
||||
]},
|
||||
{name: "deleteDialog", kind: "PopupDialog", onAccept: "deleteBuddy"},
|
||||
{kind: "DeleteBuddyService", onDeleteThread: "doDeleteThread"}
|
||||
],
|
||||
buddiesWatch: function() {
|
||||
//enyo.log("#@#@ buddies list is updating......");
|
||||
this.$.list.reset();
|
||||
},
|
||||
showOfflineChanged: function() {
|
||||
// dump the existing list dataset so that subsequent list query will use
|
||||
// the new 'showOffline' flag
|
||||
this.$.list.punt();
|
||||
},
|
||||
filterList: function(inSender, inValue){
|
||||
this.filterString = this.$.search.getValue();
|
||||
this.$.list.punt();
|
||||
},
|
||||
listQuery: function(inSender, inQuery) {
|
||||
if (this.filterString && this.filterString.length > 0) {
|
||||
inQuery.where = [{
|
||||
prop: "primary",
|
||||
op: "=",
|
||||
val: true
|
||||
}, {
|
||||
prop: "offline",
|
||||
op: "=",
|
||||
val: this.showOffline ? [true, false] : false
|
||||
}, {
|
||||
prop: "displayName",
|
||||
op: "?",
|
||||
val: this.filterString,
|
||||
"collate": "primary",
|
||||
"tokenize": "all"
|
||||
}];
|
||||
inQuery.search = true;
|
||||
} else {
|
||||
// var whereStr = "where primary=true and offline=" + (this.showOffline ? "[true, false]" : "false") + " and groupAvailability%''";
|
||||
// var where = parseQuery(whereStr).where;
|
||||
var where = [{
|
||||
prop: "primary",
|
||||
op: "=",
|
||||
val: true
|
||||
}, {
|
||||
prop: "offline",
|
||||
op: "=",
|
||||
val: this.showOffline ? [true, false] : false
|
||||
}, {
|
||||
prop: "groupAvailability",
|
||||
op: "%",
|
||||
val: "",
|
||||
collate: "secondary"
|
||||
}];
|
||||
// where[2].collate = "secondary";
|
||||
inQuery.where = where;
|
||||
}
|
||||
|
||||
if (window.PalmSystem) {
|
||||
return this.$.buddyService.call({query: inQuery});
|
||||
} else {
|
||||
if (!this.gotMockData) {
|
||||
this.gotMockData = true;
|
||||
return this.$.buddyService.call({query: inQuery});
|
||||
} else {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
},
|
||||
gotBuddies: function(inSender, inResponse, inRequest) {
|
||||
//enyo.log("#@#@ got buddies: ", inResponse);
|
||||
this.$.list.queryResponse(inResponse, inRequest);
|
||||
this.emptyMessageCheck(inResponse, inRequest, this.loginStates, this.filterString, this.showOffline);
|
||||
},
|
||||
showEmptyMessage: function(message) {
|
||||
this.$.emptyMessage.setContent(message);
|
||||
this.$.emptyMessage.show();
|
||||
},
|
||||
listSetupRow: function(inSender, inBuddy, inIndex) {
|
||||
//enyo.log("#@#@ buddy list set up row" );
|
||||
if (!this.filterString || this.filterString.length === 0) {
|
||||
this.setupDivider(inBuddy, inIndex);
|
||||
}
|
||||
else{
|
||||
this.$.divider.canGenerate = false;
|
||||
}
|
||||
this.$.buddyItem.setBuddy(inBuddy);
|
||||
// highlight selected buddy
|
||||
this.$.buddyItem.addRemoveClass("enyo-item-selected", this.shouldHighlight(inBuddy, this.selectedRecord));
|
||||
},
|
||||
setupDivider: function(inBuddy, inIndex) {
|
||||
var offlineCaption = $L("offline");
|
||||
var caption = inBuddy.offline ? offlineCaption : inBuddy.group;
|
||||
var pt = this.$.list.fetch(inIndex - 1);
|
||||
var previousCaption = pt && (pt.offline ? offlineCaption : pt.group);
|
||||
//
|
||||
var showDivider = caption != previousCaption;
|
||||
//enyo.log("#@#@ current caption: ", caption, " previous caption: ", previousCaption, " show divider: ", showDivider);
|
||||
if (showDivider) {
|
||||
this.$.divider.setCaption(caption.toUpperCase());
|
||||
this.$.buddyItem.addClass("enyo-first");
|
||||
}
|
||||
//
|
||||
this.$.divider.canGenerate = showDivider;
|
||||
},
|
||||
buddyClicked: function(inSender, inEvent) {
|
||||
var selectedRecord = this.$.list.fetch(inEvent.rowIndex);
|
||||
if (selectedRecord && this.selectedRecord && selectedRecord.personId === this.selectedRecord.personId){
|
||||
return;
|
||||
}
|
||||
else{
|
||||
this.selectedRecord = selectedRecord;
|
||||
}
|
||||
|
||||
if (this.selectedRecord) {
|
||||
enyo.log("clicked on buddy: ", (this.selectedRecord.displayName || this.selectedRecord.username));
|
||||
|
||||
// broadcast buddy selection
|
||||
this.doSelectBuddy(this.selectedRecord);
|
||||
this.$.list.refresh();
|
||||
}
|
||||
},
|
||||
showDeleteConfirmDialog: function(inSender, inEvent) {
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
var deletedBuddy = this.$.list.fetch(inEvent);
|
||||
this.$.deleteBuddyService.setBuddy(deletedBuddy);
|
||||
|
||||
var template = new enyo.g11n.Template($L("Are you sure you want to delete #{name} from your buddies list?"));
|
||||
var dialogMessage = template.evaluate({name: deletedBuddy.displayName});
|
||||
enyo.log("Deleting buddy: ", deletedBuddy);
|
||||
|
||||
this.$.deleteDialog.openAtCenter();
|
||||
this.$.deleteDialog.setAcceptButtonCaption($L("Delete"));
|
||||
this.$.deleteDialog.setTitle($L("Remove Buddy"));
|
||||
this.$.deleteDialog.setMessage(dialogMessage);
|
||||
},
|
||||
deleteBuddy: function(inSender, inEvent) {
|
||||
this.$.deleteBuddyService.deleteBuddy();
|
||||
},
|
||||
setSelection: function(inChatThread){
|
||||
if ((this.selectedRecord && inChatThread
|
||||
&& ((inChatThread.buddyId && this.selectedRecord.buddyId === inChatThread.buddyId)
|
||||
|| (inChatThread.personId && this.selectedRecord.personId === inChatThread.personId)))
|
||||
|| (!this.selectedRecord && inChatThread && !inChatThread.personId)) {
|
||||
//case selection didn't change, do nothing
|
||||
return;
|
||||
} else {
|
||||
this.selectedRecord = {
|
||||
personId: inChatThread ? inChatThread.personId : null
|
||||
};
|
||||
// if (!inChatThread) {
|
||||
this.$.list.refresh();
|
||||
// }
|
||||
}
|
||||
},
|
||||
windowHiddenHandler: function(){
|
||||
// dump data buffer
|
||||
this.$.list.punt();
|
||||
// cancel service call
|
||||
//this.$.buddyService.cancel();
|
||||
|
||||
// clear out filter search
|
||||
this.filterString = "";
|
||||
this.$.search.setValue(this.filterString);
|
||||
this.$.search.forceBlur();
|
||||
},
|
||||
disableKeyboardMannualMode: function(){
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
},
|
||||
updateList: function() {
|
||||
this.$.list.update();
|
||||
},
|
||||
resetList: function() {
|
||||
this.$.list.reset();
|
||||
},
|
||||
|
||||
/***********************************
|
||||
* Functions below are unit tested *
|
||||
***********************************/
|
||||
isOffline: function(states) {
|
||||
var offline = true;
|
||||
|
||||
for (var i = 0; i < states.length; i++) {
|
||||
if (states[i].availability !== enyo.messaging.im.availability.OFFLINE && states[i].state !== "offline") {
|
||||
offline = false;
|
||||
}
|
||||
}
|
||||
return offline;
|
||||
},
|
||||
emptyMessageCheck: function(inResponse, inRequest, loginStates, filterString, showOffline) {
|
||||
var message;
|
||||
// by default, hide these messages
|
||||
this.$.emptyMessage.hide();
|
||||
|
||||
if (!loginStates || loginStates.length === 0) {
|
||||
message = BuddyConstants.OFFLINE_MESSAGE;
|
||||
} else if ((inRequest.index === 0) && (!inResponse || !inResponse.results || inResponse.results.length === 0)) {
|
||||
if (filterString && filterString.length > 0) {
|
||||
// No buddies found using search
|
||||
message = enyo.messaging.CONSTANTS.NO_SEARCH_RESULTS;
|
||||
} else if (this.isOffline(loginStates)) {
|
||||
// all accounts are offline
|
||||
message = BuddyConstants.OFFLINE_MESSAGE;
|
||||
} else if (showOffline) {
|
||||
// really has no buddies
|
||||
message = BuddyConstants.NO_BUDDY_MESSAGE;
|
||||
} else {
|
||||
// either has no online buddies or no buddies at all
|
||||
message = BuddyConstants.NO_ONLINE_BUDDY_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (message !== undefined) {
|
||||
this.showEmptyMessage(message);
|
||||
}
|
||||
|
||||
// Added the return for unit testing
|
||||
return message;
|
||||
},
|
||||
shouldHighlight: function(inBuddy, selectedRecord) {
|
||||
if (!selectedRecord) {
|
||||
enyo.log("Selected record doesn't exist, so can't highligh buddy items");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedRecord.personId) {
|
||||
// if person Id is found, use it for matching buddy record
|
||||
return selectedRecord.personId === inBuddy.personId;
|
||||
}
|
||||
|
||||
if (selectedRecord.normalizedAddress) {
|
||||
// If normalizedAddresss is found, use it for matching buddy record.
|
||||
// This selection should come from a tap event from conversations list.
|
||||
return selectedRecord.normalizedAddress === enyo.messaging.utils.normalizeAddress(inBuddy.username, inBuddy.serviceName);
|
||||
}
|
||||
|
||||
if (selectedRecord.username && selectedRecord.serviceName) {
|
||||
// This selection should come from buddy list.
|
||||
return enyo.messaging.utils.normalizeAddress(selectedRecord.username, selectedRecord.serviceName) === enyo.messaging.utils.normalizeAddress(inBuddy.username, inBuddy.serviceName);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
enyo.kind({
|
||||
name: "BuddyService",
|
||||
kind: enyo.Service,
|
||||
requestKind: "BuddyRequest",
|
||||
events: {
|
||||
onWatch: ""
|
||||
}
|
||||
});
|
||||
|
||||
enyo.kind({
|
||||
name: "BuddyRequest",
|
||||
kind: enyo.Request,
|
||||
events: {
|
||||
onWatch: "doWatch"
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{name: "buddies", method: "find", service: "palm://com.palm.tempdb/", dbKind: "com.palm.imbuddystatus:1", onSuccess: "gotBuddies", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/},
|
||||
{name: "buddiesSearch", method: "search", service: "palm://com.palm.tempdb/", dbKind: "com.palm.imbuddystatus:1", onSuccess: "gotBuddies", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/},
|
||||
{name: "threads", dbKind: "com.palm.chatthread:1", method: "find", onSuccess: "gotThreads", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/ },
|
||||
{name: "people", dbKind: "com.palm.person:1", method: "find", onSuccess: "gotPeople", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/}
|
||||
]},
|
||||
{name: "mockBuddies", kind: "MockDb", dbKind: "buddies/com.palm.imbuddystatus:1", method: "find", onSuccess: "gotBuddies", onFailure: "fail", onWatch: "doWatch"},
|
||||
{name: "mockPeople", kind: "MockDb", dbKind: "persons/com.palm.person:1", method: "find", onSuccess: "gotPeople", onFailure: "fail", onWatch: "doWatch"},
|
||||
{name: "mockThreads", kind: "MockDb", dbKind: "chatthreads/com.palm.chatthread:1", method: "find", onSuccess: "gotThreads", onFailure: "fail", onWatch: "doWatch"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
},
|
||||
initComponents: function() {
|
||||
this.inherited(arguments);
|
||||
if (!window.PalmSystem) {
|
||||
this.$.buddies = this.$.mockBuddies;
|
||||
this.$.people = this.$.mockPeople;
|
||||
this.$.threads = this.$.mockThreads;
|
||||
}
|
||||
},
|
||||
call: function() {
|
||||
//enyo.log(this.params);
|
||||
if (this.params && this.params.query && this.params.query.search) {
|
||||
this.params.query.search = undefined;
|
||||
this.$.buddiesSearch.cancel();
|
||||
this.$.buddiesSearch.call(this.params);
|
||||
} else {
|
||||
this.$.buddies.cancel();
|
||||
this.$.buddies.call(this.params);
|
||||
}
|
||||
},
|
||||
reply: function() {
|
||||
//enyo.log(this.data);
|
||||
this.receive(this.data);
|
||||
},
|
||||
fail: function(inSender, inResponse) {
|
||||
enyo.error("BuddyService - DbService failure: ", inResponse);
|
||||
this.reply();
|
||||
},
|
||||
finish: function() {
|
||||
// don't destroy automatically, so we can see watches
|
||||
},
|
||||
gotBuddies: function(inSender, inResponse, inRequest) {
|
||||
//enyo.log("#@#@ got buddies in ", inRequest.latency, "ms");
|
||||
//enyo.log(inResponse);
|
||||
this.data = inResponse;
|
||||
this.assembleIds();
|
||||
this.queryPeople();
|
||||
},
|
||||
assembleIds: function() {
|
||||
this.pids = [];
|
||||
for (var i=0, d; d=this.data.results[i]; i++) {
|
||||
this.pids.push(d.personId);
|
||||
}
|
||||
},
|
||||
queryPeople: function() {
|
||||
this.$.people.id = "buddyRequest_people";
|
||||
this.$.people.cancel();
|
||||
this.$.people.call({query: {
|
||||
where: [{
|
||||
prop: "_id",
|
||||
op: "=",
|
||||
val: this.pids
|
||||
}],
|
||||
select: enyo.messaging.person.selectAttributes
|
||||
}
|
||||
});
|
||||
},
|
||||
gotPeople: function(inSender, inResponse, inRequest) {
|
||||
// enyo.log("#@#@ got people in ", inRequest.latency, "ms");
|
||||
// enyo.log("#@#@ got people", inResponse);
|
||||
// join person data to buddy data
|
||||
enyo.messaging.utils.joinData(this.data, inResponse.results, "_id", "personId", "person");
|
||||
this.queryThreads();
|
||||
},
|
||||
queryThreads: function() {
|
||||
this.$.threads.id = "buddyRequest_threads";
|
||||
this.$.threads.cancel();
|
||||
this.$.threads.call({query: {
|
||||
where: [{
|
||||
prop: "personId",
|
||||
op: "=",
|
||||
val: this.pids
|
||||
}],
|
||||
select: [
|
||||
"_id",
|
||||
"_kind",
|
||||
"personId",
|
||||
"normalizedAddress",
|
||||
"displayName",
|
||||
"unreadCount"
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
gotThreads: function(inSender, inResponse, inRequest) {
|
||||
//enyo.log("#@#@ got chat threads in ", inRequest.latency, "ms");
|
||||
//enyo.log("#@#@ got chat threads: ", inResponse);
|
||||
// join chat thread data to buddy data
|
||||
enyo.messaging.utils.joinData(this.data, inResponse.results, "personId", "personId", "thread");
|
||||
// all done now
|
||||
this.reply();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,129 @@
|
||||
enyo.kind({
|
||||
name: "DeleteBuddyService",
|
||||
kind: "enyo.Component",
|
||||
published: {
|
||||
buddy: undefined
|
||||
},
|
||||
events: {
|
||||
onDeleteThread: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{name: "personFinder", method: "get", onSuccess: "gotPerson"},
|
||||
{name: "contactFinder", method: "get", onSuccess: "gotContacts"},
|
||||
{name: "threadFinder", dbKind: enyo.messaging.thread.dbKind, method: "find", onSuccess: "gotThreads"}
|
||||
]},
|
||||
{name: "deleteService", kind: "DeleteThreadService"}
|
||||
],
|
||||
deleteBuddy: function() {
|
||||
if (this.buddy.person && this.buddy.person.contactIds) {
|
||||
// get all the contact records that are linked for this person
|
||||
this.$.contactFinder.call({
|
||||
ids: this.buddy.person.contactIds
|
||||
});
|
||||
} else {
|
||||
if (this.buddy.personId) {
|
||||
this.$.personFinder.call({
|
||||
ids: [this.buddy.personId]
|
||||
});
|
||||
} else {
|
||||
enyo.error("Can't delete buddy", this.buddy._id, "with no personId!");
|
||||
}
|
||||
}
|
||||
},
|
||||
gotPerson: function(inSender, inResponse) {
|
||||
if (inResponse.results.length > 0) {
|
||||
var person = inResponse.results[0];
|
||||
// get all the contact records that are linked for this person
|
||||
this.$.contactFinder.call({
|
||||
ids: person.contactIds
|
||||
});
|
||||
} else {
|
||||
enyo.error("Can't delete buddy", this.buddy._id, "with no person record!");
|
||||
}
|
||||
},
|
||||
gotContacts: function(inSender, inResponse) {
|
||||
var contacts = inResponse.results;
|
||||
contacts.forEach(this.addDeleteBuddy.bind(this));
|
||||
this.deleteBuddyThreads();
|
||||
},
|
||||
addDeleteBuddy: function(contact) {
|
||||
if (contact.imBuddy) {
|
||||
new DeleteBuddyCommand().deleteContact(contact, enyo.application.accountService);
|
||||
}
|
||||
},
|
||||
deleteBuddyThreads: function() {
|
||||
this.$.threadFinder.call({
|
||||
query: {
|
||||
where: [{
|
||||
prop: "personId",
|
||||
op: "=",
|
||||
val: this.buddy.personId
|
||||
}],
|
||||
select: [
|
||||
"_id"
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
gotThreads: function(inSender, inResponse){
|
||||
var results = inResponse.results;
|
||||
// set the thread id to each deleted buddy
|
||||
for (i = 0; i < results.length; i++) {
|
||||
var thread = results[i];
|
||||
this.deleteThread(thread);
|
||||
}
|
||||
},
|
||||
deleteThread: function(thread) {
|
||||
this.$.deleteService.setId(thread._id);
|
||||
this.$.deleteService.deleteThread();
|
||||
this.doDeleteThread(thread);
|
||||
}
|
||||
});
|
||||
|
||||
enyo.kind({
|
||||
name: "DeleteBuddyCommand",
|
||||
kind: "enyo.Component",
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{name: "accountFinder", method: "get", onSuccess: "gotAccount"},
|
||||
{name: "deleteCommand", dbKind: "com.palm.imcommand:1", method: "put"}
|
||||
]}
|
||||
],
|
||||
deleteContact: function(contact, accountService) {
|
||||
this.contact = contact;
|
||||
this.accountService = accountService;
|
||||
|
||||
// get IM account so that we can get the value for 'fromUsername'
|
||||
this.$.accountFinder.call({
|
||||
ids: [this.contact.accountId]
|
||||
});
|
||||
},
|
||||
gotAccount: function(inSender, inResponse) {
|
||||
var account = inResponse.results[0];
|
||||
var ims = this.contact.ims;
|
||||
|
||||
for (var i = 0; ims && i < ims.length; i++) {
|
||||
this.doDeleteCommand(ims[i].value, ims[i].type, account.username);
|
||||
}
|
||||
},
|
||||
doDeleteCommand: function(targetUsername, serviceName, fromUsername) {
|
||||
enyo.log("Creating ImCommand to delete buddy: {", targetUsername, ", ", serviceName, ", ", fromUsername, "}");
|
||||
if (!targetUsername || !serviceName || !fromUsername) {
|
||||
enyo.warn("Found invalid parameter to delete a buddy");
|
||||
return;
|
||||
}
|
||||
|
||||
// create IM command to delete the contact
|
||||
this.$.deleteCommand.call({
|
||||
objects: [{
|
||||
_kind: this.accountService.getImCommandKind(serviceName),
|
||||
command: "deleteBuddy",
|
||||
handler: "transport",
|
||||
targetUsername: targetUsername,
|
||||
fromUsername: fromUsername,
|
||||
serviceName: serviceName
|
||||
}]
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,470 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "ComposeView",
|
||||
kind: enyo.VFlexBox,
|
||||
className: "composeview",
|
||||
published: {
|
||||
loginStates:[],
|
||||
params:"",
|
||||
input:""
|
||||
},
|
||||
events: {
|
||||
onOpenConversation: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "ApplicationEvents", onUnload: "windowUnloadHandler", onWindowHidden:"windowHiddenHandler"},
|
||||
{layoutKind: "HFlexLayout", className: "enyo-row", components: [
|
||||
/* Watch keyup because the default action of a key (printing/deleting a character)
|
||||
* is done before keyup, which means the input will have resized.
|
||||
* Watch keypress because pressing & holding a key generates
|
||||
* multiple presses but never a keyup
|
||||
*/
|
||||
{kind: "AddressingPopup", onkeyup: "addressingPopupKeyup", onBeforePopupOpen: "onBeforePopupOpenHandler", flex: 1, addressTypes: ["ims", "phoneNumbers"], components: [
|
||||
{kind: "ImTransportList", onSelect: "chatUsingTransport"}
|
||||
]}
|
||||
]},
|
||||
|
||||
{flex: 1},
|
||||
{className:"footer-shadow"},
|
||||
{kind: "Toolbar", className:"enyo-toolbar-light conversation-bottom", components: [
|
||||
{name: "slidingDrag", slidingHandler: true, kind: "GrabButton" },
|
||||
/* Watch keyup because the default action of a key (printing/deleting a character)
|
||||
* is done before keyup, which means the input will have resized.
|
||||
* Watch keypress because pressing & holding a key generates
|
||||
* multiple presses but never a keyup
|
||||
*/
|
||||
{name: "scroller", kind: "BasicScroller", style: "max-height: 155px;", flex: 1, horizontal: false, autoHorizontal: false, components: [
|
||||
{name: "richText", kind: "RichText", hint: $L("Enter message here..."), richContent: false, onkeyup: "checkInputHeight", alwaysLooksFocused:true, onkeydown: "checkKey", autoEmoticons: true}
|
||||
]}
|
||||
]},
|
||||
{name: "errorDialog", kind: "PopupDialog"},
|
||||
{name: "connectPhoneDialog", kind: "ConnectPhoneDialog", onClose: "openConversationAfterPaired"},
|
||||
{name: "messageServiceWatch", kind: "DbService", dbKind: "com.palm.message:1", method: "find", subscribe: true, resubscribe: true, reCallWatches: true, onSuccess: "gotMessage"},
|
||||
{name: "messageService", kind: "DbService", dbKind: enyo.messaging.message.dbKind, onFailure: "messagesFailure", components: [
|
||||
{name: "messageServiceFind", method: "find", onSuccess: "gotDraftMessages"},
|
||||
{name: "messageServiceDelete", method: "del", onSuccess: "deletedDraftMessages"},
|
||||
{name: "messageServicePut", method: "put", onSuccess: "onInsertMessage"}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
|
||||
if (enyo.application.telephonyWatcher) {
|
||||
enyo.application.telephonyWatcher.register(this, this.connectionUpdated.bind(this));
|
||||
}
|
||||
this.getDraft();
|
||||
},
|
||||
getDraft: function(){
|
||||
this.$.messageServiceFind.call({
|
||||
query: {
|
||||
where: [{
|
||||
prop: "conversations",
|
||||
op: "=",
|
||||
val: "_compose"
|
||||
}, {
|
||||
prop: "folder",
|
||||
op: "=",
|
||||
val: enyo.messaging.message.FOLDERS.DRAFTS
|
||||
}],
|
||||
select: [
|
||||
"contacts",
|
||||
"conversations",
|
||||
"messageText"
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
gotDraftMessages: function(inSender, inResponse){
|
||||
if (inResponse.results[0] && inResponse.results[0].conversations && inResponse.results[0].conversations[0] === "_compose") {
|
||||
this.$.richText.setValue(inResponse.results[0].messageText);
|
||||
var contacts = inResponse.results[0].contacts;
|
||||
if( contacts && contacts.length>0){
|
||||
this.$.addressingPopup.setContacts(contacts);
|
||||
}
|
||||
this.$.messageServiceDelete.call({
|
||||
query: {
|
||||
from: enyo.messaging.message.dbKind,
|
||||
where: [{
|
||||
prop: "conversations",
|
||||
op: "=",
|
||||
val: "_compose"
|
||||
}, {
|
||||
prop: "folder",
|
||||
op: "=",
|
||||
val: enyo.messaging.message.FOLDERS.DRAFTS
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
connectionUpdated: function(connected) {
|
||||
this.phoneConnected = connected;
|
||||
},
|
||||
onBeforePopupOpenHandler: function(){
|
||||
// this.$.addressingPopup.$.popup.validateComponents();
|
||||
this.$.imTransportList.setLoginStates(this.loginStates);
|
||||
this.$.imTransportList.setInput(this.input);
|
||||
},
|
||||
addressingPopupKeyup: function(inSender, inEvent) {
|
||||
if (this.$.imTransportList) {
|
||||
this.$.imTransportList.setInput(inSender.getInputValue());
|
||||
}
|
||||
this.input = inSender.getInputValue();
|
||||
},
|
||||
chatUsingTransport: function (inSender, inDisplayName, inValue, inServiceName){
|
||||
this.$.addressingPopup.addContact({displayName: inDisplayName, value: inValue, serviceName: inServiceName, type: inServiceName});
|
||||
},
|
||||
loginStatesChanged: function(oldLoginStates){
|
||||
if (this.$.imTransportList) {
|
||||
this.$.imTransportList.setLoginStates(this.loginStates);
|
||||
}
|
||||
},
|
||||
paramsChanged: function(inOldParams){
|
||||
enyo.log("ComposeView::paramsChanged::params:",this.params, "inOldParams:",inOldParams);
|
||||
this.prePopulatedRecipients = [];
|
||||
if (this.params !== undefined) {
|
||||
var contactAddress;
|
||||
if (this.params.personId !== undefined) {
|
||||
this.personId = this.params.personId;
|
||||
}
|
||||
if (this.params.ims !== undefined) {
|
||||
for (i = 0; i < this.params.ims.length; i++) {
|
||||
contactAddress = this.params.ims[i];
|
||||
if (contactAddress.value || contactAddress.addr) {
|
||||
this.prePopulatedRecipients.push({
|
||||
value: contactAddress.value || contactAddress.addr,
|
||||
displayName: contactAddress.displayName || contactAddress.value || contactAddress.addr,
|
||||
type: "im",
|
||||
serviceName: contactAddress.serviceName || contactAddress.type
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.params.phoneNumbers !== undefined) {
|
||||
for (i = 0; i < this.params.phoneNumbers.length; i++) {
|
||||
contactAddress = this.params.phoneNumbers[i];
|
||||
if (contactAddress.value || contactAddress.addr) {
|
||||
this.prePopulatedRecipients.push({
|
||||
value: contactAddress.value || contactAddress.addr,
|
||||
displayName: contactAddress.displayName || contactAddress.value || contactAddress.addr,
|
||||
serviceName: contactAddress.serviceName || contactAddress.type || "sms",
|
||||
type: "phone"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// composeRecipients expects the following:
|
||||
// [
|
||||
// {
|
||||
// "address": <phone number or IM>,
|
||||
// "serviceName": <serviceName or domain> // only required for IM addresses
|
||||
// }, ...
|
||||
// ]
|
||||
if(this.params.composeRecipients && this.params.composeRecipients.length) {
|
||||
for (i = 0; i < this.params.composeRecipients.length; i++) {
|
||||
var recipient = this.params.composeRecipients[i];
|
||||
var serviceName;
|
||||
if (recipient.serviceName && (recipient.serviceName).indexOf("type_") === 0){
|
||||
serviceName = recipient.serviceName;
|
||||
}
|
||||
else {
|
||||
serviceName = recipient.serviceName ? "type_"+recipient.serviceName : '';
|
||||
}
|
||||
if (recipient.address) {
|
||||
this.prePopulatedRecipients.push({
|
||||
value: recipient.address,
|
||||
displayName: recipient.address,
|
||||
type: (recipient.serviceName ? 'im' : 'phone'),
|
||||
serviceName: serviceName
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.params.messageText !== undefined) {
|
||||
this.$.richText.setValue(this.params.messageText);
|
||||
setTimeout((function(){
|
||||
this.$.addressingPopup.forceFocus();
|
||||
}).bind(this), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.prePopulatedRecipients.length > 0) {
|
||||
enyo.log("ComposeView::has prePopulatedRecipients");
|
||||
var contacts = [], i;
|
||||
for (i=0; i < this.prePopulatedRecipients.length; i++){
|
||||
contacts.push({displayName:this.prePopulatedRecipients[i].displayName, value: this.prePopulatedRecipients[i].value, serviceName: this.prePopulatedRecipients[i].serviceName});
|
||||
}
|
||||
this.$.addressingPopup.setContacts(contacts);
|
||||
}
|
||||
},
|
||||
considerForSend: function(){
|
||||
var message = this.$.richText.getValue();
|
||||
if (message.length > 0) {//todo: check max length?
|
||||
var contacts = this.$.addressingPopup.getContacts();
|
||||
enyo.log("ComposeView::considerForSend::contacts:",contacts);
|
||||
var recipientsArray = [];
|
||||
if (contacts && contacts.length > 0) {
|
||||
recipientsArray = contacts;
|
||||
if (recipientsArray.length > 0) {
|
||||
var phoneRecipients = [];
|
||||
var imRecipients = [];
|
||||
var lastService = null;
|
||||
var isGroupChatCandidate = recipientsArray.length > 1;
|
||||
// separate the different types of recipients
|
||||
for (var x = 0; x < recipientsArray.length; x++) {
|
||||
var recipient = recipientsArray[x];
|
||||
if (recipient !== undefined) {
|
||||
//this.pushReminder(recipient);
|
||||
|
||||
var serviceName;
|
||||
if(!recipient.serviceName && recipient.type){
|
||||
serviceName = recipient.type;
|
||||
}
|
||||
else{
|
||||
serviceName = recipient.serviceName;
|
||||
}
|
||||
if (enyo.messaging.utils.isTextMessage(serviceName)) {
|
||||
phoneRecipients.push(recipient);
|
||||
isGroupChatCandidate = false;
|
||||
}
|
||||
else {
|
||||
// make sure that the serviceName is supported
|
||||
enyo.log("ComposeView::ConsiderForSend::recipient.serviceName:", serviceName);
|
||||
var account = enyo.application.accountService.getAccount(serviceName);
|
||||
|
||||
if (!account) {
|
||||
var errorMessage = "";
|
||||
//errorMessage = $L("You are attempting to send to an IM type that is not supported. Please choose a different address.");
|
||||
errorMessage = $L("You are attempting to send to an IM type for which you do not have an account to send from.");
|
||||
this.$.errorDialog.openAtCenter();
|
||||
this.$.errorDialog.setTitle($L("Unable to send"));
|
||||
this.$.errorDialog.setMessage(errorMessage);
|
||||
this.$.errorDialog.setCancelButtonCaption($L("OK"));
|
||||
this.$.errorDialog.hideAcceptButton();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (lastService === null) {
|
||||
lastService = serviceName;
|
||||
}
|
||||
else
|
||||
if (lastService !== serviceName) {
|
||||
isGroupChatCandidate = false;
|
||||
}
|
||||
}
|
||||
if(!recipient.serviceName && recipient.type){ //gtalk has type set, but not serviceName
|
||||
recipient.serviceName = recipient.type;
|
||||
}
|
||||
imRecipients.push(recipient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send to phone recipients
|
||||
if (phoneRecipients.length > 0) {
|
||||
this.sendMessage(phoneRecipients, message);
|
||||
}
|
||||
|
||||
if (imRecipients.length > 0) {
|
||||
// send to IM recipients if not starting a group chat, don't segment the data for IM
|
||||
// we start a group chat if all recipients are IM recipients of the same service, and that
|
||||
// service supports group chat.
|
||||
if (isGroupChatCandidate && false) {
|
||||
/* this.supportsGroupChat(lastService)) {
|
||||
place holder for group logic later
|
||||
|
||||
}*/
|
||||
}
|
||||
else {
|
||||
this.sendMessage(imRecipients, message);
|
||||
this.sendMessageCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {//no recipient from addressingPopup.contacts
|
||||
enyo.error("ComposeView::considerForSend:recipientsArray is empty");
|
||||
}
|
||||
}
|
||||
else{
|
||||
enyo.warn("ComposeView::considerForSend:: addressingPopup.getContacts() return 0 contacts, don't do anything.");
|
||||
}
|
||||
}
|
||||
else{
|
||||
enyo.warn("ComposeView::considerForSend:: messagingText is empty, don't do anything.");
|
||||
}
|
||||
},
|
||||
|
||||
sendMessage: function(recipientsArray, message) {
|
||||
enyo.log("ComposeView::sendMessage: no. recip:", recipientsArray.length, " message:", message);
|
||||
|
||||
var kind;
|
||||
for( var i=0; i<recipientsArray.length; i++ ){
|
||||
kind = undefined; // ensure this starts out undefined each iteration
|
||||
var recipient = recipientsArray[i];
|
||||
var currentTimestamp = Date.now();
|
||||
if(recipient.displayName !== undefined) {
|
||||
recipient.displayName = recipient.displayName;
|
||||
}
|
||||
if(!recipient.serviceName){
|
||||
recipient.type = "phone";
|
||||
}
|
||||
else if(enyo.messaging.utils.isTextMessage(recipient.serviceName)){
|
||||
recipient.type = "phone";
|
||||
}
|
||||
else{
|
||||
recipient.type = "im";
|
||||
}
|
||||
var params = {
|
||||
folder: enyo.messaging.message.FOLDERS.OUTBOX,
|
||||
status: "pending",
|
||||
to: [{
|
||||
"addr": enyo.messaging.utils.cleanPhoneNumber(recipient.value, recipient.type),
|
||||
"name": recipient.displayName
|
||||
}],
|
||||
timestamp: currentTimestamp,
|
||||
flags: {
|
||||
visible: true,
|
||||
deliveryReport: false//this.Messaging.messagingPrefs.getUseDeliveryReceipts() && enyo.messaging.utils.isTextMessage(recipient.serviceName)
|
||||
},
|
||||
localTimestamp: currentTimestamp,
|
||||
serviceName: recipient.serviceName,
|
||||
messageText: message
|
||||
};
|
||||
|
||||
if (recipient.type === "im") {
|
||||
var dbkinds = enyo.application.accountService.getDbKinds(recipient.serviceName);
|
||||
if (dbkinds) {
|
||||
kind = dbkinds.immessage;
|
||||
}
|
||||
else {
|
||||
enyo.error("No dbkinds specified for service ", recipient.serviceName);
|
||||
}
|
||||
|
||||
|
||||
//NOTE: this is a dumb attempt at getting an account. It should really figure out which account
|
||||
// is associated with the buddy (recipient.value)
|
||||
var account = enyo.application.accountService.getAccount(recipient.serviceName);
|
||||
if (account) {
|
||||
enyo.error("ComposeView::sendMessage::Recipient is missing sender address. Guessing that account is ok:", account.username);
|
||||
params.from = {
|
||||
addr: account.username
|
||||
};
|
||||
params.username = account.username;
|
||||
}
|
||||
else {
|
||||
enyo.error("ComposeView::sendMessage::Recipient ",recipient.value, " is missing sender address and there is no account for serviceName " , recipient.serviceName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
kind = "com.palm.smsmessage:1";
|
||||
params.serviceName = "sms";
|
||||
}
|
||||
|
||||
if(kind !== undefined) {
|
||||
this.sendMessageHelper(params, kind);
|
||||
// MessagingUtils.checkAirplaneMode(params);
|
||||
} else {
|
||||
enyo.error("ComposeView::sendMessage::Warning! Unsupported recipient: ", recipient);
|
||||
}
|
||||
}
|
||||
},
|
||||
sendMessageHelper: function(params, kind){
|
||||
params._kind = kind;
|
||||
params.folder = "outbox";
|
||||
params.localTimestamp = Date.now();
|
||||
|
||||
this.$.messageServicePut.call({objects: [params]});
|
||||
this.$.richText.setValue("");
|
||||
this.prePopulatedRecipients = [];
|
||||
this.$.addressingPopup.setContacts(null);
|
||||
|
||||
if (kind === enyo.messaging.message.SMS.dbKind && !this.phoneConnected) {
|
||||
// this call is needed for devices that lack of SMS capability
|
||||
// phone is not connected, so prompt the user to connect to the phone
|
||||
this.$.connectPhoneDialog.openAtCenter();
|
||||
}
|
||||
},
|
||||
onInsertMessage: function(inSender, inResponse){
|
||||
if (inResponse.returnValue && inResponse.results && inResponse.results.length > 0) {
|
||||
var whereClause = [{"prop":"_id","op":"=","val":inResponse.results[0].id}];
|
||||
this.$.messageServiceWatch.cancel();
|
||||
this.$.messageServiceWatch.call({
|
||||
query: {
|
||||
where: whereClause
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
gotMessage: function(inSender, inResponse){
|
||||
if (inResponse.returnValue && inResponse.results && inResponse.results.length > 0 && inResponse.results[0].folder !== enyo.messaging.message.FOLDERS.DRAFTS) {
|
||||
this.message = inResponse.results[0];
|
||||
if (this.message.conversations && this.message.conversations.length) {
|
||||
this.$.messageServiceWatch.cancel();
|
||||
|
||||
if (this.message._kind !== enyo.messaging.message.SMS.dbKind || this.phoneConnected) {
|
||||
//enyo.log("************ opening conversation: ", this.message.conversations[0]);
|
||||
this.doOpenConversation(this.message.conversations[0]);
|
||||
this.message = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
openConversationAfterPaired: function() {
|
||||
if (this.message && this.message.conversations && this.message.conversations.length > 0) {
|
||||
this.doOpenConversation(this.message.conversations[0]);
|
||||
this.message = undefined;
|
||||
}
|
||||
},
|
||||
checkKey: function(inSender, inEvent) {
|
||||
var messageText;
|
||||
|
||||
// Pressing "enter" should send a message and clear the input
|
||||
if (inEvent.keyCode === 13) {
|
||||
inEvent.preventDefault();
|
||||
messageText = this.$.richText.getValue();
|
||||
|
||||
// Only send non-empty messages
|
||||
if (messageText) {
|
||||
this.considerForSend();
|
||||
}
|
||||
}
|
||||
},
|
||||
windowHiddenHandler: function(){
|
||||
this.$.addressingPopup.close();
|
||||
if (this.$.richText.hasFocus()) {
|
||||
this.$.richText.forceBlur();
|
||||
}
|
||||
},
|
||||
saveDraft: function(){
|
||||
var message = this.$.richText.getValue();
|
||||
if (message.length > 0) {
|
||||
var contacts = this.$.addressingPopup.getContacts();
|
||||
|
||||
var params = {
|
||||
contacts: contacts,
|
||||
messageText: message,
|
||||
attachment: this.attachment,
|
||||
folder: enyo.messaging.message.FOLDERS.DRAFTS,
|
||||
flags: { visible: "false" },
|
||||
_kind: enyo.messaging.message.dbKind,
|
||||
conversations: ["_compose"],
|
||||
localTimestamp: Date.now()
|
||||
};
|
||||
|
||||
this.$.messageServicePut.call({objects: [params]});
|
||||
}
|
||||
},
|
||||
messagesFailure: function(inSender, inResponse){
|
||||
enyo.error("--------ComposeView::messagesFailure::inResponse:", inResponse);
|
||||
},
|
||||
windowUnloadHandler: function(){
|
||||
if (enyo.application.telephonyWatcher) {
|
||||
enyo.application.telephonyWatcher.unregister();
|
||||
}
|
||||
this.saveDraft();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
var msg_cpd_windowHeight = "220px";
|
||||
if (enyo.g11n.currentLocale().language == "fr") msg_cpd_windowHeight = "250px";
|
||||
if (enyo.g11n.currentLocale().language == "de") msg_cpd_windowHeight = "270px";
|
||||
|
||||
enyo.kind({
|
||||
name: "ConnectPhoneDialog",
|
||||
kind: "ModalDialog",
|
||||
caption: $L("Message Can't Be Sent"),
|
||||
height: msg_cpd_windowHeight,
|
||||
contentClassName: "enyo-paragraph",
|
||||
components: [
|
||||
{kind: "ApplicationEvents", onWindowHidden: "close", onUnload: "close", onWindowActivated: "windowActivatedHandler"},
|
||||
{name: "message", content: $L("A phone must be connected to this device via Bluetooth in order to send a text message.")},
|
||||
{name: "buttonOrientation", layoutKind: "VFlexLayout", components: [
|
||||
{name: "connectButton", kind: "Button", flex:1, caption: $L("Connect Phone"), className:"enyo-button-dark", onclick: "connectButtonClick"},
|
||||
{name: "closeButton", kind: "Button", flex:1, caption: $L("OK"), onclick: "close"}
|
||||
]},
|
||||
{name: "launchApp", kind: "Launcher", app: "com.palm.app.bluetooth"}
|
||||
],
|
||||
renderOpen: function() {
|
||||
this.inherited(arguments);
|
||||
if (enyo.application.telephonyWatcher) {
|
||||
enyo.application.telephonyWatcher.register(this, this.connectionUpdated.bind(this));
|
||||
}
|
||||
},
|
||||
connectionUpdated: function(connected) {
|
||||
this.connected = connected;
|
||||
},
|
||||
connectButtonClick: function() {
|
||||
this.$.launchApp.launch();
|
||||
},
|
||||
windowActivatedHandler: function() {
|
||||
if (this.isOpen && this.connected) {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
this.inherited(arguments);
|
||||
if (this.isOpen && enyo.application.telephonyWatcher) {
|
||||
enyo.application.telephonyWatcher.unregister(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,182 @@
|
||||
enyo.kind({
|
||||
name: "ImTransportList",
|
||||
kind: enyo.Control,
|
||||
published: {
|
||||
loginStates: [],
|
||||
input: ""
|
||||
},
|
||||
shortcode: "",
|
||||
events: {
|
||||
onSelect: ""
|
||||
},
|
||||
chrome: [
|
||||
// {kind: "Divider", caption: $L("SHORT CODE")},
|
||||
// {name: "sms", kind: enyo.Item, tapHighlight: true, align: "center", layoutKind: "HFlexLayout", onclick: "itemClick"},
|
||||
{name: "imDivider", kind: "Divider", caption: $L("IM USING:")},
|
||||
{name: "transports", kind: enyo.Item, tapHighlight: true, align: "center", layoutKind: "HFlexLayout"}
|
||||
],
|
||||
create: function(){
|
||||
this.inherited(arguments);
|
||||
// this.$.divider.hide();
|
||||
// this.$.sms.hide();
|
||||
},
|
||||
inputChanged: function(){
|
||||
if (this.input.length > 0) {
|
||||
/* this.shortcode = this.toPhonepadNumber(this.input);
|
||||
if (this.shortcode) {
|
||||
this.$.sms.setContent(this.input + " (" + this.shortcode + ")");
|
||||
this.$.divider.show();
|
||||
this.$.sms.show();
|
||||
} else {
|
||||
this.$.divider.hide();
|
||||
this.$.sms.hide();
|
||||
}*/
|
||||
var template = new enyo.g11n.Template($L("IM with \"#{input}\" using:"));
|
||||
var userinput = template.evaluate({input: this.input});
|
||||
this.$.imDivider.setCaption(userinput.toUpperCase());
|
||||
|
||||
if (this.loginStates.length > 0) {
|
||||
if (this.imItems === undefined) {
|
||||
this.loginStatesChanged();
|
||||
} else if(this.imItems.length>0) {
|
||||
this.$.imDivider.show();
|
||||
this.$.transports.show();
|
||||
} else {
|
||||
this.$.imDivider.hide();
|
||||
this.$.transports.hide();
|
||||
}
|
||||
} else {
|
||||
this.$.imDivider.hide();
|
||||
this.$.transports.hide();
|
||||
}
|
||||
} else {
|
||||
// this.$.divider.hide();
|
||||
// this.$.sms.hide();
|
||||
this.$.imDivider.hide();
|
||||
this.$.transports.hide();
|
||||
}
|
||||
},
|
||||
loginStatesChanged: function(){
|
||||
if (this.loginStates.length > 0 && this.input.length > 0) {
|
||||
var state, icon;
|
||||
var items = [];
|
||||
var serviceNameHash = {};
|
||||
for (var i = 0; i < this.loginStates.length; i++) {
|
||||
state = this.loginStates[i];
|
||||
if(state.state === "online" && enyo.application.accountService.getChatWithNonBuddies(state.serviceName) !== false){
|
||||
icon = enyo.application.accountService.getIcons(state.serviceName);
|
||||
if (icon && icon.splitter && !serviceNameHash[state.serviceName]) {
|
||||
serviceNameHash[state.serviceName] = state.serviceName;
|
||||
items.push({name: state.serviceName, kind: "Image", src: icon.splitter, style: "display: block", onclick: "itemClick"});
|
||||
}
|
||||
}
|
||||
}
|
||||
this.imItems = items;
|
||||
if (items.length > 0) {
|
||||
this.$.transports.destroyControls();
|
||||
this.$.transports.createComponents(items, {owner: this});
|
||||
this.$.transports.components = items;
|
||||
this.render();
|
||||
|
||||
this.$.imDivider.show();
|
||||
this.$.transports.show();
|
||||
} else {
|
||||
this.$.imDivider.hide();
|
||||
this.$.transports.hide();
|
||||
}
|
||||
} else {
|
||||
this.$.imDivider.hide();
|
||||
this.$.transports.hide();
|
||||
}
|
||||
},
|
||||
itemClick: function(inSender){
|
||||
if (inSender.name === "sms") {
|
||||
this.doSelect(this.input+"("+this.shortcode+")", this.shortcode, inSender.name);
|
||||
} else {
|
||||
this.doSelect(this.input, this.input, inSender.name);
|
||||
}
|
||||
this.input = "";
|
||||
this.inputChanged();
|
||||
},
|
||||
toPhonepadNumber: function(str){
|
||||
var smsAccount = enyo.application.accountService.getAccount("sms");
|
||||
if (!smsAccount){
|
||||
return null;
|
||||
}
|
||||
return this.getNumberString(str);
|
||||
},
|
||||
|
||||
/***********************************
|
||||
* Functions below are unit tested *
|
||||
***********************************/
|
||||
getNumberString: function(str){
|
||||
var is = true;
|
||||
var numChar = "-1";
|
||||
var numberStr = "";
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
numChar = this.translateToPhonepadDigit(str.charAt(i));
|
||||
if (numChar == "-1") {
|
||||
is = false;
|
||||
break;
|
||||
} else {
|
||||
numberStr += numChar;
|
||||
}
|
||||
}
|
||||
|
||||
if (is) {
|
||||
return numberStr;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
translateToPhonepadDigit: function(letter){
|
||||
var number = -1;
|
||||
letter = letter.toLowerCase();
|
||||
switch (letter) {
|
||||
case "a":
|
||||
case "b":
|
||||
case "c":
|
||||
number = 2;
|
||||
break;
|
||||
case "d":
|
||||
case "e":
|
||||
case "f":
|
||||
number = 3;
|
||||
break;
|
||||
case "g":
|
||||
case "h":
|
||||
case "i":
|
||||
number = 4;
|
||||
break;
|
||||
case "j":
|
||||
case "k":
|
||||
case "l":
|
||||
number = 5;
|
||||
break;
|
||||
case "m":
|
||||
case "n":
|
||||
case "o":
|
||||
number = 6;
|
||||
break;
|
||||
case "p":
|
||||
case "q":
|
||||
case "r":
|
||||
case "s":
|
||||
number = 7;
|
||||
break;
|
||||
case "t":
|
||||
case "u":
|
||||
case "v":
|
||||
number = 8;
|
||||
break;
|
||||
case "w":
|
||||
case "x":
|
||||
case "y":
|
||||
case "z":
|
||||
number = 9;
|
||||
break;
|
||||
}
|
||||
return number;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
enyo.kind({
|
||||
name: "BlockPersonService",
|
||||
kind: "enyo.Component",
|
||||
published: {
|
||||
thread: null
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", components:[
|
||||
{name: "messageFinder", dbKind: "com.palm.message:1", method: "find", onSuccess: "gotMessage"},
|
||||
{name: "dbAdder", dbKind: "com.palm.imcommand:1", method: "put"}
|
||||
]}
|
||||
],
|
||||
blockPerson: function() {
|
||||
this.$.messageFinder.call({
|
||||
query: {
|
||||
where: [{
|
||||
prop: "conversations",
|
||||
op: "=",
|
||||
val: this.thread._id
|
||||
}],
|
||||
select: [
|
||||
"username"
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
gotMessage: function(inSender, inResponse) {
|
||||
this.addBlockBuddy(inResponse.results[0]);
|
||||
},
|
||||
addBlockBuddy: function(data) {
|
||||
enyo.log("BlockPersonService::addBlockBuddy::thread: ",this.thread);
|
||||
this.$.dbAdder.call({
|
||||
objects: [{
|
||||
_kind: enyo.application.accountService.getImCommandKind(this.thread.replyService),
|
||||
command: "blockBuddy",
|
||||
params: { block: true },
|
||||
handler: "transport",
|
||||
targetUsername: this.thread.replyAddress,
|
||||
fromUsername: data.username,
|
||||
serviceName: this.thread.replyService
|
||||
}]
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,145 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "ConversationItem",
|
||||
kind: "enyo.SwipeableItem",
|
||||
confirmCaption: $L("Delete"),
|
||||
layoutKind: "enyo.HFlexLayout",
|
||||
align: "start",
|
||||
published: {
|
||||
message: ""
|
||||
},
|
||||
events: {
|
||||
onError: ""
|
||||
},
|
||||
components: [
|
||||
{name: "imageContainer", className:"conversationContactImage", components: [
|
||||
{className: "contact-image-border"},
|
||||
{name: "contactImage", kind: "Image", className: "contact-image"}
|
||||
]},
|
||||
{name: "messageContainer", flex: 1, components:[
|
||||
{name: "message", components:[
|
||||
{name: "messageText", allowHtml:true},
|
||||
{layoutKind: "HLayout", components:[
|
||||
{name: "messageTime", className: "message-time"},
|
||||
{name: "errorIcon", kind: "Image", src: "images/header-warning-icon.png", className:"erroricon", onclick: "showError"}
|
||||
]} ,
|
||||
{name: "invitationButtons", layoutKind: "HLayout", className:"accept-decline-box", components: [
|
||||
{name: "declineButton", kind: "IconButton", className: "enyo-button-negative", icon: "images/icon-decline.png", onclick: "declinedBuddy"},
|
||||
{name: "acceptButton", kind: "IconButton", className: "enyo-button-affirmative", icon: "images/icon-accept.png", onclick: "acceptedBuddy"}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
{name: "inviteService", kind: "InviteResponseService"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.messageChanged();
|
||||
this.addClass("chat-balloon");
|
||||
},
|
||||
messageChanged: function() {
|
||||
this.updateMessageText(this.message, this.message.folder !== enyo.messaging.message.FOLDERS.OUTBOX && this.message.folder !== enyo.messaging.message.FOLDERS.INBOX);
|
||||
this.updateContactImage(this.message.personImage);
|
||||
this.updateSentReceived(this.message.folder);
|
||||
this.updateTime(this.message.localTimestamp);
|
||||
this.updateMessageStatus(this.message.status, this.message.errorCategory);
|
||||
this.updateInvite(this.message);
|
||||
this.updatePriority(this.message);
|
||||
},
|
||||
updatePriority: function(message){
|
||||
// 0 = normal priority
|
||||
// 1 = interactive
|
||||
// 2 = urgent
|
||||
// 3 = emergency
|
||||
// 4 = low priority
|
||||
if (message.priority && (message.priority === 2 || message.priority === 3)) {
|
||||
this.$.message.addClass("high-priority");
|
||||
}
|
||||
},
|
||||
updateMessageStatus: function(inStatus, errorCategory){
|
||||
if (inStatus !== "successful") {
|
||||
this.$.message.setClassName("enyo-item chat-balloon-error");
|
||||
}
|
||||
if(errorCategory && (inStatus === enyo.messaging.message.MESSAGE_STATUS.FAILED || inStatus === enyo.messaging.message.MESSAGE_STATUS.UNDELIVERABLE)){
|
||||
this.$.errorIcon.canGenerate = true;
|
||||
this.$.errorIcon.show();
|
||||
}
|
||||
else{
|
||||
this.$.errorIcon.canGenerate = false;
|
||||
}
|
||||
},
|
||||
updateMessageText: function(inMessage, skipTextIndexer) {
|
||||
var inText = inMessage.messageText || "";
|
||||
if (enyo.messaging.message.isMMSMessage(inMessage)) {
|
||||
inText = enyo.messaging.message.getMMSDisplayMessage();
|
||||
} else if (inMessage.folder === enyo.messaging.message.FOLDERS.INBOX) {
|
||||
inText = inText.replace(/\r|\n|\\r|\\n/g, "<br>");
|
||||
} else if (inMessage.folder === enyo.messaging.message.FOLDERS.OUTBOX) {
|
||||
// outgoing message needs to be sanitized since the incoming ones
|
||||
// are already sanitized before they are written into database.
|
||||
inText = enyo.string.escapeHtml(inText);
|
||||
}
|
||||
|
||||
if (!skipTextIndexer) {
|
||||
inText = enyo.string.runTextIndexer(inText);
|
||||
}
|
||||
this.$.messageText.setContent(inText);
|
||||
},
|
||||
updateContactImage: function(personImage) {
|
||||
this.$.contactImage.setAttribute("src", personImage);
|
||||
},
|
||||
updateTime: function(localTimestamp) {
|
||||
this.$.messageTime.setContent(this.formatTime(new Date(localTimestamp)));
|
||||
},
|
||||
formatTime: function(date){
|
||||
if (!date) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
return Utils.formatShortTime(date);
|
||||
},
|
||||
updateSentReceived: function(inFolder) {
|
||||
if (inFolder === enyo.messaging.message.FOLDERS.INBOX) {
|
||||
this.$.message.setClassName("enyo-item chat-balloon-received");
|
||||
this.$.imageContainer.canGenerate = true;
|
||||
this.$.imageContainer.show();
|
||||
} else if(inFolder === enyo.messaging.message.FOLDERS.OUTBOX){
|
||||
this.$.message.setClassName("enyo-item chat-balloon-sent");
|
||||
this.$.imageContainer.canGenerate = false;
|
||||
} else {
|
||||
this.$.message.setClassName("enyo-item chat-balloon-system");
|
||||
this.$.imageContainer.canGenerate = false;
|
||||
}
|
||||
},
|
||||
updateInvite: function(message) {
|
||||
var showInvite = this.message._kind === "com.palm.iminvitation:1" && this.message.accepted === "pending";
|
||||
|
||||
// update invite buttons
|
||||
this.updateInviteButtons(showInvite);
|
||||
// update style
|
||||
if (showInvite) {
|
||||
this.$.message.setClassName("enyo-item chat-balloon-error");
|
||||
this.$.imageContainer.canGenerate = false;
|
||||
}
|
||||
},
|
||||
updateInviteButtons: function(show) {
|
||||
this.$.invitationButtons.canGenerate = show;
|
||||
this.$.invitationButtons.setShowing (show);
|
||||
},
|
||||
showError: function(inSender, inEvent){
|
||||
this.doError(this.message);
|
||||
return true;
|
||||
},
|
||||
acceptedBuddy: function(inSender, inEvent) {
|
||||
this.setResponseInvitation(true);
|
||||
return true;
|
||||
},
|
||||
declinedBuddy: function(inSender, inEvent) {
|
||||
this.setResponseInvitation(false);
|
||||
return true;
|
||||
},
|
||||
setResponseInvitation: function(accepted) {
|
||||
this.$.inviteService.responseToInvite(this.message, accepted);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
enyo.kind({
|
||||
name: "ConversationService",
|
||||
kind: enyo.Service,
|
||||
requestKind: "ConversationRequest",
|
||||
events: {
|
||||
onWatch: ""
|
||||
}});
|
||||
|
||||
enyo.kind({
|
||||
name: "ConversationRequest",
|
||||
kind: enyo.Request,
|
||||
events: {
|
||||
onWatch: "doWatch"
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{name: "messages", dbKind: "com.palm.message:1", method: "find", onSuccess: "gotMessages", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/}
|
||||
]}/*,
|
||||
{name: "mockMessages", kind: "MockDb", dbKind: "com.palm.message:1", method: "find", onSuccess: "gotMessages", onFailure: "fail", onWatch: "doWatch"}*/
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
},
|
||||
/*
|
||||
initComponents: function() {
|
||||
this.inherited(arguments);
|
||||
if (!window.PalmSystem) {
|
||||
this.$.messages = this.$.mockMessages;
|
||||
}
|
||||
},
|
||||
*/
|
||||
finish: function() {
|
||||
// don't destroy automatically, so we can see watches
|
||||
},
|
||||
call: function() {
|
||||
this.$.messages.cancel();
|
||||
this.$.messages.call(this.params);
|
||||
},
|
||||
reply: function() {
|
||||
this.receive(this.data);
|
||||
},
|
||||
fail: function(inSender, inResponse) {
|
||||
enyo.log(inResponse);
|
||||
this.reply();
|
||||
},
|
||||
gotMessages: function(inSender, inResponse) {
|
||||
this.data = inResponse;
|
||||
//update list as soon as got data
|
||||
this.reply();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
enyo.kind({
|
||||
name: "InviteResponseService",
|
||||
kind: "enyo.Component",
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "faile", components: [
|
||||
{dbKind: "com.palm.imcommand:1", components: [
|
||||
{name: "commandMerger", method: "merge", onSuccess: "mergedCommand"}
|
||||
]},
|
||||
{dbKind: "com.palm.iminvitation:1", components: [
|
||||
{name: "inviteMerger", method: "merge", onSuccess: "mergedInvite"}
|
||||
]},
|
||||
{dbKind: "com.palm.immessage:1", components: [
|
||||
{name: "responseAdder", method: "put"}
|
||||
]}
|
||||
]}
|
||||
],
|
||||
responseToInvite: function(invite, accepted) {
|
||||
this.invite = invite;
|
||||
this.accepted = accepted;
|
||||
|
||||
// update IMCommand so that transport will process the accepted/rejected buddy invitation
|
||||
this.$.commandMerger.call({
|
||||
objects: [{
|
||||
_id: this.invite.commandId,
|
||||
params: { "accept": this.accepted },
|
||||
handler: "transport"
|
||||
}]
|
||||
});
|
||||
},
|
||||
mergedCommand: function() {
|
||||
// update ImInvitation so that messaging app can update the conversation item
|
||||
this.$.inviteMerger.call({
|
||||
objects: [{
|
||||
_id: this.invite._id,
|
||||
accepted: this.accepted ? "accepted" : "rejected"
|
||||
}]
|
||||
});
|
||||
},
|
||||
mergedInvite: function(inSender, inResponse) {
|
||||
var messageText;
|
||||
var from = this.invite.from;
|
||||
if (!from.name) {
|
||||
from.name = "";
|
||||
} else {
|
||||
from.name = from.name + " ";;
|
||||
}
|
||||
var template;
|
||||
if (this.accepted) {
|
||||
template = new enyo.g11n.Template($L("Accepted Invitation from #{name}#{address}"));
|
||||
} else {
|
||||
template = new enyo.g11n.Template($L("Rejected Invitation from #{name}#{address}"));
|
||||
}
|
||||
messageText = template.evaluate({name: from.name, address: from.addr});
|
||||
|
||||
// also create a acceptance/rejection message back to the person
|
||||
this.$.responseAdder.call({
|
||||
objects: [{
|
||||
_kind: "com.palm.immessage:1",
|
||||
folder: "outbox",
|
||||
status: "successful",
|
||||
"messageText": messageText,
|
||||
"serviceName": this.invite.serviceName,
|
||||
"localTimestamp": Date.now(),
|
||||
"from": this.invite.to[0],
|
||||
"to": [this.invite.from],
|
||||
"imType": "system"
|
||||
}]
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
enyo.kind({
|
||||
name: "TransportSelector",
|
||||
kind: enyo.CustomListSelector,
|
||||
events: {
|
||||
onPhoneClick: "",
|
||||
onVideoClick: ""
|
||||
},
|
||||
itemKind: "TransportMenuCheckItem",
|
||||
setItemProps: function(inItem) {
|
||||
this.item.setCaption(inItem.caption);
|
||||
this.item.setPhone(inItem.phone);
|
||||
this.item.setVideo(inItem.video);
|
||||
this.item.setSecondaryLabel(inItem.secondaryLabel);
|
||||
},
|
||||
makePopup: function() {
|
||||
this.popup = this.createComponent({
|
||||
kind: "TransportPopupSelect",
|
||||
onSelect: "popupSelect",
|
||||
onBeforeOpen: "popupBeforeOpen",
|
||||
defaultKind: this.itemKind,
|
||||
onPhoneClick: "doPhoneClick",
|
||||
onVideoClick: "doVideoClick"
|
||||
});
|
||||
},
|
||||
closePopup: function() {
|
||||
this.popup.close();
|
||||
}
|
||||
});
|
||||
enyo.kind({
|
||||
name: "TransportPopupSelect",
|
||||
kind: enyo.PopupSelect,
|
||||
events: {
|
||||
onPhoneClick: "",
|
||||
onVideoClick: ""
|
||||
},
|
||||
itemsChanged: function() {
|
||||
this.selected = null;
|
||||
this.inherited(arguments);
|
||||
},
|
||||
phoneClick: function(inSender) {
|
||||
return this.doPhoneClick(inSender.replyAddress, inSender.serviceName);
|
||||
},
|
||||
videoClick: function(inSender) {
|
||||
return this.doVideoClick(inSender.replyAddress, inSender.serviceName);
|
||||
}
|
||||
});
|
||||
|
||||
enyo.kind({
|
||||
name: "TransportMenuCheckItem",
|
||||
kind: enyo.MenuCheckItem,
|
||||
className: "transport-picker",
|
||||
published: {
|
||||
phone: "",
|
||||
video: "",
|
||||
secondaryLabel:""
|
||||
},
|
||||
events: {
|
||||
onPhoneClick: "phoneClick",
|
||||
onVideoClick: "videoClick"
|
||||
},
|
||||
chrome: [
|
||||
{name: "item", kind: enyo.Item, tapHighlight: true, align: "center", className: "enyo-menuitem",
|
||||
layoutKind: "HFlexLayout", onclick: "itemClick", components: [
|
||||
{name: "phone", kind: "IconButton", onclick: "doPhoneClick", icon: "images/phone-icon.png"/*, className: "transport-picker-dial-icon"*/},
|
||||
{name: "video", kind: "IconButton", onclick: "doVideoClick", icon: "images/video-icon2.png"/*, className: "transport-picker-video-icon"*/},
|
||||
{name: "icon", kind: "Image", className: "enyo-menuitem-icon"},
|
||||
{flex: 1, components: [
|
||||
{name: "caption", flex: 1, className: "enyo-menucheckitem-caption"},
|
||||
{name: "secondaryLabel", flex: 1, className:"transport-picker-phoneLabel"}]},
|
||||
{name: "arrow", kind: enyo.CustomButton, toggling: true, showing: false, className: "enyo-menuitem-arrow"}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.phoneChanged();
|
||||
this.videoChanged();
|
||||
this.secondaryLabelChanged();
|
||||
},
|
||||
setCaptionControl: function() {
|
||||
this.captionControl = this.$.caption;
|
||||
},
|
||||
secondaryLabelChanged: function() {
|
||||
this.$.secondaryLabel.setContent(this.secondaryLabel);
|
||||
if (this.secondaryLabel && this.secondaryLabel.length > 0) {
|
||||
this.$.secondaryLabel.setShowing(true);
|
||||
this.$.secondaryLabel.setContent(this.secondaryLabel.toUpperCase());
|
||||
}
|
||||
else{
|
||||
this.$.secondaryLabel.setShowing(false);
|
||||
}
|
||||
},
|
||||
phoneChanged: function() {
|
||||
this.$.phone.setShowing(!this.hideIcon && this.phone);
|
||||
},
|
||||
videoChanged: function() {
|
||||
this.$.video.setShowing(!this.hideIcon && this.video);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,456 @@
|
||||
/* Copyright 2010 Palm, Inc. All rights reserved. */
|
||||
transportPicker = {
|
||||
_selectedTransport: -1,
|
||||
_transports: [{disabled: true, caption:$L("Send message to...")}],
|
||||
_buddyStatusHash: {},
|
||||
availabilityIcons: [
|
||||
"images/status-available.png",
|
||||
"images/status-available.png",
|
||||
"images/status-away.png",
|
||||
"images/status-offline.png",
|
||||
"images/status-offline.png",
|
||||
"images/status-offline.png",
|
||||
"images/status-offline.png"
|
||||
],
|
||||
getSelectedTransport: function() {
|
||||
enyo.log("transportPicker::getSelectedTransport::this._selectedTransport:", this._selectedTransport);
|
||||
if (this._selectedTransport < 1 || this._selectedTransport >= this._transports.length) {
|
||||
enyo.error("****************************** Invalid transport selection=", this._selectedTransport);
|
||||
//enyo.error("Available options are: %j", this._transports);
|
||||
return {};
|
||||
}
|
||||
|
||||
return this._transports[this._selectedTransport];
|
||||
},
|
||||
|
||||
getTransports: function() {
|
||||
return this._transports;
|
||||
},
|
||||
setTransportsByChatThread: function(chatThread, listSelectorObj) {
|
||||
this._transportSelectorObj = listSelectorObj;
|
||||
this._selectedTransport = -1;
|
||||
var displayName, secondaryLabel;
|
||||
if (!chatThread.replyAddress) {
|
||||
displayName = $L("Unknown");
|
||||
} else if (enyo.messaging.utils.isTextMessage(chatThread.replyService)) {
|
||||
var phonefmt = new enyo.g11n.PhoneFmt({style: "default"});
|
||||
displayName = phonefmt.format(new enyo.g11n.PhoneNumber(chatThread.replyAddress));
|
||||
secondaryLabel = $L("Text"); //assume it is Mobile since the number isn't associated with a contact
|
||||
} else {
|
||||
displayName = chatThread.replyAddress; //enyo.string.escapeHtml(chatThread.replyAddress);
|
||||
}
|
||||
|
||||
var account = enyo.application.accountService.getAccount(chatThread.replyService);
|
||||
|
||||
if (account) {
|
||||
secondaryLabel = account.loc_shortName;
|
||||
}
|
||||
else{
|
||||
enyo.warn("transportPicker::setTransposByChatThread::can't find account based on replyService:", chatThread.replyService);
|
||||
}
|
||||
|
||||
var transportCommand = this.getTransportId(chatThread.replyAddress, chatThread.replyService);
|
||||
|
||||
this._transports = [
|
||||
{disabled: true, caption:$L("Send message to...")},
|
||||
{kind: "Divider", caption: enyo.messaging.utils.isTextMessage(chatThread.replyService)? $L("SMS ACCOUNT"): secondaryLabel},
|
||||
{
|
||||
label: secondaryLabel,
|
||||
caption: displayName, //enyo.string.escapeHtml(displayName),
|
||||
displayName: displayName, //enyo.string.escapeHtml(displayName),
|
||||
value: transportCommand,//displayName,
|
||||
replyAddress: chatThread.replyAddress,
|
||||
normalizedValue: chatThread.normalizedAddress,
|
||||
serviceName: chatThread.replyService,
|
||||
secondaryLabel: secondaryLabel,
|
||||
//availability: enyo.messaging.im.availability.NO_PRESENCE,
|
||||
//className: "status-offline",
|
||||
icon: this.availabilityIcons[enyo.messaging.im.availability.NO_PRESENCE],
|
||||
account: account,
|
||||
id: transportCommand
|
||||
}];
|
||||
|
||||
this._selectedTransport = 2;
|
||||
listSelectorObj.setItems(this._transports);
|
||||
listSelectorObj.setValue(this._transports[this._selectedTransport].id);
|
||||
if (this._transports[this._selectedTransport].label !== undefined) {
|
||||
listSelectorObj.setLabel(this._transports[this._selectedTransport].label);
|
||||
}
|
||||
else{//edge case: accout was sent from is removed.
|
||||
listSelectorObj.setLabel((this._transports[this._selectedTransport].serviceName).slice(5));
|
||||
}
|
||||
},
|
||||
|
||||
setTransportsByPerson: function(headerStatusObj, listSelectorObj, person, chatThread, selectIMTransport, selectIMBuddyStatus ) {
|
||||
this._headerStatusObj = headerStatusObj;
|
||||
this._transportSelectorObj = listSelectorObj;
|
||||
//TODO: if a list of transports already exists and a selection is set, we need to remember that selection
|
||||
//and make sure it is still selected after the update
|
||||
this._transports = [{disabled: true, caption:$L("Send message to...")}];
|
||||
this._selectedTransport = -1;
|
||||
|
||||
var i, addr, transport, headerName;
|
||||
var validAccountTypes = enyo.application.accountService.getMyAccountTypesHash();
|
||||
if(!validAccountTypes){
|
||||
enyo.warn("transportPicker::setTransportsByPerson::can't find validAccountTypes");
|
||||
setTimeout(this.setTransportsByPerson.bind(this,headerStatusObj, listSelectorObj, person, chatThread, selectIMTransport, selectIMBuddyStatus ), 10);
|
||||
return;
|
||||
}
|
||||
|
||||
//phone numbers
|
||||
if (validAccountTypes.sms === undefined) {
|
||||
enyo.warn("TransportPicker skipping phone numbers because IMAccounts.getMyAccountTypesHash doesn't contain an SMS entry.");
|
||||
} else {
|
||||
if (person.phoneNumbers.length > 0) {
|
||||
this._transports.push({
|
||||
kind: "Divider",
|
||||
caption: $L("SMS ACCOUNT")
|
||||
});
|
||||
}
|
||||
for (i = 0; i < person.phoneNumbers.length; ++i) {
|
||||
addr = person.phoneNumbers[i];
|
||||
if (!addr.value) {
|
||||
enyo.error("setTransportsByPerson ignoring person because phone value property is missing");
|
||||
} else {
|
||||
this._transports.push({
|
||||
label: $L("Text"),//enyo.messaging.utils.phoneNumberLabels[addr.type] == undefined ? $L("Text"): enyo.messaging.utils.phoneNumberLabels[addr.type],//ContactsLib.PhoneNumber.Labels.getLabel(addr.type),
|
||||
caption: enyo.messaging.utils.formatAddress(addr.value, addr.type),
|
||||
displayName: enyo.messaging.utils.formatAddress(addr.value, addr.type),
|
||||
value: this.getTransportId(addr.value, "sms"),//addr.value,
|
||||
replyAddress: addr.value,
|
||||
normalizedValue: enyo.messaging.utils.normalizeAddress(addr.value, addr.type),
|
||||
serviceName: "sms", //TODO get this from IMAccounts
|
||||
secondaryLabel: ContactsLib.PhoneNumber.Labels.getLabel(addr.type),
|
||||
//availability: enyo.messaging..Availability.NO_PRESENCE,
|
||||
//className: "status-offline",
|
||||
icon: this.availabilityIcons[enyo.messaging.im.availability.NO_PRESENCE],
|
||||
account: {},//TODO add "account" property
|
||||
id: this.getTransportId(addr.value, "sms")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (person.ims.length > 0 && person.contactIds.length > 0) {
|
||||
//need build IM transport in ConversationList::gotContacts
|
||||
}
|
||||
else {
|
||||
this._headerStatusObj.setClassName("status status-no-presence");
|
||||
this.selectBestTransport(chatThread.replyService, chatThread.replyAddress, chatThread.normalizedAddress, listSelectorObj, selectIMTransport, selectIMBuddyStatus);
|
||||
}
|
||||
},
|
||||
setTransportsByContacts: function(headerStatusObj, listSelectorObj, contacts, chatThread, selectIMTransport, selectIMBuddyStatus/*, allowVideo*/){
|
||||
enyo.log("transportpicker::setTransportsByContacts contacts.length:", contacts.length);
|
||||
//todo: need test out
|
||||
this._headerStatusObj = headerStatusObj;
|
||||
var i, j, contact;
|
||||
var validAccountTypes = enyo.application.accountService.getMyAccountTypesHash();
|
||||
if(!validAccountTypes){
|
||||
enyo.warn("transportpicker::setTransportsByContacts:: can't find validAccountTypes");
|
||||
setTimeout(this.setTransportsByContacts.bind(this,headerStatusObj, listSelectorObj, contacts, chatThread, selectIMTransport, selectIMBuddyStatus/*, allowVideo*/), 10);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < contacts.length; i++) {
|
||||
contact = contacts[i];
|
||||
if(contact.imBuddy){
|
||||
for (j = 0; contact.ims && j < contact.ims.length; j++) {
|
||||
addr = contact.ims[j];
|
||||
if (!addr.value) {
|
||||
enyo.error("transportpicker::setTransportsByContacts ignoring contact because IM value property is missing");
|
||||
}
|
||||
else if (validAccountTypes[contact.accountId] === undefined) {
|
||||
enyo.error("setTransportsByContacts ignoring service=", addr.type," because we don't have an account for it.");
|
||||
}
|
||||
else {
|
||||
var handled = false;
|
||||
var serviceName = addr.type;
|
||||
if (serviceName == this._transports[this._transports.length - 1].serviceName /*&& this._transports[this._transports.length - 1].account.loc_shortName === validAccountTypes[contact.accountId].loc_shortName*/) {
|
||||
//no divider needed for same type
|
||||
this._transports.push({
|
||||
label: validAccountTypes[contact.accountId].loc_shortName,
|
||||
caption: addr.value,//validAccountTypes[contact.accountId].loc_shortName,//addr.value,
|
||||
displayName: addr.value, //enyo.string.escapeHtml(addr.value),
|
||||
value: this.getTransportId(addr.value, serviceName,contact.accountId),//addr.value,
|
||||
replyAddress: addr.value,
|
||||
normalizedValue: enyo.messaging.utils.normalizeAddress(addr.value, addr.type),
|
||||
serviceName: serviceName,
|
||||
phone: serviceName === "type_skype" ? true : undefined,
|
||||
video: serviceName === "type_skype"/* && allowVideo*/ ? true : undefined,
|
||||
//availability: this.getBuddyAvailability(serviceName, addr.value),
|
||||
//className: "status-offline",
|
||||
icon: this.availabilityIcons[enyo.messaging.im.availability.NO_PRESENCE],
|
||||
account: validAccountTypes[contact.accountId],
|
||||
id: this.getTransportId(addr.value, serviceName, contact.accountId)
|
||||
});
|
||||
}
|
||||
else {
|
||||
var k;
|
||||
for (k = 0; k < this._transports.length; k++) {
|
||||
|
||||
if (serviceName === this._transports[k].serviceName /*&& this._transports[this._transports.length - 1].account.loc_shortName === validAccountTypes[contact.accountId].loc_shortName*/) {
|
||||
//insert after this one
|
||||
this._transports.splice(k, 0, {
|
||||
label: validAccountTypes[contact.accountId].loc_shortName,
|
||||
caption: addr.value,//validAccountTypes[contact.accountId].loc_shortName,//addr.value,
|
||||
displayName: addr.value, //enyo.string.escapeHtml(addr.value),
|
||||
value: this.getTransportId(addr.value, serviceName, contact.accountId),//addr.value,
|
||||
replyAddress: addr.value,
|
||||
normalizedValue: enyo.messaging.utils.normalizeAddress(addr.value, addr.type),
|
||||
serviceName: serviceName,
|
||||
phone: serviceName === "type_skype" ? true : undefined,
|
||||
video: serviceName === "type_skype" /*&& allowVideo */? true : undefined,
|
||||
//availability: this.getBuddyAvailability(serviceName, addr.value),
|
||||
//className: "status-offline",
|
||||
icon: this.availabilityIcons[enyo.messaging.im.availability.NO_PRESENCE],
|
||||
account: validAccountTypes[contact.accountId],
|
||||
id: this.getTransportId(addr.value, serviceName, contact.accountId)
|
||||
});
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!handled) { //insert divider and transport
|
||||
this._transports.push({
|
||||
kind: "Divider",
|
||||
caption: validAccountTypes[contact.accountId].loc_shortName
|
||||
});
|
||||
this._transports.push({
|
||||
label: validAccountTypes[contact.accountId].loc_shortName,
|
||||
caption: addr.value,//validAccountTypes[contact.accountId].loc_shortName,//addr.value,
|
||||
displayName: addr.value, //enyo.string.escapeHtml(addr.value),
|
||||
value: this.getTransportId(addr.value, serviceName, contact.accountId),//addr.value,
|
||||
replyAddress: addr.value,
|
||||
normalizedValue: enyo.messaging.utils.normalizeAddress(addr.value, addr.type),
|
||||
serviceName: serviceName,
|
||||
phone: serviceName === "type_skype" ? true : undefined,
|
||||
video: serviceName === "type_skype" /*&& allowVideo */? true : undefined,
|
||||
//availability: this.getBuddyAvailability(serviceName, addr.value),
|
||||
//className: "status-offline",
|
||||
icon: this.availabilityIcons[enyo.messaging.im.availability.NO_PRESENCE],
|
||||
account: validAccountTypes[contact.accountId],
|
||||
id: this.getTransportId(addr.value, serviceName, contact.accountId)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this._transports.length > 1) {
|
||||
//todo: for im only, should not check this._transports.length, instead should be buddy's length, can also move out of transportPicker or in for obj?
|
||||
enyo.warn("transportPicker::setTransportsByContacts::call selectBestTransport");
|
||||
this.selectBestTransport(chatThread.replyService, chatThread.replyAddress, chatThread.normalizedAddress, listSelectorObj, selectIMTransport, selectIMBuddyStatus);
|
||||
}
|
||||
else{
|
||||
this.setTransportsByChatThread(chatThread, listSelectorObj);
|
||||
}
|
||||
},
|
||||
selectTransportById: function(id) {
|
||||
//enyo.log("TransportPickerModel.selectTransportById id=", id);
|
||||
if (this._transports.length > 0) {
|
||||
var transport = undefined;
|
||||
var found = false;
|
||||
var i = 0;
|
||||
while (found !== true && i < this._transports.length) {
|
||||
transport = this._transports[i];
|
||||
if (transport.id === id) {
|
||||
enyo.log("TransportPickerModel.selectTransportById transport found ", transport.id);
|
||||
found = true;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (found === true && this._selectedTransport !== i) {
|
||||
//enyo.log("TransportPickerModel.selectTransportById old=", this._selectedTransport,", new=" ,i);
|
||||
this._selectedTransport = i;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//TODO: selectBestTransport needs a healthy cleanup to work better with bedlam
|
||||
// TODO: this could be further improved to choose an online transport over one that is offline
|
||||
// when no exact matches are found
|
||||
// @param selectIMTransport if set to true, the most online IM address in the transport will be selected
|
||||
selectBestTransport: function(serviceName, chatAddress, normalizedChatAddress, listSelectorObj, selectIMTransport, selectIMBuddyStatus) {
|
||||
enyo.log("transportpicker::selectBestTransport:serviceName:", serviceName, " chatAddress:", chatAddress, " normalizedChatAddress:", normalizedChatAddress, " selectIMTransport:", selectIMTransport);
|
||||
var selectedTransport = -1;
|
||||
var data = this._transports;
|
||||
if (data.length > 0) {
|
||||
var i, transport;
|
||||
var serviceIsSms = enyo.messaging.utils.isTextMessage(serviceName);
|
||||
if (serviceIsSms && serviceName !== "sms") {
|
||||
serviceName = "sms";
|
||||
}
|
||||
|
||||
if (!normalizedChatAddress) {
|
||||
normalizedChatAddress = enyo.messaging.utils.normalizeAddress(chatAddress, serviceName);
|
||||
//enyo.warn("Normalized chat address not supplied, estimating as %s", normalizedChatAddress);
|
||||
}
|
||||
|
||||
// Special case where the chatview was launched from the buddy list, but the latest
|
||||
// chat over a phone number. HI wants this to switch to a IM transport so choose the
|
||||
// IM transport with the best availability
|
||||
if (selectIMTransport === true) {
|
||||
for (i = 0; i < data.length; i++) {
|
||||
transport = data[i];
|
||||
if (!transport.kind && !transport.disabled) {
|
||||
if (transport.serviceName === selectIMBuddyStatus.serviceName && transport.value === selectIMBuddyStatus.username) {
|
||||
//enyo.log("match found %i", i);
|
||||
selectedTransport = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedTransport === -1) {
|
||||
// selected transport is a phone number
|
||||
if (serviceIsSms === true) {
|
||||
//enyo.log("TransportPickerModel.selectBestTransport: looking for phone number");
|
||||
for (i = 0; i < data.length; i++) {
|
||||
transport = data[i];
|
||||
if (!transport.kind && !transport.disabled) {
|
||||
if (enyo.messaging.utils.isTextMessage(transport.serviceName) === true) {
|
||||
// The transport may have a longer normalized phone number (eg, including area code)
|
||||
// so just see if the two strings match up to the length of normalizedChatAddress.
|
||||
if (transport.normalizedValue.indexOf(normalizedChatAddress) === 0) {
|
||||
//enyo.log("match found %i", i);
|
||||
selectedTransport = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // transport is IM
|
||||
//enyo.log("TransportPickerModel.selectBestTransport: looking for IM addr");
|
||||
for (i = 0; i < data.length; i++) {
|
||||
transport = data[i];
|
||||
if (transport.serviceName === serviceName && transport.normalizedValue === normalizedChatAddress) {
|
||||
selectedTransport = i;
|
||||
//enyo.log("TransportPicker.selectBestTransport: found IM addr selectedTransport:",selectedTransport," this._selectedTransport:",this._selectedTransport);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there was no match, use a transport of the same type
|
||||
// else: just use the first transport as a last resort
|
||||
if (selectedTransport === -1) {
|
||||
enyo.warn("Unable to match transport that the chat was launched with. Choosing the best one.");
|
||||
var foundMatch = false;
|
||||
var backupChoice = -1;
|
||||
for (i = 0; i < data.length && !foundMatch; i++) {
|
||||
transport = data[i];
|
||||
if (!transport.kind && !transport.disabled) {
|
||||
// match if both are phone numbers (serviceName == sms) or match where the serviceNames are the same (same IM transport)
|
||||
if (serviceName === transport.serviceName) {
|
||||
//enyo.log("match found %i", i);
|
||||
selectedTransport = i;
|
||||
foundMatch = true;
|
||||
}
|
||||
// backup match: both are an IM transport. We will use this if we cannot match another IM transport with the same serviceName
|
||||
if (serviceIsSms === false && enyo.messaging.utils.isTextMessage(transport.serviceName) === false) {
|
||||
backupChoice = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedTransport === -1) {
|
||||
if (backupChoice !== -1) {
|
||||
//enyo.log("No match found. Using backup IM choice %i", i);
|
||||
selectedTransport = backupChoice; // use the backup if we have one
|
||||
} else {
|
||||
//enyo.log("No match found. Selecting first item in list");
|
||||
selectedTransport = 2; // only other option is to use the first transport in the array
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
enyo.error("transportpicker::selectBestTransport::Error, the transport list is empty.");
|
||||
selectedTransport = 0;
|
||||
this._transports = [{
|
||||
username: "Error",
|
||||
serviceName: "error",
|
||||
availability: enyo.messaging.im.availability.NO_PRESENCE
|
||||
}];
|
||||
}
|
||||
if (selectedTransport !== -1 && this._selectedTransport !== selectedTransport) {
|
||||
this._selectedTransport = selectedTransport;
|
||||
enyo.log("--------transportPicker::selectBestTransports::this._transports:", this._transports[this._selectedTransport]);
|
||||
listSelectorObj.setItems(this._transports);
|
||||
if (this._transports[this._selectedTransport]) {
|
||||
listSelectorObj.setValue(this._transports[this._selectedTransport].id);
|
||||
listSelectorObj.setLabel(this._transports[this._selectedTransport].label);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getBuddyHashKey: function(serviceName, address) {
|
||||
return serviceName+(address.toLowerCase());
|
||||
},
|
||||
|
||||
getBuddyAvailability: function(serviceName, address) {
|
||||
var availability = enyo.messaging.im.availability.NO_PRESENCE;
|
||||
if (enyo.messaging.utils.isTextMessage(serviceName) === false) {
|
||||
var buddy = this._buddyStatusHash[this._getBuddyHashKey(serviceName, address)];
|
||||
if (buddy === undefined || buddy.availability === undefined) {
|
||||
availability = enyo.messaging.im.availability.OFFLINE;
|
||||
} else {
|
||||
availability = buddy.availability;
|
||||
}
|
||||
}
|
||||
return availability;
|
||||
},
|
||||
|
||||
getTransportId: function(addr, serviceName, accountId) {
|
||||
var id;
|
||||
if (enyo.messaging.utils.isTextMessage(serviceName)) {
|
||||
id = "phone_"+addr;
|
||||
} else {
|
||||
id = "IM_"+addr+":"+serviceName;
|
||||
}
|
||||
return (accountId ? id+accountId: id);
|
||||
},
|
||||
|
||||
gotStatus: function(inResponse, statusChangedFn){
|
||||
this._buddyStatusHash = {};
|
||||
if(inResponse.returnValue && inResponse.results && inResponse.results.length > 0){
|
||||
var i, buddy;
|
||||
var transportUpdateFn = function(transport) {
|
||||
if (transport.serviceName === buddy.serviceName && transport.caption === buddy.username) {
|
||||
transport.availability = this.getBuddyAvailability(transport.serviceName, transport.caption);
|
||||
transport.icon = this.availabilityIcons[transport.availability];
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
for(i = 0; i < inResponse.results.length; ++i) {
|
||||
buddy = inResponse.results[i];
|
||||
this._buddyStatusHash[this._getBuddyHashKey(buddy.serviceName, buddy.username)] = buddy;
|
||||
// Whenever a buddy changes, update the transport for that buddy (copy over availability)
|
||||
this._transports.forEach(transportUpdateFn);
|
||||
var selection = this._transports[this._selectedTransport];
|
||||
if( selection && selection.serviceName === buddy.serviceName &&
|
||||
selection.caption === buddy.username) {
|
||||
this._headerStatusObj.setClassName("status status-"+enyo.messaging.im.buddyAvailabilities[this.getBuddyAvailability(buddy.serviceName, buddy.username)]);
|
||||
//update buddy status in converstaion list
|
||||
statusChangedFn({
|
||||
serviceName: buddy.serviceName,
|
||||
username: buddy.username,
|
||||
availability: this.getBuddyAvailability(buddy.serviceName, buddy.username),
|
||||
status: buddy.status,
|
||||
_kind: buddy._kind
|
||||
});
|
||||
}
|
||||
}
|
||||
this._transportSelectorObj.setItems(this._transports);
|
||||
}
|
||||
else {
|
||||
enyo.error("transportPicker::gotStatus:Failed to get status:payload: ", inResponse.results);
|
||||
this._headerStatusObj.setClassName("status status-no-presence");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
enyo.kind({
|
||||
name: "BannerThrottler",
|
||||
kind: enyo.Component,
|
||||
newMessages: [],
|
||||
bannerWidgetIds: [],
|
||||
lastShownTimestamp: 0,
|
||||
SHOW_BANNER_INTERVAL: 2000, // two seconds
|
||||
create: function(db) {
|
||||
this.inherited(arguments);
|
||||
this.dashboard = db;
|
||||
},
|
||||
addNewMessages: function(messages) {
|
||||
this.newMessages = this.newMessages.concat(messages);
|
||||
|
||||
if (Date.now() - this.lastShownTimestamp > this.SHOW_BANNER_INTERVAL * 2) {
|
||||
// show the banner immediately if the time for last banner shown is greater than twice of the pre-defined shown interval
|
||||
this.showBanner();
|
||||
} else if (!this.showBannerTimer) {
|
||||
this.showBannerTimer = setTimeout(function() {
|
||||
this.showBannerTimer = undefined;
|
||||
this.showBanner();
|
||||
}.bind(this), this.SHOW_BANNER_INTERVAL);
|
||||
}
|
||||
},
|
||||
showBanner: function() {
|
||||
this.lastShownTimestamp = Date.now();
|
||||
|
||||
//enyo.log("this.newMessages: ", this.newMessages);
|
||||
if (this.newMessages.length > 0) {
|
||||
var messages = this.newMessages;
|
||||
|
||||
enyo.log("Showing banner for ", messages.length, " new messages received");
|
||||
this.newMessages = [];
|
||||
var bannerMessage = this.getBannerMessagePrefix(messages) + this.getBannerMessage(messages[messages.length - 1]);
|
||||
|
||||
// play notification sound and then put up banner
|
||||
this.dashboard.playSoundNotification();
|
||||
if (this.bannerWidgetIds.length > 10) {
|
||||
this.bannerWidgetIds = [];
|
||||
}
|
||||
this.bannerWidgetIds.push(enyo.windows.addBannerMessage(bannerMessage, "{}", "images/notification-small.png"));
|
||||
}
|
||||
},
|
||||
getBannerMessagePrefix: function(messages) {
|
||||
return messages.length > 1 ? ("(" + messages.length + "/" + messages.length + ") ") : "";
|
||||
},
|
||||
getBannerMessage: function(latestMessage) {
|
||||
enyo.log("Latest message for banner: ", latestMessage);
|
||||
return this.dashboard.getDisplayName(latestMessage) + ": " + this.dashboard.getDisplayText(latestMessage);
|
||||
},
|
||||
clearBannerMessages: function(filter) {
|
||||
var ids = this.bannerWidgetIds;
|
||||
this.bannerWidgetIds = [];
|
||||
|
||||
// TODO: there was a framework issue that prevented the following codes to work, so this function is currently not used.
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
enyo.log("Clearing banner with id: ", ids[i]);
|
||||
try {
|
||||
enyo.windows.removeBannerMessage(ids[i]);
|
||||
} catch (e) {
|
||||
enyo.warn("Unable to remove banner messages");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
enyo.kind({
|
||||
name: "Class0AlertManager",
|
||||
kind: "enyo.Component",
|
||||
dialogName: "messaging-class0Alert-stage",
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{dbKind: "com.palm.carrierdb.settings.current:1", components: [
|
||||
{name: "carrierFinder", method: "find", onSuccess: "gotCarriers", subscribe: true, resubscribe: true, reCallWatches: true}
|
||||
]}
|
||||
]},
|
||||
{name: "messageAdder", kind: "DbService", dbKind: "com.palm.message:1", method: "put"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.$.carrierFinder.call();
|
||||
},
|
||||
gotCarriers: function(inSender, inResponse) {
|
||||
var carrier;
|
||||
|
||||
enyo.log("Class0AlertManager.gotCarriers(), got carrier settings: ", inResponse);
|
||||
if (inResponse && inResponse.results && inResponse.results.length > 0) {
|
||||
this.carrierSettings = inResponse.results[0];
|
||||
}
|
||||
},
|
||||
fail: function(inSender, inResponse) {
|
||||
enyo.error("Failed to get carrier db settings: ", inResponse);
|
||||
},
|
||||
handleNotification: function(params) {
|
||||
enyo.log("Class0Manager.handleNotification(), parmas: ", params);
|
||||
|
||||
var dialog = enyo.windows.fetchWindow(this.dialogName);
|
||||
var layer = this.getAlertLayer(params);
|
||||
|
||||
//show banner
|
||||
this.putUpBanner(layer);
|
||||
// show alert dialog
|
||||
if (!dialog) {
|
||||
enyo.log("Class0Manager.handleNotification(), creating class0 dialog with: ", layer);
|
||||
enyo.windows.openPopup("app/dashboards/class0/class0.html", this.dialogName, layer, undefined, "135px");
|
||||
}
|
||||
else {
|
||||
enyo.log("Class0Manager.handleNotification(), activating class0 dialog with: ", layer);
|
||||
enyo.windows.activate(undefined, this.dialogName, layer);
|
||||
}
|
||||
},
|
||||
getAlertLayer: function(params) {
|
||||
var layer = {};
|
||||
layer.message = params.message;
|
||||
layer.text = enyo.string.removeHtml(layer.message.messageText);
|
||||
|
||||
layer.message.carrierName = "carrier";
|
||||
if (this.carrierSettings) {
|
||||
layer.message.carrierName = this.carrierSettings.qOperatorLongName;
|
||||
}
|
||||
var template = new enyo.g11n.Template($L("Message from #{name}"));
|
||||
layer.title = layer.message.alertTitle = template.evaluate({name: layer.message.carrierName});
|
||||
|
||||
return layer;
|
||||
},
|
||||
putUpBanner: function(layer) {
|
||||
// put up banner
|
||||
var bannerMessage = layer.title + ": " + enyo.string.removeHtml(layer.text);
|
||||
enyo.windows.addBannerMessage(bannerMessage, "{}", "images/notification-small.png", "notifications");
|
||||
},
|
||||
addClass0Message: function(message) {
|
||||
// add message to MojoDB
|
||||
|
||||
// set dbkind to message
|
||||
message._kind = "com.palm.smsmessage:1";
|
||||
this.$.messageAdder.call({
|
||||
objects: [message]
|
||||
});
|
||||
},
|
||||
closeDialog: function() {
|
||||
var dialog = enyo.windows.fetchWindow(this.dialogName);
|
||||
if (dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,446 @@
|
||||
enyo.kind({
|
||||
name: "DashboardManager",
|
||||
kind: enyo.Component,
|
||||
published: {
|
||||
appDeactivated: true,
|
||||
filter: undefined
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{dbKind: "com.palm.message:1", components: [
|
||||
{name: "newMessagesFinder", method: "find", onSuccess: "gotMessages",subscribe: true, resubscribe: true, reCallWatches: true}
|
||||
]},
|
||||
{dbKind: "com.palm.chatthread:1", components: [
|
||||
{name: "threads", method: "get", onSuccess: "gotThreads"}
|
||||
]}
|
||||
]},
|
||||
//{name: "displayOn", kind:"PalmService", service:"palm://com.palm.display/control/", onSuccess: "", onFailure: ""},
|
||||
{name: "displayState", kind: "PalmService", service: "palm://com.palm.display/control/", method: "status", onSuccess: "displayUpdate", subscribe: true}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.init();
|
||||
},
|
||||
init: function() {
|
||||
this.cutoffRevSet = 0; // 'readRevSet' is a more reliable reference to indicate the order that the message is received on the phone. We can't use 'localTimestamp' since multiple messages can have the same local timestamp.
|
||||
this.lastRev = 0;
|
||||
this.filter = {filterAll: false};
|
||||
this.dashboardUtil = new MessageDashboardUtil();
|
||||
this.bannerThrottler = new BannerThrottler(this);
|
||||
|
||||
// listening to display on/off changes
|
||||
this.$.displayState.call();
|
||||
|
||||
// try to query the largest readRevSet for all inbox messages
|
||||
this.queryMessages();
|
||||
|
||||
// register for prefs changes
|
||||
enyo.application.prefsHandler.register(this, this.prefsUpdated.bind(this));
|
||||
},
|
||||
queryMessages: function() {
|
||||
enyo.log("*********** In ", this.name, "DashboardManager, querying new messages in MasessageDashboardManager");
|
||||
this.$.newMessagesFinder.cancel();
|
||||
this.$.newMessagesFinder.call({
|
||||
query: {
|
||||
orderBy: "readRevSet",
|
||||
desc: true,
|
||||
limit: !this.isInited ? 1 : 500
|
||||
}
|
||||
});
|
||||
},
|
||||
gotMessages: function(inSender, inResponse) {
|
||||
// TODO: need to handle the case that the result set has more records to follow
|
||||
//enyo.log("*********************** In ", this.name, "DashboardManager, gotMessages: ", inResponse);
|
||||
if (inResponse && inResponse.results.length > 0) {
|
||||
this.processChanges(inResponse.results);
|
||||
}
|
||||
if (!this.isInited) {
|
||||
this.isInited = true;
|
||||
}
|
||||
},
|
||||
updateWatch: function() {
|
||||
enyo.log("In ", this.name, "DashboardManager, update notification watch with revision: ", this.lastRev);
|
||||
// var where = parseQuery("where folder='inbox' and _rev>" + this.lastRev).where;
|
||||
var where = [{
|
||||
prop: "folder",
|
||||
op: "=",
|
||||
val: "inbox"
|
||||
}, {
|
||||
prop: "_rev",
|
||||
op: ">",
|
||||
val: this.lastRev
|
||||
}];
|
||||
this.$.newMessagesFinder.cancel();
|
||||
this.$.newMessagesFinder.call({
|
||||
query: {
|
||||
where: where,
|
||||
orderBy: "_rev"
|
||||
}
|
||||
});
|
||||
|
||||
// reset this flag
|
||||
this.shouldUpdateWatch = false;
|
||||
},
|
||||
processPendingMessages: function() {
|
||||
var tids = [];
|
||||
for (var i = 0; i < this.pendingMessages.length; i++) {
|
||||
var msg = this.pendingMessages[i];
|
||||
tids.push(msg.conversations[0]);
|
||||
}
|
||||
enyo.log("In ", this.name, "DashboardManager, looking up threads for new messages: ", tids);
|
||||
this.findThreads(tids);
|
||||
},
|
||||
findThreads: function(tids) {
|
||||
this.$.threads.call({
|
||||
ids: tids
|
||||
});
|
||||
},
|
||||
gotThreads: function(inSender, inResponse) {
|
||||
var results = inResponse.results;
|
||||
var peopleThreads = {};
|
||||
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
var thread = results[i];
|
||||
peopleThreads[thread._id] = thread.displayName;
|
||||
}
|
||||
|
||||
//enyo.log("In ", this.name, "DashboardManager, got threads ", results);
|
||||
for (var j = 0; j < this.pendingMessages.length; j++) {
|
||||
var msgThread = this.pendingMessages[j].conversations[0];
|
||||
|
||||
this.pendingMessages[j].displayName = peopleThreads[msgThread];
|
||||
}
|
||||
|
||||
this.notify();
|
||||
},
|
||||
notify: function() {
|
||||
// add dashboard layers into banner throttle so that throttle can batch the layers to show only one banner per specified interval
|
||||
this.bannerThrottler.addNewMessages(this.pendingMessages);
|
||||
|
||||
var layers = this.getPendingLayers(this.pendingMessages);
|
||||
// put new message layers into dashboard
|
||||
this.putUpDashboard(layers);
|
||||
|
||||
// clean up pendingMessages cache
|
||||
delete this.pendingMessages;
|
||||
|
||||
// update db watch
|
||||
if (this.shouldUpdateWatch) {
|
||||
this.updateWatch();
|
||||
}
|
||||
},
|
||||
putUpDashboard: function(layers) {
|
||||
enyo.log("In ", this.name, "DashboardManager, updating dashboard with new layers: ", layers);
|
||||
this.updateDashboard(layers);
|
||||
},
|
||||
updateDashboard: function(layers) {
|
||||
var db = this.dashboard;
|
||||
|
||||
if (!db && layers.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!db) {
|
||||
enyo.log("$$$$$$$$$$$$$$ In ", this.name, "DashboardManager, creating dashboard for ", this.name);
|
||||
db = this.createComponent({name: "messaging-" + this.name + "-dashboard", kind:"enyo.Dashboard", smallIcon:"images/notification-small.png", onIconTap:"dashboardIconTap", onMessageTap:"dashboardMessageTap", onUserClose: "dashboardClose"});
|
||||
this.dashboard = db;
|
||||
}
|
||||
this.dashboardUtil.addLayers(db, layers);
|
||||
},
|
||||
playSoundNotification: function() {
|
||||
var options = this.getSoundOptions(this.prefs);
|
||||
if (options && window.PalmSystem) {
|
||||
window.PalmSystem.playSoundNotification(options.soundClass, options.soundPath);
|
||||
}
|
||||
},
|
||||
dashboardIconTap: function(inSender, layer, event) {
|
||||
//enyo.log("In ", this.name, "DashboardManager, icon is tapped");
|
||||
var layers = this.dashboard.getLayers();
|
||||
enyo.windows.activate("index.html", "messaging", (layers.length > 1 ? {} : {message:layers[0]._message}));
|
||||
|
||||
// clear & close dashboard
|
||||
this.dashboard.setLayers([]);
|
||||
this.dashboardClose(this);
|
||||
},
|
||||
dashboardMessageTap: function(inSender, layer, event) {
|
||||
//enyo.log("In ", this.name, "DashboardManager, message text is tapped");
|
||||
enyo.windows.activate("index.html", "messaging", {message:layer._message});
|
||||
},
|
||||
dashboardClose: function(inSender, event) {
|
||||
//enyo.log("In ", this.name, "Dashboard is closed.");
|
||||
delete this.dashboard;
|
||||
},
|
||||
filterChanged: function() {
|
||||
this.clearDashboard(this.filter);
|
||||
this.bannerThrottler.clearBannerMessages(this.filter);
|
||||
},
|
||||
clearDashboard: function(filter) {
|
||||
var db = this.dashboard;
|
||||
if (db) {
|
||||
//enyo.log("In ", this.name, "DashboardManager, clearing dashboard with filter: ", filter);
|
||||
var layers = [];
|
||||
var currLayers = db.getLayers();
|
||||
if (currLayers) {
|
||||
for (var i = 0; i < currLayers.length; i++) {
|
||||
if (!this.matchFilter(filter, currLayers[i]._message.conversations)) {
|
||||
layers.push(currLayers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
db.setLayers(layers);
|
||||
}
|
||||
},
|
||||
removeThread : function(thread) {
|
||||
//enyo.log("In ", this.name, "DashboardManager, removeThread - thread = ", thread);
|
||||
var db = this.dashboard;
|
||||
|
||||
function threadInCoversations (conversations) {
|
||||
var x = conversations.length;
|
||||
while (x--) {
|
||||
if (conversations[x] === thread) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (db) {
|
||||
var layers = [];
|
||||
var currLayers = db.getLayers();
|
||||
|
||||
if (currLayers) {
|
||||
for (var i = 0; i < currLayers.length; i++) {
|
||||
if (!threadInCoversations(currLayers[i]._message.conversations)) {
|
||||
layers.push(currLayers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
db.setLayers(layers);
|
||||
}
|
||||
},
|
||||
accountRemoved : function(account) {
|
||||
//enyo.log("In ", this.name, "DashboardManager, accountRemoved: ", account.username);
|
||||
var db = this.dashboard;
|
||||
|
||||
function keepLayer(layer) {
|
||||
if (layer._message.serviceName !== account.serviceName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (layer._message.username === account.username) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If target user name is not an email address, then we need to use the
|
||||
* user name prefix of the email address from the IM account and compare
|
||||
* it to the target user name. So 'bob.dole' should match 'bob.dole@company.com'
|
||||
* but not 'bob@company.com'.
|
||||
*/
|
||||
var accountUsername = account.username.replace(/\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/, "");
|
||||
return layer._message.username !== accountUsername;
|
||||
}
|
||||
|
||||
if (db) {
|
||||
var layers = [];
|
||||
var currLayers = db.getLayers();
|
||||
|
||||
if (currLayers) {
|
||||
for (var i = 0; i < currLayers.length; i++) {
|
||||
if (keepLayer(currLayers[i])) {
|
||||
layers.push(currLayers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
db.setLayers(layers);
|
||||
}
|
||||
},
|
||||
// unit tested functions
|
||||
prefsUpdated: function(latestPrefs) {
|
||||
this.prefs = latestPrefs;
|
||||
//enyo.log("---------#### In ", this.name, "Dashboard, got preferences: ", this.prefs);
|
||||
|
||||
if (this.prefs && !this.prefs.enableNotification) {
|
||||
this.clearDashboard();
|
||||
}
|
||||
},
|
||||
appDeactivatedChanged: function() {
|
||||
//enyo.log("In ", this.name, "DashboardManager, app deactivated changed: ", this.appDeactivated);
|
||||
if (!this.appDeactivated) {
|
||||
this.clearDashboard(this.filter);
|
||||
this.bannerThrottler.clearBannerMessages(this.filter);
|
||||
}
|
||||
},
|
||||
// maintains this.displayOff, used to determine if new message dashboards should be displayed.
|
||||
displayUpdate: function (inSender, inResult) {
|
||||
if (inResult.event) {
|
||||
// Note, specifically checking Off and On so we ignore displayInactive,
|
||||
// displayActive and displayDimmed
|
||||
if (inResult.event === "displayOff") {
|
||||
this.displayOff = true;
|
||||
} else if (inResult.event === "displayOn" || inResult.event === "request") {
|
||||
this.displayOff = false;
|
||||
}
|
||||
|
||||
//enyo.log("displayUpdate event ", inResult.event);
|
||||
}
|
||||
},
|
||||
processChanges: function(newMessages) {
|
||||
this.pendingMessages = this.getPendingNewMessages(newMessages);
|
||||
|
||||
if (this.pendingMessages.length > 0) {
|
||||
this.processPendingMessages();
|
||||
} else if (this.shouldUpdateWatch) {
|
||||
this.updateWatch();
|
||||
}
|
||||
},
|
||||
isNewMessage: function(message) {
|
||||
var isNew = message.folder === enyo.messaging.message.FOLDERS.INBOX;
|
||||
isNew = isNew && (message.conversations && message.conversations.length > 0);
|
||||
isNew = isNew && (message.readRevSet > this.cutoffRevSet);
|
||||
isNew = isNew && (message.status === "successful" || message.status === "delayed"); // TODO: need to figure out whether we should show notification for messages with error
|
||||
isNew = isNew && !message.errorCode;
|
||||
isNew = isNew && (!message.flags || (!message.flags.read && !message.flags.visible && !message.flags.noNotification));
|
||||
isNew = isNew &&(!message.accepted || message.accepted === "pending");
|
||||
|
||||
return isNew;
|
||||
},
|
||||
shouldNotify: function(message) {
|
||||
var should = this.prefs && this.prefs.enableNotification;
|
||||
should = should && (this.displayOff || (this.appDeactivated || !this.matchFilter(this.filter, message.conversations)));
|
||||
|
||||
return should;
|
||||
},
|
||||
getPendingNewMessages: function(newMessages) {
|
||||
var i, msg;
|
||||
var latestRev = 0;
|
||||
var latestRevSet = this.cutoffRevSet;
|
||||
var pendingMessages = [];
|
||||
|
||||
enyo.log("In ", this.name, "DashboardManager, cutoffRevset: ", this.cutoffRevSet);
|
||||
//enyo.log(" current filter: ", this.filter);
|
||||
for (i = 0; i < newMessages.length; i++) {
|
||||
msg = newMessages[i];
|
||||
//enyo.log("*********** In ", this.name, "DashboardManager, message: ", msg);
|
||||
|
||||
if (!this.isInited && msg.folder === enyo.messaging.message.FOLDERS.INBOX) {
|
||||
latestRevSet = msg.conversations && latestRevSet < msg.readRevSet ? msg.readRevSet : latestRevSet;
|
||||
} else if (this.isNewMessage(msg)) {
|
||||
if (this.shouldNotify(msg)) {
|
||||
pendingMessages.push(msg);
|
||||
}
|
||||
latestRevSet = msg.conversations && latestRevSet < msg.readRevSet ? msg.readRevSet : latestRevSet;
|
||||
}
|
||||
latestRev = latestRev < msg._rev ? msg._rev : latestRev;
|
||||
}
|
||||
|
||||
if (latestRevSet > this.cutoffRevSet) {
|
||||
this.cutoffRevSet = latestRevSet;
|
||||
}
|
||||
|
||||
if (latestRev > this.lastRev) {
|
||||
this.lastRev = latestRev;
|
||||
this.shouldUpdateWatch = true;
|
||||
}
|
||||
|
||||
return pendingMessages;
|
||||
},
|
||||
getPendingLayers: function(pendingMessages) {
|
||||
var layers = [];
|
||||
|
||||
enyo.log("*********** In ", this.name, "DashboardManager, pending changes for notification: ", this.pendingMessages);
|
||||
// notify new messages
|
||||
for (var i = 0; i < pendingMessages.length; i++) {
|
||||
var msg = pendingMessages[i];
|
||||
// get layers to push to dashboard
|
||||
layers.push(this.makeDashboardLayer(msg));
|
||||
}
|
||||
|
||||
return layers;
|
||||
},
|
||||
makeDashboardLayer: function(message) {
|
||||
var layer = {};
|
||||
|
||||
layer._message = message;
|
||||
layer._messageCount = 1;
|
||||
if (message.from) {
|
||||
layer._from = layer.title = this.getDisplayName(message);
|
||||
} else {
|
||||
layer.title = "";
|
||||
enyo.error("Message ", message._id, " is missing Sender information, message len= ", (message.messageText && message.messageText.length));
|
||||
}
|
||||
layer.text = this.getDisplayText(message);
|
||||
layer.icon = "images/notification-large-messaging.png";
|
||||
|
||||
return layer;
|
||||
},
|
||||
getDisplayName: function(message) {
|
||||
//enyo.log("%%%% In ", this.name, "DashboardManager, message's display name=", message.displayName, ", message.from.name= ", message.from.name, ", message.from.addr=", message.from.addr);
|
||||
if (enyo.messaging.person.isNotBlank(message.displayName)) {
|
||||
return message.displayName;
|
||||
} else if (message.from && enyo.messaging.person.isNotBlank(message.from.name)) {
|
||||
return message.from.name;
|
||||
} else if (message.from && enyo.messaging.person.isNotBlank(message.from.addr)) {
|
||||
return message.from.addr;
|
||||
}
|
||||
|
||||
enyo.warn("DashboardManager: unable to find dispaly name for message '", message._id, "', so default display name to a blank string");
|
||||
return "";
|
||||
},
|
||||
getDisplayText: function(message) {
|
||||
var text = message.messageText || "";
|
||||
|
||||
if (enyo.messaging.message.isMMSMessage(message)) {
|
||||
text = enyo.messaging.message.getMMSDisplayMessage();
|
||||
} else if (message._kind === "com.palm.immessage.libpurple:1" || message._kind === "com.palm.immessage.skypem:1" || message._kind === "com.palm.iminvitation:1") {
|
||||
// almost all the kinds of the messages, except the ones from libpurple and skype,
|
||||
// are escaped HTML tags by transports before saving them. The two
|
||||
// exception transports remove HTML when processing incoming messages.
|
||||
// however, apostrophe is escaped by gtalk, so unescape it to clean the text.
|
||||
text = text.replace(/'/g, "'");
|
||||
// remove all trusted html tags
|
||||
text = enyo.messaging.message.removeHtml(text);
|
||||
} else {
|
||||
// other transport escaped text in DB
|
||||
text = enyo.messaging.message.unescapeText(text);
|
||||
}
|
||||
// remove new line characters
|
||||
text = text.replace(/\r|\n|\\r|\\n/g, " ");
|
||||
|
||||
return text;
|
||||
},
|
||||
matchFilter: function(filter, threads) {
|
||||
if (!filter) {// || filter.filterAll) {
|
||||
return true;
|
||||
} else if (!threads) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < threads.length; i++) {
|
||||
if (filter.thread === threads[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
getSoundOptions: function(prefs) {
|
||||
if (prefs && prefs.notificationSound === "system") {
|
||||
return {
|
||||
soundClass: enyo.messaging.message.SOUND_CLASSES.RINGTON,
|
||||
soundPath: enyo.messaging.utils.getAppRootPath() + enyo.messaging.message.SOUND_PATHS.RECEIVED
|
||||
};
|
||||
} else if (prefs && prefs.notificationSound === "vibrate") {
|
||||
return {
|
||||
soundClass: enyo.messaging.message.SOUND_CLASSES.VIBRATE
|
||||
};
|
||||
} else if (prefs && prefs.notificationSound === "ringtone") {
|
||||
return {
|
||||
soundClass: enyo.messaging.message.SOUND_CLASSES.RINGTON,
|
||||
soundPath: prefs.ringtone.fullPath
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
enyo.kind({
|
||||
name: "InviteDashboardManager",
|
||||
kind: DashboardManager,
|
||||
create: function() {
|
||||
this.name = "invite";
|
||||
this.inherited(arguments);
|
||||
this.$.newMessagesFinder.dbKind = "com.palm.iminvitation:1";
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,202 @@
|
||||
enyo.kind({
|
||||
name: "InviteWatcher",
|
||||
kind: enyo.Component,
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{dbKind: "com.palm.imcommand:1", components: [
|
||||
{name: "inviteCommandFinder", method: "find", onSuccess: "foundInviteCommands", subscribe: true, resubscribe: true, reCallWatches: true},
|
||||
{name: "inviteCommandDeleter", method: "del"}
|
||||
]},
|
||||
{dbKind: "com.palm.iminvitation:1", components: [
|
||||
{name: "invitationFinder", method: "find", onSuccess: "foundInvites"},
|
||||
{name: "invitationAdder", method: "put"}
|
||||
]}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.queryIMCommands();
|
||||
},
|
||||
queryIMCommands: function() {
|
||||
enyo.log("Querying IM commands to find buddy invite");
|
||||
|
||||
this.$.inviteCommandFinder.call({
|
||||
query: {
|
||||
where: [{
|
||||
prop: "command",
|
||||
op: "=",
|
||||
val: "receivedBuddyInvite"
|
||||
}, {
|
||||
prop: "handler",
|
||||
op: "=",
|
||||
val: "application"
|
||||
}]
|
||||
}
|
||||
}); // foundInviteCommands
|
||||
},
|
||||
foundInviteCommands: function(inSender, inResponse) {
|
||||
enyo.log("Got invite response: ", (inResponse || inResponse.results));
|
||||
delete this.pendingCommands;
|
||||
this.pendingCommands = {};
|
||||
|
||||
if (inResponse && inResponse.results) {
|
||||
for (var i = 0; i < inResponse.results.length; i++) {
|
||||
var command = inResponse.results[i];
|
||||
this.pendingCommands[command._id] = command;
|
||||
}
|
||||
|
||||
this.queryImInvitations(this.pendingCommands);
|
||||
}
|
||||
},
|
||||
queryImInvitations: function(pendingCommands) {
|
||||
var commandIds = [];
|
||||
for (var key in this.pendingCommands) {
|
||||
commandIds.push(key);
|
||||
}
|
||||
|
||||
// try to look up if buddy invites already exist or the given set of imcommand ids
|
||||
this.$.invitationFinder.call({
|
||||
query: {
|
||||
where: [{
|
||||
prop: "commandId",
|
||||
op: "=",
|
||||
val: commandIds
|
||||
}]
|
||||
}
|
||||
}); // foundInvites
|
||||
},
|
||||
foundInvites: function(inSender, inResponse) {
|
||||
this.removeProcessedCommands(this.pendingCommands, inResponse && inResponse.results);
|
||||
this.addPendingInvites(this.pendingCommands);
|
||||
},
|
||||
removeProcessedCommands: function(pendingCommands, foundInvites) {
|
||||
for (var i = 0; foundInvites && i < foundInvites.length; i++) {
|
||||
if (pendingCommands[foundInvites[i].commandId]) {
|
||||
// if buddy invite has been added already, remove it from
|
||||
// the pending invite command so that we won't add the same
|
||||
// buddy invite twice
|
||||
delete pendingCommands[foundInvites[i].commandId];
|
||||
}
|
||||
}
|
||||
},
|
||||
addPendingInvites: function(pendingCommands) {
|
||||
enyo.log("Creating IM invitations for the following pending invite commands: ", pendingCommands);
|
||||
var invites = [];
|
||||
for (var id in pendingCommands) {
|
||||
var command = pendingCommands[id];
|
||||
var account = this.getIMAccount(command);
|
||||
|
||||
if (account) {
|
||||
invites.push(this.createInvite(command, account));
|
||||
} else {
|
||||
enyo.error("The imcommand for adding buddy '", command.fromUsername,
|
||||
"' can not be processed since an IM account doesn't exist for serive='",
|
||||
command.serviceName, "' and username='", command.targetUsername,
|
||||
"'). So delete this command from database.");
|
||||
this.$.inviteCommandDeleter.call({
|
||||
ids: [command._id]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (invites.length > 0) {
|
||||
this.$.invitationAdder.call({
|
||||
objects: invites
|
||||
});
|
||||
}
|
||||
},
|
||||
createInvite: function(command, account) {
|
||||
return {
|
||||
_kind: "com.palm.iminvitation:1",
|
||||
folder: enyo.messaging.message.FOLDERS.INBOX,
|
||||
status: "successful",
|
||||
serviceName: command.serviceName,
|
||||
localTimestamp: Date.now(),
|
||||
from: {
|
||||
addr: command.fromUsername
|
||||
},
|
||||
to: [{
|
||||
addr: command.targetUsername
|
||||
}],
|
||||
commandId: command._id,
|
||||
accepted: "pending",
|
||||
messageText: this.getInviteText(command, account),
|
||||
username: account.username,
|
||||
accountId: account.accountId
|
||||
};
|
||||
},
|
||||
getInviteText: function(command, account, displayName) {
|
||||
var params = command.params;
|
||||
var accountName = account ? ", " + account.username + "," : "";
|
||||
var messageText = (params && params.message) ? enyo.string.removeHtml(params.message) : undefined;
|
||||
var template, text;
|
||||
|
||||
if (params && messageText && displayName) {
|
||||
template = new enyo.g11n.Template($L("Your account#{accountName} received an invitation from #{name} #{addr} with the message, \"#{msg}\"."));
|
||||
text = template.evaluate({
|
||||
accountName: accountName,
|
||||
name: displayName,
|
||||
addr: command.fromUsername,
|
||||
msg: messageText
|
||||
});
|
||||
} else if (params && messageText) {
|
||||
template = new enyo.g11n.Template($L("Your account#{accountName} received an invitation from #{addr} with the message, \"#{msg}\"."));
|
||||
text = template.evaluate({
|
||||
accountName: accountName,
|
||||
addr: command.fromUsername,
|
||||
msg: messageText
|
||||
});
|
||||
} else if (displayName) {
|
||||
template = new enyo.g11n.Template($L("Your account#{accountName} received an invitation from #{name} #{addr}."));
|
||||
text = template.evaluate({
|
||||
accountName: accountName,
|
||||
name: displayName,
|
||||
addr: command.fromUsername
|
||||
});
|
||||
} else {
|
||||
template = new enyo.g11n.Template($L("Your account#{accountName} received an invitation from #{addr}."));
|
||||
text = template.evaluate({
|
||||
accountName: accountName,
|
||||
addr: command.fromUsername
|
||||
});
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
getIMAccount: function(command){
|
||||
var ims = enyo.application.accountService.getCleanedImAccounts();
|
||||
var account = undefined;
|
||||
for (var i = 0, a; ims && i < ims.length; i++) {
|
||||
if (this.isSameIMAccount(command, ims[i])) {
|
||||
if (account) {
|
||||
// if there are more than one IM account that matches the service name
|
||||
// and user name pair, we should not display the account name.
|
||||
return undefined;
|
||||
} else {
|
||||
account = ims[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return account;
|
||||
},
|
||||
isSameIMAccount: function(command, im) {
|
||||
if (command.serviceName !== im.serviceName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var targetUsername = command.targetUsername;
|
||||
var imAccountName = im.username;
|
||||
if (targetUsername === imAccountName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If target user name is not an email address, then we need to use the
|
||||
* user name prefix of the email address from the IM account and compare
|
||||
* it to the target user name. So 'bob.dole' should match 'bob.dole@company.com'
|
||||
* but not 'bob@company.com'.
|
||||
*/
|
||||
var accountUsername = imAccountName.replace(/\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/, "");
|
||||
return targetUsername === accountUsername;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
enyo.kind({
|
||||
name: "MessageDashboardManager",
|
||||
kind: DashboardManager,
|
||||
create: function() {
|
||||
this.name = "message";
|
||||
this.inherited(arguments);
|
||||
},
|
||||
shouldNotify: function(message) {
|
||||
return message._kind !== "com.palm.iminvitation:1" && this.inherited(arguments);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* A utility class that merges the new layers into existing layers. The new layers
|
||||
* that are passed into this class should be ordered so that the layer with the
|
||||
* oldest message is placed at the beginning of the list and the layer with the
|
||||
* newest message is at the end.
|
||||
*
|
||||
* After all the layers are merged, the layers that are set into dashboard are
|
||||
* ordered by read revision. The layer with the latest read revision, which is
|
||||
* the latest message, will be placed at the end of the list. The layer with oldest
|
||||
* read revision will be at the beginning of the list.
|
||||
*
|
||||
* Each layer in dashboard represents a chat thread. When the chat thread is
|
||||
* not found in the existing dashboard layers, the layer with new chat thread is
|
||||
* added to the end of the list immediately. When there are multiple new
|
||||
* messages are sent to a chat thread, these new messages are merged into one single
|
||||
* layer. A counter will be used to indicate how many new messages are in the
|
||||
* layer this chat thread. And the layer will show the latest message text.
|
||||
*/
|
||||
enyo.kind({
|
||||
name: "MessageDashboardUtil",
|
||||
addLayers: function(dashboard, newLayers) {
|
||||
if (newLayers) {
|
||||
try {
|
||||
var currentLayers = dashboard.getLayers() || [];
|
||||
for (var i = 0; i < newLayers.length; i++) {
|
||||
var layer = newLayers[i];
|
||||
if (enyo.messaging.message.isMMSMessage(layer._message)) {
|
||||
// update layer's title and text for MMS message layer
|
||||
layer = this.getMMSMessageLayer(layer);
|
||||
}
|
||||
|
||||
this.pushLayer(currentLayers, layer);
|
||||
}
|
||||
//TODO: need to hide personal data in the logging below
|
||||
//enyo.log("In MessageDashboard, new layers in dashboard", currentLayers);
|
||||
dashboard.setLayers(currentLayers);
|
||||
} catch (e) {
|
||||
// got error, most likely from framework or other logic error
|
||||
enyo.error("Unable to add dashboard messages");
|
||||
}
|
||||
}
|
||||
},
|
||||
// unit tested functions
|
||||
getMMSMessageLayer: function(layer) {
|
||||
var mmsLayer = layer;
|
||||
mmsLayer._from = mmsLayer.title = layer._from;
|
||||
mmsLayer.text = enyo.messaging.message.getMMSDisplayMessage();
|
||||
|
||||
return mmsLayer;
|
||||
},
|
||||
pushLayer: function(currentLayers, layer) {
|
||||
var ndx = this.findExistingLayerIndex(currentLayers, layer);
|
||||
|
||||
if (ndx === undefined) {
|
||||
// this is a new layer
|
||||
this.addNewLayer(currentLayers, layer);
|
||||
} else {
|
||||
enyo.log("******* found existing layer at location ", ndx, " in array of ", currentLayers.length, " elements");
|
||||
|
||||
if (layer._message.readRevSet > currentLayers[ndx]._message.readRevSet) {
|
||||
// only update existing layer if new layer has a newer timestamp
|
||||
var newLayer = this.getUpdatedLayer(currentLayers[ndx], layer);
|
||||
|
||||
if (ndx !== currentLayers.length - 1) {
|
||||
this.moveToLatestLayer(currentLayers, newLayer, ndx);
|
||||
} else {
|
||||
this.mergeLatestLayer(currentLayers, newLayer, ndx);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
findExistingLayerIndex: function(currentLayers, newLayer) {
|
||||
var layer;
|
||||
for (var i = 0; i < currentLayers.length; i++) {
|
||||
layer = currentLayers[i];
|
||||
|
||||
if (enyo.messaging.message.isMMSMessage(newLayer._message) && enyo.messaging.message.isMMSMessage(layer._message)) {
|
||||
return i;
|
||||
} else {
|
||||
newThreads = newLayer._message.conversations;
|
||||
existingThreads = layer._message.conversations;
|
||||
|
||||
//enyo.log("******* is new layer ", newLayer, " a mms message: ", enyo.messaging.message.isMMSMessage(newLayer._message));
|
||||
//enyo.log("******* do current layer ", layer._message._id, " and new layer ", newLayer._message._id, " have same threads: ", this.areThreadsMatched(existingThreads, newThreads));
|
||||
|
||||
// The same thread with sms and mms messages are not considered
|
||||
// as the same layer in dashboard. They are placed in two separate
|
||||
// layers. Based on the current design, all MMS messages are always
|
||||
// placed in the same layer.
|
||||
if (!enyo.messaging.message.isMMSMessage(newLayer._message) && this.areThreadsMatched(existingThreads, newThreads, layer, newLayer)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
areThreadsMatched: function(threads1, threads2, layer1, layer2) {
|
||||
if (!threads1 || !threads2) {
|
||||
// THIS SCENARIO SHOULD NOT HAPPEN
|
||||
// if one of the threads are not defined, then the threads are not matched.
|
||||
enyo.warn("Dashboard layer for message ", (!threads1 ? (layer1._message._id + (!threads2 ? (" and " + layer2._message._id) : "")) : layer2._message._id), " doesn't belong to a chat thread.");
|
||||
return false;
|
||||
} else if (threads1.length === 0 && threads2.length === 0) {
|
||||
enyo.warn("Dashboard layer for message ", layer1._message._id, "and ", layer2._message._id, " doesn't belong to a chat thread.");
|
||||
// THIS SCENARIO SHOULD NOT HAPPEN
|
||||
// if both threads are empty, they are not considered as matched either
|
||||
// since messages in dashboard notification should have an associated chat thread.
|
||||
return false;
|
||||
}
|
||||
|
||||
var match = threads1.length === threads2.length;
|
||||
if (match) {
|
||||
for (var n = 0; n < threads1.length && n < threads2.length; n++) {
|
||||
if (threads1[n] !== threads2[n]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
},
|
||||
addNewLayer: function(currentLayers, layer) {
|
||||
currentLayers.push(layer);
|
||||
if (currentLayers.length > 1) {
|
||||
this.updateIcon(layer, true);
|
||||
}
|
||||
},
|
||||
moveToLatestLayer: function(currentLayers, newLayer, ndx) {
|
||||
// if existing layer is not the last element, we need to
|
||||
// reshavel elements in the array to make the elements sort
|
||||
// by timestamp.
|
||||
var sortIdx;
|
||||
|
||||
enyo.log("*********** move existing layer to the end of the list");
|
||||
for (sortIdx = ndx; sortIdx < currentLayers.length && newLayer._message.readRevSet > currentLayers[sortIdx]._message.readRevSet; sortIdx++) {
|
||||
}
|
||||
// remove existing layer from its original location
|
||||
currentLayers.splice(ndx, 1);
|
||||
// add it back to the new location
|
||||
currentLayers.splice(sortIdx, 0, newLayer);
|
||||
|
||||
if (currentLayers.length > 1 && sortIdx > 0) {
|
||||
// Always change existing layer to represent multi-layers since
|
||||
// there are more than one layer in the dashbarod.
|
||||
this.updateIcon(newLayer, true);
|
||||
|
||||
if (ndx === 0) {
|
||||
// change the new first element's icon to represent single layer.
|
||||
this.updateIcon(currentLayers[0], false);
|
||||
}
|
||||
}
|
||||
},
|
||||
mergeLatestLayer: function(currentLayers, newLayer, ndx) {
|
||||
this.updateIcon(newLayer, currentLayers.length > 1);
|
||||
// layer's position is not changed.
|
||||
currentLayers[ndx] = newLayer;
|
||||
enyo.log("************* merged latest layer: ", currentLayers[ndx]);
|
||||
},
|
||||
getUpdatedLayer: function(existingLayer, newLayer) {
|
||||
var layer = newLayer;
|
||||
|
||||
// increment unread new message count
|
||||
layer._messageCount = existingLayer._messageCount + 1;
|
||||
// only need to check one of the layers since both layers are representing the same thread or same kind of messages
|
||||
if (!enyo.messaging.message.isMMSMessage(existingLayer._message)) {
|
||||
layer.title = newLayer._from + " (" + layer._messageCount + ")";
|
||||
}
|
||||
//enyo.log("In MessageDashboard, updated existing layer: ", layer);
|
||||
|
||||
return layer;
|
||||
},
|
||||
updateIcon: function(layer, isMultiple) {
|
||||
if (layer) {
|
||||
layer.icon = isMultiple ? "images/notification-large-messaging-mult.png" : "images/notification-large-messaging.png";
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* A temporary dialog for Class0 notification. Once the framework has an Alert
|
||||
* widget, we should use that instead of this dialog box.
|
||||
*/
|
||||
enyo.kind({
|
||||
name: "Class0Dialog",
|
||||
kind: enyo.VFlexBox,
|
||||
className: "class0",
|
||||
showing: true,
|
||||
published: {
|
||||
/** Array of layer objects specifying contents of alert.*/
|
||||
layers: null
|
||||
},
|
||||
components:
|
||||
[
|
||||
{kind: enyo.HFlexBox, components: [
|
||||
{kind: "Image", src:"../../../images/notification-large-messaging.png", className:"notification-icon"},
|
||||
{kind: enyo.VFlexBox, className:"notification-text-box", components: [
|
||||
{name: "title", className: "notification-subject"},
|
||||
{name: "text", className: "notification-body-multi"}
|
||||
]}
|
||||
]},
|
||||
{kind: enyo.HFlexBox, components: [
|
||||
{
|
||||
kind:"NotificationButton", name: "dismissBtn", content: $L("Dismiss"), className: "enyo-notification-button-affirmative button-left", flex:1, onclick: "dismissClicked"
|
||||
},
|
||||
{
|
||||
kind:"NotificationButton", name: "keepBtn", content: $L("Keep"), className: "enyo-notification-button-alternate button-right", flex:1, onclick: "keepClicked"
|
||||
}
|
||||
]},
|
||||
{kind: "ApplicationEvents", onWindowParamsChange: "windowParamsChangeHandler"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.layers = [];
|
||||
},
|
||||
dismissClicked: function() {
|
||||
this.layers.pop();
|
||||
this.updateWindow();
|
||||
},
|
||||
keepClicked: function() {
|
||||
var topLayer = this.layers.pop();
|
||||
|
||||
this.updateWindow();
|
||||
enyo.application.class0AlertManager.addClass0Message(topLayer.message);
|
||||
},
|
||||
addLayer: function(layer) {
|
||||
this.layers.push(layer);
|
||||
this.updateWindow();
|
||||
},
|
||||
updateWindow: function() {
|
||||
if (!this.layers || !this.layers.length) {
|
||||
enyo.application.class0AlertManager.closeDialog();
|
||||
} else {
|
||||
var len = this.layers.length;
|
||||
var top = this.layers[len - 1];
|
||||
this.$.title.setContent(top.title);
|
||||
this.$.text.setContent(top.text);
|
||||
}
|
||||
},
|
||||
windowParamsChangeHandler: function(inSender, event) {
|
||||
var layer = event.params;
|
||||
|
||||
enyo.log("*&*&*&*&* Class0Dialog.windowParamsChangeHandler, window reactivated: ", layer);
|
||||
this.addLayer(layer);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
body {
|
||||
color: #FFF;
|
||||
font-family: Prelude, 'Prelude Medium', 'Segoe UI', Arial, Helvetica, sans-serif;
|
||||
background:transparent;
|
||||
}
|
||||
.notification-icon {
|
||||
margin-right:12px;
|
||||
margin-left:2px;
|
||||
}
|
||||
.notification-text-box {
|
||||
margin-bottom:15px;
|
||||
}
|
||||
.notification-subject {
|
||||
font-size:16px;
|
||||
font-weight:bold;
|
||||
}
|
||||
.notification-subject,
|
||||
.notification-body {
|
||||
width:260px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space:nowrap;
|
||||
}
|
||||
.notification-body,
|
||||
.notification-body-multi {
|
||||
font-size:14px;
|
||||
line-height:17px;
|
||||
}
|
||||
.notification-body-multi {
|
||||
width:260px;
|
||||
height:53px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.button-top {
|
||||
margin-bottom:4px;
|
||||
}
|
||||
.button-right {
|
||||
margin-left:4px;
|
||||
}
|
||||
.button-left {
|
||||
margin-right:4px;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
// console.profile && console.profile ("calendar");
|
||||
var timing = {start: -Date.now()};
|
||||
</script>
|
||||
|
||||
<title>Reminder</title>
|
||||
<script type="text/javascript" launch="debug" src="../../../../../enyo/0.10/framework/enyo.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
new Class0Dialog().renderInto(document.body);
|
||||
|
||||
(function() {
|
||||
enyo.relaunch = function(inParams){
|
||||
if (inParams.launchedAtBoot) {
|
||||
return;
|
||||
}
|
||||
enyo.log("Class0 enyo.relaunch inParams: ", inParams);
|
||||
enyo.windows.activate("class0.html", "class0", inParams);
|
||||
};
|
||||
enyo.applicationRelaunchHandler = enyo.relaunch;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
enyo.depends(
|
||||
"Class0Dialog.js",
|
||||
"class0.css"
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
FavoriteConstants= {
|
||||
NO_FAVORITE_MESSAGE: $L("You do not have any favorite contacts."),
|
||||
NO_IM_ACCOUNS_MESSAGE: $L("Tap here to add contact info")
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "FavoriteItem",
|
||||
kind: "SwipeableItem",
|
||||
confirmCaption: $L("Remove"),
|
||||
layoutKind: "HFlexLayout",
|
||||
components: [
|
||||
{name: "imageContainer", style: "display: inline-block;", components: [
|
||||
{className: "contact-image-border"},
|
||||
{name: "contactImage", kind: "Image", className: "contact-image"}
|
||||
]},
|
||||
{name: "status", className: "status status-buddy"},
|
||||
{className:"message-summary", components: [
|
||||
{name: "contactName", className:"contact-name-box contact-name", align: "center"},
|
||||
{name: "statusMessage", className: "status-message"},
|
||||
{name: "contactMessage", className: "contact-message"}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.addClass("contactItem");
|
||||
},
|
||||
updateContactName: function(inFavorite) {
|
||||
var displayName = enyo.messaging.person.getDisplayName(inFavorite);
|
||||
this.$.contactName.setContent(enyo.messaging.message.removeHtml(displayName));
|
||||
},
|
||||
updateStatus: function(ims) {
|
||||
var bestAvailability = this.getBestAvailability(ims);
|
||||
var statusClass = "";
|
||||
|
||||
if (bestAvailability !== undefined) {
|
||||
statusClass = "status-" + enyo.messaging.im.availabilityClasses[bestAvailability];
|
||||
}
|
||||
|
||||
this.$.status.setClassName("status status-buddy " + statusClass);
|
||||
|
||||
// update status message
|
||||
this.$.statusMessage.setContent(enyo.messaging.message.removeHtml(this.getStatusMessage(ims)));
|
||||
},
|
||||
updateContactImage: function(inPerson) {
|
||||
this.$.contactImage.setAttribute("src", enyo.messaging.person.getDisplayImage(inPerson));
|
||||
},
|
||||
updateAccounts: function(inFavorite) {
|
||||
if (enyo.messaging.person.hasMessagingAccounts(inFavorite) || enyo.messaging.person.hasSMSAccounts(inFavorite)) {
|
||||
this.$.contactMessage.setContent("");
|
||||
} else {
|
||||
// if there is no IM/SMS accounts, show the message to let user know to add contact info
|
||||
this.$.contactMessage.setContent(FavoriteConstants.NO_IM_ACCOUNS_MESSAGE);
|
||||
this.$.statusMessage.setContent("");
|
||||
this.$.contactName.setClassName("contact-name contact-name-no-messaging-account");
|
||||
}
|
||||
},
|
||||
setFavorite: function(inFavorite) {
|
||||
//enyo.log("#@#@ favorite: ", inFavorite);
|
||||
this.updateContactName(inFavorite);
|
||||
this.updateContactImage(inFavorite);
|
||||
this.updateAccounts(inFavorite);
|
||||
this.updateStatus(inFavorite.ims);
|
||||
},
|
||||
|
||||
/***********************************
|
||||
* Functions below are unit tested *
|
||||
***********************************/
|
||||
getBestAvailabilityImsIndex: function(ims) {
|
||||
var index;
|
||||
var bestAvailability;
|
||||
if (ims) {
|
||||
for (var i = 0; ims && i < ims.length; i++) {
|
||||
if (bestAvailability === undefined || ims[i].availability < bestAvailability) {
|
||||
index = i;
|
||||
bestAvailability = ims[i].availability;
|
||||
}
|
||||
}
|
||||
}
|
||||
return index;
|
||||
},
|
||||
getBestAvailability: function(ims) {
|
||||
var bestAvailability;
|
||||
if (ims) {
|
||||
var index = this.getBestAvailabilityImsIndex(ims);
|
||||
bestAvailability = index !== undefined ? ims[index].availability : undefined;
|
||||
}
|
||||
return bestAvailability;
|
||||
},
|
||||
getStatusMessage: function(ims) {
|
||||
var statusMessage = "";
|
||||
if (ims) {
|
||||
var index = this.getBestAvailabilityImsIndex(ims);
|
||||
if (index !== undefined) {
|
||||
statusMessage = ims[index].status || "";
|
||||
if (ims[index]._kind === "com.palm.imbuddystatus.libpurple:1") {
|
||||
statusMessage = enyo.messaging.message.unescapeText(statusMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
return statusMessage;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "FavoriteList",
|
||||
kind: "VFlexBox",
|
||||
className: "favoriteItem",
|
||||
published: {
|
||||
},
|
||||
events: {
|
||||
onSelectFavorite: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "FavoriteService", onSuccess: "gotFavorites", onWatch: "favoritesWatch"},
|
||||
{className:"header-shadow header-favorite-shadow"},
|
||||
//{kind: "SearchInput", hint: $L("Search"), className:"searchList", onSearch: "filterList"},
|
||||
{name: "noFavoriteMessage", content: FavoriteConstants.NO_FAVORITE_MESSAGE, className: "messageTexts"},
|
||||
{flex: 1, name: "list", kind: "DbList", desc: false, onQuery: "listQuery", className:"messaging-listsDivider", onSetupRow: "listSetupRow", components: [
|
||||
{name: "favoriteItem", tapHighlight:true, kind: "FavoriteItem", onclick: "favoriteClicked", onConfirm: "removeFavorite", onCancel: "disableKeyboardMannualMode"}
|
||||
]},
|
||||
{name: "contactLauncher", kind: "Launcher", app: "com.palm.app.contacts"},
|
||||
{name: "unfavoritePersonService", kind:"PalmService", service: "palm://com.palm.service.contacts/", method: "unfavoritePerson"}
|
||||
],
|
||||
favoritesWatch: function() {
|
||||
//enyo.log("#@#@ favorites list is updating......");
|
||||
this.$.list.reset();
|
||||
},
|
||||
gotFavorites: function(inSender, inResponse, inRequest) {
|
||||
//enyo.log("#@#@ got favorites: ", inResponse);
|
||||
this.$.list.queryResponse(inResponse, inRequest);
|
||||
|
||||
// by default, hide these messages
|
||||
this.$.noFavoriteMessage.hide();
|
||||
|
||||
|
||||
if ((inRequest.index === 0) && (!inResponse || !inResponse.results || inResponse.results.length === 0)) {
|
||||
this.$.noFavoriteMessage.show();
|
||||
}
|
||||
},
|
||||
filterList: function(inSender, inValue){
|
||||
this.filterString = inValue;
|
||||
this.$.list.reset();
|
||||
},
|
||||
listQuery: function(inSender, inQuery) {
|
||||
//enyo.log("#@#@ quering favorites");
|
||||
inQuery.where = [{
|
||||
prop: "favorite",
|
||||
op: "=",
|
||||
val: true
|
||||
}];
|
||||
inQuery.select = enyo.messaging.person.selectAttributes;
|
||||
inQuery.orderBy = "sortKey";
|
||||
|
||||
return this.$.favoriteService.call({query: inQuery});
|
||||
},
|
||||
listSetupRow: function(inSender, inFavorite, inIndex) {
|
||||
//enyo.log("#@#@ favorite list set up row" );
|
||||
this.$.favoriteItem.setFavorite(inFavorite);
|
||||
// set selected row color
|
||||
this.$.favoriteItem.addRemoveClass("enyo-item-selected", this.selectedRecord && inFavorite._id === this.selectedRecord._id ? true: false);
|
||||
},
|
||||
favoriteClicked: function(inSender, inEvent) {
|
||||
var selected = this.$.list.fetch(inEvent.rowIndex);
|
||||
if (selected) {
|
||||
//FIXME: should comment out the following log since it contain user data
|
||||
enyo.log("clicked on favorite: ", (selected.displayName || selected.username));
|
||||
|
||||
if (enyo.messaging.person.hasMessagingAccounts(selected)) {
|
||||
// update list item selection
|
||||
this.selectedRecord = selected;
|
||||
this.$.list.refresh();
|
||||
|
||||
this.doSelectFavorite({
|
||||
personId: this.selectedRecord._id,
|
||||
displayName: this.selectedRecord.displayName,
|
||||
username: this.selectedRecord.ims[0].username,
|
||||
serviceName: this.selectedRecord.ims[0].serviceName
|
||||
});
|
||||
} else if (enyo.messaging.person.hasSMSAccounts(selected)) {
|
||||
// update list item selection
|
||||
this.selectedRecord = selected;
|
||||
this.$.list.refresh();
|
||||
|
||||
this.doSelectFavorite({
|
||||
personId: this.selectedRecord._id,
|
||||
displayName: this.selectedRecord.displayName,
|
||||
username: enyo.messaging.utils.normalizeAddress(this.selectedRecord.phoneNumbers[0], "sms"),
|
||||
serviceName: "sms"
|
||||
});
|
||||
} else {
|
||||
this.$.contactLauncher.launch({
|
||||
contact: selected,
|
||||
launchType:"editContact"
|
||||
});
|
||||
// update dashboard's filter
|
||||
enyo.application.messageDashboardManager.setFilter({thread: undefined});
|
||||
}
|
||||
}
|
||||
},
|
||||
setSelection: function(inChatThread){
|
||||
if ((this.selectedRecord && inChatThread && this.selectedRecord._id === inChatThread.personId) || (!this.selectedRecord && inChatThread && !inChatThread.personId)) {
|
||||
//case selection didn't change, do nothing
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.selectedRecord = {_id: inChatThread ? inChatThread.personId : null};
|
||||
if (!inChatThread) {
|
||||
this.$.list.refresh();
|
||||
}
|
||||
}
|
||||
},
|
||||
disableKeyboardMannualMode: function(){
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
},
|
||||
removeFavorite: function(inSender, inEvent) {
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
var favorite = this.$.list.fetch(inEvent);
|
||||
this.$.unfavoritePersonService.call({"personId": favorite._id});
|
||||
},
|
||||
updateList: function() {
|
||||
this.$.list.update();
|
||||
},
|
||||
resetList: function() {
|
||||
this.$.list.reset();
|
||||
},
|
||||
windowHiddenHandler: function() {
|
||||
// dump data buffer
|
||||
this.$.list.punt();
|
||||
// cancel service call
|
||||
//this.$.favoriteService.cancel();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,154 @@
|
||||
enyo.kind({
|
||||
name: "FavoriteService",
|
||||
kind: enyo.Service,
|
||||
requestKind: "FavoriteRequest",
|
||||
events: {
|
||||
onWatch: ""
|
||||
}
|
||||
});
|
||||
|
||||
enyo.kind({
|
||||
name: "FavoriteRequest",
|
||||
kind: enyo.Request,
|
||||
events: {
|
||||
onWatch: "doWatch"
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{name: "people", method: "find", dbKind: "com.palm.person:1", onSuccess: "gotPeople", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/},
|
||||
{name: "peopleSearch", method: "search", dbKind: "com.palm.person:1", onSuccess: "gotPeople", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/},
|
||||
{name: "threads", dbKind: "com.palm.chatthread:1", method: "find", onSuccess: "gotThreads", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/},
|
||||
{name: "buddy", service: "palm://com.palm.tempdb/", dbKind: "com.palm.imbuddystatus:1", method: "find", onSuccess: "gotBuddies", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/}
|
||||
]}/*,
|
||||
{name: "mockPeople", kind: "MockDb", dbKind: "com.palm.people:1", method: "find", onSuccess: "gotPeople", onFailure: "fail", onWatch: "doWatch"}*/
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
},
|
||||
initComponents: function() {
|
||||
this.inherited(arguments);
|
||||
/*
|
||||
if (!window.PalmSystem) {
|
||||
this.$.people = this.$.mockPeople;
|
||||
}
|
||||
*/
|
||||
},
|
||||
call: function() {
|
||||
//enyo.log(this.params);
|
||||
if (this.params.query.search) {
|
||||
this.params.query.search = undefined;
|
||||
this.$.peopleSearch.cancel();
|
||||
this.$.peopleSearch.call(this.params);
|
||||
}
|
||||
else {
|
||||
this.$.people.cancel();
|
||||
this.$.people.call(this.params);
|
||||
}
|
||||
},
|
||||
reply: function() {
|
||||
//enyo.log(this.data);
|
||||
this.receive(this.data);
|
||||
},
|
||||
fail: function(inSender, inResponse) {
|
||||
enyo.error("FavoriteRequest DbService fail: ", inResponse);
|
||||
this.reply();
|
||||
},
|
||||
finish: function() {
|
||||
// don't destroy automatically, so we can see watches
|
||||
},
|
||||
gotPeople: function(inSender, inResponse, inRequest) {
|
||||
this.data = inResponse;
|
||||
this.assembleIds();
|
||||
this.queryThreads();
|
||||
},
|
||||
assembleIds: function() {
|
||||
this.pids = [];
|
||||
for (var i=0, d; d=this.data.results[i]; i++) {
|
||||
this.pids.push(d._id);
|
||||
}
|
||||
},
|
||||
queryThreads: function() {
|
||||
this.$.threads.id = "favoriteRequest_threads";
|
||||
this.$.threads.cancel();
|
||||
this.$.threads.call({query: {
|
||||
where: [{
|
||||
prop: "personId",
|
||||
op: "=",
|
||||
val: this.pids
|
||||
}],
|
||||
select: [
|
||||
"_id",
|
||||
"_kind",
|
||||
"personId",
|
||||
"normalizedAddress",
|
||||
"displayName",
|
||||
"unreadCount"
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
gotThreads: function(inSender, inResponse, inRequest) {
|
||||
//enyo.log("#@#@ got chat threads in ", inRequest.latency, "ms");
|
||||
//enyo.log("#@#@ got chat threads: ", inResponse);
|
||||
// join chat thread data to buddy data
|
||||
enyo.messaging.utils.joinData(this.data, inResponse.results, "personId", "_id", "thread");
|
||||
this.queryBuddies();
|
||||
},
|
||||
queryBuddies: function() {
|
||||
this.$.buddy.id = "favoriteRequest_buddies";
|
||||
this.$.buddy.cancel();
|
||||
this.$.buddy.call({query: {
|
||||
where: [{
|
||||
prop: "personId",
|
||||
op: "=",
|
||||
val: this.pids
|
||||
}],
|
||||
select: [
|
||||
"_id",
|
||||
"_kind",
|
||||
"personId",
|
||||
"username",
|
||||
"serviceName",
|
||||
"availability",
|
||||
"status"
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
gotBuddies: function(inSender, inResponse) {
|
||||
var favorites = this.data.results;
|
||||
var buddies = inResponse.results;
|
||||
var lookup = {};
|
||||
var i, j;
|
||||
|
||||
//enyo.log("@#@# got buddies: ", buddies);
|
||||
|
||||
// in Person' record, 'ims' records store only the username and service
|
||||
// name for each buddy. So we need to use this pair of information to
|
||||
// reverse look up the buddy record.
|
||||
for (i = 0; i < favorites.length; i++) {
|
||||
var favorite = favorites[i];
|
||||
lookup[favorite._id] = favorite.ims;
|
||||
}
|
||||
//enyo.log("@#@# got lookup: ", lookup);
|
||||
|
||||
for (i = 0; i < buddies.length; i++) {
|
||||
var ims = lookup[buddies[i].personId];
|
||||
//enyo.log("@#@# looked up ims: ", ims);
|
||||
if (ims) {
|
||||
for (j = 0; j < ims.length; j++) {
|
||||
if (buddies[i].serviceName === ims[j].type && buddies[i].username === ims[j].value) {
|
||||
ims[j].availability = buddies[i].availability;
|
||||
ims[j].status = buddies[i].status;
|
||||
ims[j]._kind = buddies[i]._kind;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// all done now
|
||||
this.reply();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
enyo.kind({
|
||||
name: "ConnectPhone",
|
||||
className: "connect-phone",
|
||||
kind: "enyo.VFlexBox",
|
||||
firstLaunch: false,
|
||||
events: {
|
||||
onComplete: ""
|
||||
},
|
||||
components: [
|
||||
{name: "connectPhoneMsg", content:FirstLaunchConstants.PAIR_PHONE_MESSAGE, className:"accounts-body-title"},
|
||||
{name: "launchApp", kind: "Launcher", app: "com.palm.app.bluetooth"},
|
||||
{name: "connectPhoneButton", kind: "Button", label: FirstLaunchConstants.PAIR_PHONE_BUTTON_LABEL, onclick: "connectPhone", className:"accounts-btn"},
|
||||
{name: "telephony", kind: "PalmService", service: "palm://com.palm.telephony/", method: "platformQuery", onSuccess: "gotPlatformData", subscribe: true}
|
||||
],
|
||||
subscriptToTelephony: function() {
|
||||
this.$.telephony.call();
|
||||
},
|
||||
gotPlatformData: function(inSender, inResponse) {
|
||||
enyo.log("------------ platformDate: ", inResponse);
|
||||
if (inResponse && inResponse.extended && inResponse.extended.capabilities && inResponse.extended.capabilities.mapcenable) {
|
||||
this.connected = true;
|
||||
this.$.connectPhoneMsg.setContent(FirstLaunchConstants.SHOW_PHONE_MESSAGE);
|
||||
this.$.connectPhoneButton.setCaption(FirstLaunchConstants.SHOW_PHONE_BUTTON_LABEL);
|
||||
} else {
|
||||
this.connected = false;
|
||||
this.$.connectPhoneMsg.setContent(FirstLaunchConstants.PAIR_PHONE_MESSAGE);
|
||||
this.$.connectPhoneButton.setCaption(FirstLaunchConstants.PAIR_PHONE_BUTTON_LABEL);
|
||||
}
|
||||
},
|
||||
connectPhone: function(){
|
||||
if (this.connected) {
|
||||
this.doComplete();
|
||||
} else {
|
||||
this.$.launchApp.launch();
|
||||
}
|
||||
},
|
||||
accountsUpdated: function(ims) {
|
||||
if (this.firstLaunch) {
|
||||
this.setShowing(ims && ims.length === 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
enyo.kind({
|
||||
name: "FirstLaunch",
|
||||
kind: "enyo.VFlexBox",
|
||||
events: {
|
||||
onFirstLaunchDone: ""
|
||||
},
|
||||
components: [
|
||||
{
|
||||
kind: "firstLaunchView",
|
||||
iconSmall: "images/messaging-48x48.png", // Path to small icon used for title bar
|
||||
iconLarge: "icon-256x256.png", // Path to large icon used for Welcome page
|
||||
/*
|
||||
components: [
|
||||
{name: "connectPhoneLayer", layoutKind: "VFlexLayout", className:"box-center", style:"margin-top:0", components: [
|
||||
{kind: "ConnectPhone", firstLaunch: true, onComplete: "done"}
|
||||
]}
|
||||
],
|
||||
*/
|
||||
onAccountsFirstLaunchDone: "done",
|
||||
capability: "MESSAGING"
|
||||
}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
|
||||
this.accountService = enyo.application.accountService;
|
||||
if (this.accountService) {
|
||||
this.accountService.register(this, this.accountsUpdated.bind(this));
|
||||
}
|
||||
},
|
||||
rendered: function() {
|
||||
this.inherited(arguments);
|
||||
|
||||
var msgs = {
|
||||
pageTitle: FirstLaunchConstants.TITLE_MESSAGE_WITH_ACCOUNTS,
|
||||
welcome: FirstLaunchConstants.TITLE_MESSAGE
|
||||
};
|
||||
|
||||
// Templates to exclude from the accounts list (can be an array if you need more than one)
|
||||
// Do not exclude com.palm.palmprofile; the library will do the right thing WRT that template
|
||||
var exclude = "com.palm.palmprofile";
|
||||
this.$.firstLaunchView.startFirstLaunch(exclude, msgs);
|
||||
|
||||
/** To fix DFISH-28718
|
||||
// Show your custom UI (optional)
|
||||
enyo.asyncMethod(this.$.connectPhoneLayer, "show");
|
||||
|
||||
this.$.connectPhone.subscriptToTelephony();
|
||||
*/
|
||||
},
|
||||
accountsUpdated: function() {
|
||||
//this.$.connectPhone.accountsUpdated(this.accountService.getImAccounts());
|
||||
},
|
||||
done: function() {
|
||||
this.unregisterServices();
|
||||
this.doFirstLaunchDone();
|
||||
},
|
||||
unregisterServices: function() {
|
||||
this.accountService.unregister(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
FirstLaunchConstants={
|
||||
TITLE_MESSAGE: $L("To get started, set up a messaging account:"),
|
||||
TITLE_MESSAGE_WITH_ACCOUNTS: $L("Your Messaging Accounts"),
|
||||
SETUP_IM_MESSAGE: $L("To get started, set up an IM account."),
|
||||
PAIR_PHONE_MESSAGE: $L("If this device is connected with your HP webOS phone, you can also send and receive SMS text messages."),
|
||||
PAIR_PHONE_BUTTON_LABEL: $L("Connect Phone"),
|
||||
SHOW_PHONE_MESSAGE: $L("Your device is connected with your phone. You can send & receive text messages through your phone connection."),
|
||||
SHOW_PHONE_BUTTON_LABEL: $L("Send & Receive Messages")
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
enyo.kind({
|
||||
name: "FirstLaunchHandler",
|
||||
kind: enyo.Component,
|
||||
showFirstLaunch: true,
|
||||
components: [
|
||||
{name: "checkFirstLaunch", kind: "Accounts.checkFirstLaunch", onCheckFirstLaunchResult: "gotFirstLaunchResult"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.$.checkFirstLaunch.shouldFirstLaunchBeShown({appId: "com.palm.app.messaging"});
|
||||
},
|
||||
gotFirstLaunchResult: function(inSender, inResponse) {
|
||||
if (inResponse) {
|
||||
this.showFirstLaunch = inResponse.showFirstLaunch;
|
||||
}
|
||||
},
|
||||
shouldShowFirstLaunch: function() {
|
||||
return this.showFirstLaunch;
|
||||
},
|
||||
migratePrefsSettingIntoAccountService: function(prefs) {
|
||||
if (prefs) {
|
||||
enyo.log("Migrating first launch setting from messaging app's preferences in to account service");
|
||||
if (prefs.firstUseMode === false) {
|
||||
// we only need to migrate the preferences setting only when
|
||||
// the app was previously passed first launch and the app used
|
||||
// preferences to keep track of first launch mode.
|
||||
this.passedFirstLaunch(); // mark first launch shown
|
||||
}
|
||||
|
||||
// then clear the first use mode setting
|
||||
prefs.firstUseMode = null;
|
||||
enyo.application.prefsHandler.setPrefs(prefs);
|
||||
}
|
||||
},
|
||||
passedFirstLaunch: function() {
|
||||
this.showFirstLaunch = false;
|
||||
// tell account services that messaging app has shown first launch.
|
||||
this.$.checkFirstLaunch.firstLaunchHasBeenShown(true);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
enyo.kind({
|
||||
name: "AccountLoginState",
|
||||
kind: "enyo.Control",
|
||||
layoutKind: "VFlexLayout",
|
||||
className: "account-login-state",
|
||||
published: {
|
||||
loginState: {}
|
||||
},
|
||||
events: {
|
||||
onAvailabilitySet: "",
|
||||
onInputCustomMessage: "",
|
||||
onDrawerOpened: ""
|
||||
},
|
||||
components: [
|
||||
{name: "account", onclick: "toggleDrawer", style: "min-height: 30px; margin-top:8px; width:320px;", components:[
|
||||
{name: "header", layoutKind: "HFlexLayout", components: [
|
||||
{name: "status", className: "status status-buddy", style: "margin-left:6px;margin-right:10px;margin-top:3px;"},
|
||||
{name: "accountText", layoutKind: "HFlexLayout", style: "width:265px;", components: [
|
||||
{name: "accountName", style: "margin-right:5px;vertical-align:middle;font-size:16px;" },
|
||||
{name: "username", flex: 1, style: "overflow: hidden; font-size: 14px;margin-top:2px;color:grey", className: "enyo-text-ellipsis"}
|
||||
]},
|
||||
{name: "arrow", className: "enyo-menuitem-arrow"}
|
||||
]},
|
||||
{name: "footer", layoutKind: "HFlexLayout", components: [
|
||||
{className: ""}, // TODO: should set the right class name so that custom message lines up with account name
|
||||
{name: "customMessage", className:"enyo-text-ellipsis", style: "width:100%;overflow: hidden; font-size: 14px; color:#333; margin-left: 32px;margin-right:14px;margin-top: 3px;padding-bottom: 8px;"}
|
||||
]}
|
||||
]},
|
||||
{kind: "Drawer", open: false, components: [
|
||||
{name: "statuses", kind: "AccountStatuses", onAvailabilityChanged: "availabilityChanged", onInputCustomMessage: "inputCustomMessage"}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.$.accountName.setContent(this.loginState.accountTypeName);
|
||||
this.$.username.setContent(this.loginState.username);
|
||||
this.loginStateChanged();
|
||||
|
||||
this.createComponent({name: "db", kind: "AccountLoginStateDB", accountId: this.loginState._id});
|
||||
},
|
||||
loginStateChanged: function() {
|
||||
var availability = enyo.messaging.imLoginState.getAvailability(this.loginState);
|
||||
this.$.statuses.setAvailability([availability]);
|
||||
this.$.statuses.setInvisibilitySupported(this.loginState.supportsInvisibleStatus);
|
||||
if (availability <= enyo.messaging.im.availability.BUSY) {
|
||||
this.$.customMessage.setContent(this.loginState.customMessage);
|
||||
} else {
|
||||
this.$.customMessage.setContent("");
|
||||
}
|
||||
this.updateStatusClass();
|
||||
},
|
||||
availabilityChanged: function(inSender) {
|
||||
this.loginState.availability = inSender.availability;
|
||||
this.doAvailabilitySet(this.$.db, this.loginState);
|
||||
},
|
||||
inputCustomMessage: function(inSender) {
|
||||
this.doInputCustomMessage(this.$.db, this.loginState);
|
||||
},
|
||||
toggleDrawer: function() {
|
||||
this.showDrawer = !this.showDrawer;
|
||||
this.$.drawer.setOpen(this.showDrawer);
|
||||
|
||||
if (this.showDrawer) {
|
||||
this.doDrawerOpened();
|
||||
this.$.arrow.setClassName("enyo-menuitem-arrow enyo-button-down");
|
||||
this.$.account.applyStyle("border-bottom", "1px solid rgba(0,0,0,0.2);");
|
||||
} else {
|
||||
this.$.arrow.setClassName("enyo-menuitem-arrow");
|
||||
this.$.account.applyStyle("border-bottom", "none");
|
||||
}
|
||||
},
|
||||
closeDrawer: function() {
|
||||
if (this.showDrawer) {
|
||||
this.toggleDrawer();
|
||||
}
|
||||
},
|
||||
updateStatusClass: function() {
|
||||
if (this.statusClass) {
|
||||
this.$.status.removeClass(this.statusClass);
|
||||
}
|
||||
this.statusClass = "status-" + enyo.messaging.im.buddyAvailabilities[enyo.messaging.imLoginState.getAvailability(this.loginState)];
|
||||
this.$.status.addClass(this.statusClass);
|
||||
}
|
||||
});
|
||||
|
||||
AccountLoginState.COMPONENT_NAME_PREFIX = "loginState-";
|
||||
@@ -0,0 +1,143 @@
|
||||
enyo.kind({
|
||||
name: "AccountLoginStatesPopup",
|
||||
kind: "enyo.Menu",
|
||||
lazy: false,
|
||||
style: "width:330px;",
|
||||
published: {
|
||||
loginStates: [],
|
||||
accountsState: {}
|
||||
},
|
||||
events: {
|
||||
onAvailabilitySet: "",
|
||||
onInputCustomMessage: ""
|
||||
},
|
||||
|
||||
components: [
|
||||
{name: "scroller", kind: "BasicScroller", flex: 1, horizontal: false, autoHorizontal: false, components: [
|
||||
{name: "allAccountsDivider", kind: "enyo.Divider", caption: $L("ALL ACCOUNTS"), style: "margin-top:5px;margin-bottom:5px;", showing: false},
|
||||
{name: "allAccounts", kind: "AccountStatuses", onAvailabilityChanged: "changeAccountsAvailability", className: "all-accounts", onInputCustomMessage: "inputAccountsCustomMessage"},
|
||||
{name: "indAccountsDivider", kind: "enyo.Divider", caption: $L("INDIVIDUAL ACCOUNTS"), style: "margin-top:5px;margin-bottom:5px;", showing: false}
|
||||
]},
|
||||
{name: "db", kind: "BaseLoginStateDB", loginStates: this.loginStates}
|
||||
],
|
||||
loginStatesChanged: function() {
|
||||
this.$.allAccountsDivider.setShowing(this.loginStates.length > 1);
|
||||
this.$.indAccountsDivider.setShowing(this.loginStates.length > 1);
|
||||
this.checkInvisibilitySupport();
|
||||
this.updateAllAccountsAvailability();
|
||||
this.updateLoginStateComponents();
|
||||
this.$.db.setLoginStates(this.loginStates);
|
||||
},
|
||||
checkInvisibilitySupport: function () {
|
||||
this.$.allAccounts.setInvisibilitySupported(this.shouldShowAllAccountsInvisibleStatus(this.loginStates));
|
||||
},
|
||||
updateAllAccountsAvailability: function () {
|
||||
var availArray = [];
|
||||
var availability;
|
||||
for (var i=0; i<this.loginStates.length; i++) {
|
||||
availArray[i] = enyo.messaging.imLoginState.getAvailability(this.loginStates[i]);
|
||||
}
|
||||
this.$.allAccounts.setAvailability(availArray);
|
||||
},
|
||||
updateLoginStateComponents: function() {
|
||||
var loginStateComponents = this.getLoginStateComponents();
|
||||
var key;
|
||||
|
||||
if (this.loginStates.length <= 1) {
|
||||
for (key in loginStateComponents) {
|
||||
this.destroyLoginStateComponent(loginStateComponents[key]);
|
||||
}
|
||||
} else {
|
||||
var component, i;
|
||||
var stateIds = {};
|
||||
|
||||
for (i = 0; i < this.loginStates.length; i++) {
|
||||
component = loginStateComponents[this.loginStates[i]._id];
|
||||
stateIds[this.loginStates[i]._id] = true;
|
||||
|
||||
if (component) {
|
||||
enyo.log("updating login state component");
|
||||
component.setLoginState(this.loginStates[i]);
|
||||
} else {
|
||||
component = this.$.scroller.createComponent({
|
||||
name: AccountLoginState.COMPONENT_NAME_PREFIX + this.loginStates[i]._id,
|
||||
kind: "AccountLoginState",
|
||||
loginState: this.loginStates[i],
|
||||
className: "individual-account",
|
||||
onAvailabilitySet: "changeAvailability",
|
||||
onInputCustomMessage: "inputCustomMessage",
|
||||
onDrawerOpened: "drawerOpened"
|
||||
}, {owner: this});
|
||||
component.render();
|
||||
}
|
||||
|
||||
if (i != 0) component.applyStyle("border-top", "1px solid rgba(255,255,255,0.5);");
|
||||
if (i != this.loginStates.length - 1) component.applyStyle("border-bottom", "1px solid rgba(0,0,0,0.2);");
|
||||
|
||||
}
|
||||
|
||||
for (key in loginStateComponents) {
|
||||
if (!stateIds[key]) {
|
||||
this.destroyLoginStateComponent(loginStateComponents[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
drawerOpened: function(inSender) {
|
||||
var loginStateComponents = this.getLoginStateComponents();
|
||||
var key;
|
||||
for (key in loginStateComponents) {
|
||||
if (loginStateComponents[key] != inSender) {
|
||||
loginStateComponents[key].closeDrawer();
|
||||
}
|
||||
}
|
||||
},
|
||||
getLoginStateComponents: function() {
|
||||
var components = {};
|
||||
|
||||
enyo.forEach(this.getComponents(), function(comp) {
|
||||
var name = comp.getName();
|
||||
if (name.indexOf(AccountLoginState.COMPONENT_NAME_PREFIX) >= 0) {
|
||||
var id = name.substring(AccountLoginState.COMPONENT_NAME_PREFIX.length, name.length);
|
||||
components[id] = comp;
|
||||
}
|
||||
});
|
||||
|
||||
return components;
|
||||
},
|
||||
destroyLoginStateComponent: function(component) {
|
||||
component.setShowing(false);
|
||||
component.destroy();
|
||||
},
|
||||
changeAccountsAvailability: function(inSender) {
|
||||
this.accountsState.availability = inSender.availability;
|
||||
this.doAvailabilitySet(this.$.db, this.accountsState);
|
||||
this.close();
|
||||
},
|
||||
changeAvailability: function(inSender, db, loginState) {
|
||||
this.doAvailabilitySet(db, loginState);
|
||||
this.close();
|
||||
},
|
||||
inputAccountsCustomMessage: function(inSender, inEvent) {
|
||||
this.doInputCustomMessage(this.$.db, this.accountsState);
|
||||
this.close();
|
||||
},
|
||||
inputCustomMessage: function(inSender, db, loginState) {
|
||||
this.doInputCustomMessage(db, loginState);
|
||||
this.close();
|
||||
},
|
||||
close: function() {
|
||||
this.inherited(arguments);
|
||||
// This will cause all drawers to close
|
||||
this.drawerOpened();
|
||||
},
|
||||
// unit tested functions
|
||||
shouldShowAllAccountsInvisibleStatus: function(states) {
|
||||
var show = !!states && states.length > 0 && states[0].supportsInvisibleStatus;
|
||||
var i = 1, len = states ? states.length : 0;
|
||||
for (; show && i < len; i++) {
|
||||
show = show && states[i].supportsInvisibleStatus;
|
||||
}
|
||||
return show;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
enyo.kind({
|
||||
name: "AccountStatuses",
|
||||
kind: "enyo.Control",
|
||||
layoutKind: "VFlexLayout",
|
||||
published: {
|
||||
availability: [],
|
||||
invisibilitySupported: false
|
||||
},
|
||||
events: {
|
||||
onAvailabilityChanged: "",
|
||||
onInputCustomMessage: ""
|
||||
},
|
||||
components: [
|
||||
{name: "availabilities", className: "availabilities", components: [
|
||||
{name: "available", kind: "Availability", caption: $L("Available"), value: enyo.messaging.im.availability.AVAILABLE, onclick:"updateAvailability", className: "enyo-first available-status"},
|
||||
{name: "busy", kind: "Availability", caption: $L("Busy"), value: enyo.messaging.im.availability.BUSY, onclick:"updateAvailability"},
|
||||
{name: "invisible", kind: "Availability", showing: false, caption: $L("Invisible"), value: enyo.messaging.im.availability.INVISIBLE, onclick:"updateAvailability"},
|
||||
{name: "offline", kind: "Availability", caption: $L("Offline"), value: enyo.messaging.im.availability.OFFLINE, onclick:"updateAvailability"},
|
||||
{name: "customStatus", kind: "MenuItem", caption: $L("Set custom status"), onclick:"setCustomMessage", className: "custom-status"}
|
||||
]}
|
||||
],
|
||||
availabilityChanged: function() {
|
||||
this.resetAvailabilities();
|
||||
|
||||
var showCustomStatus = false;
|
||||
for (var i=0; i<this.availability.length; i++) {
|
||||
this.setAvailabilityCheckMark(this.availability[i]);
|
||||
if (this.availability[i] <= enyo.messaging.im.availability.BUSY) {
|
||||
showCustomStatus = true;
|
||||
}
|
||||
}
|
||||
this.$.customStatus.setShowing(showCustomStatus);
|
||||
this.$.customStatus.$.item.addRemoveClass("enyo-last", showCustomStatus);
|
||||
this.$.offline.$.item.addRemoveClass("enyo-last", !showCustomStatus);
|
||||
},
|
||||
setAvailabilityCheckMark: function(availability) {
|
||||
if (availability === enyo.messaging.im.availability.AVAILABLE) {
|
||||
this.$.available.setChecked(true);
|
||||
} else if (availability === enyo.messaging.im.availability.BUSY) {
|
||||
this.$.busy.setChecked(true);
|
||||
} else if (availability === enyo.messaging.im.availability.INVISIBLE) {
|
||||
this.$.invisible.setChecked(true);
|
||||
} else if (availability === enyo.messaging.im.availability.OFFLINE) {
|
||||
this.$.offline.setChecked(true);
|
||||
}
|
||||
},
|
||||
invisibilitySupportedChanged: function() {
|
||||
this.$.invisible.setShowing(this.invisibilitySupported);
|
||||
},
|
||||
resetAvailabilities: function() {
|
||||
this.$.available.setChecked(false);
|
||||
this.$.busy.setChecked(false);
|
||||
this.$.invisible.setChecked(false);
|
||||
this.$.offline.setChecked(false);
|
||||
},
|
||||
updateAvailability: function(inSender, inEvent) {
|
||||
this.availability = inSender.value;
|
||||
//this.availabilityChanged(); // TODO: this may be done by the parent component
|
||||
this.doAvailabilityChanged(this.availability);
|
||||
},
|
||||
setCustomMessage: function(inSender, inEvent) {
|
||||
this.doInputCustomMessage(this.accountId);
|
||||
}
|
||||
});
|
||||
|
||||
enyo.kind({
|
||||
name: "Availability",
|
||||
className: "availability",
|
||||
kind: "MenuCheckItem",
|
||||
tapHighlight: true,
|
||||
itemChrome: [
|
||||
{name: "status", className: "av-status"},
|
||||
{name: "caption", className: "av-caption", flex: 1},
|
||||
{name: "arrow", className: "av-arrow"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
if (!this.$.status) {
|
||||
this.createItemChrome();
|
||||
}
|
||||
if (this.value === enyo.messaging.im.availability.BUSY) {
|
||||
this.$.status.addClass("status status-buddy status-away");
|
||||
} else {
|
||||
this.$.status.addClass("status status-buddy status-"+enyo.messaging.im.buddyAvailabilities[this.value]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,277 @@
|
||||
enyo.kind({
|
||||
name: "ImStatus",
|
||||
kind: "HFlexBox",
|
||||
events: {
|
||||
onLoginStatesChange: "",
|
||||
onAddAccount: ""
|
||||
},
|
||||
components: [
|
||||
{name: "availabilityContainer", flex:1, components: [
|
||||
{layoutKind: "HFlexLayout", onclick: "availabilityClicked", align:"center" , components: [
|
||||
{name: "status", className: "status status-buddy", style: "margin-top:-1px;margin-left:0px;"},
|
||||
{name: "spinner", className: "status-spinner", kind: "Spinner", style: "margin-left:-10px;", spinning: true, shownWhenSpinning: true},
|
||||
{name: "availability", content: "Offline", className:"availability-text enyo-text-ellipsis"},
|
||||
{name: "arrow", className: "enyo-listselector-arrow"}
|
||||
]}
|
||||
]},
|
||||
{name: "customStatusContainer", flex: 1, showing: false, components: [
|
||||
{name: "inputBox", kind:"InputBox", className:"enyo-middle", style:"width:320px;margin-left:-4px;",/*style:"-webkit-border-image:none;border-width:0;",*/ components: [
|
||||
{name: "customMessageStatus", style: "margin-top:2px;", className: "status status-buddy"},
|
||||
{name: "customMessage", kind: "Input", onblur: "customMessageBlur", onchange: "customMessageChanged", className:"custom-status-input", style:"margin-left:-20px;vertical-align:middle;", flex:1, styled: false, changeOnKeypress: true, selectAllOnFocus: true}
|
||||
]},
|
||||
{layoutKind: "HFlexLayout", style:"margin-left:-6px;padding-left: 10px;padding-right:10px;", components: [
|
||||
{name: "cancelButton", kind: "Button", flex: 1, caption: "Cancel", onclick: "cancelButtonClicked"},
|
||||
{name: "setButton", kind: "Button", flex: 1, className:"enyo-button-dark", caption: "Set Status", onclick: "setButtonClicked"}
|
||||
]}
|
||||
]},
|
||||
{name: "accounts", kind: "AccountLoginStatesPopup", onAvailabilitySet: "availabilitySet", onInputCustomMessage: "inputCustomMessage"},
|
||||
{name: "loginStateFinder", kind: "DbService", dbKind: enyo.messaging.imLoginState.dbKind, method: "find", onSuccess: "gotLoginStates", subscribe: true, resubscribe: true, reCallWatches: true},
|
||||
{name: "loginStateSetter", kind: "DbService", dbKind: enyo.messaging.imLoginState.dbKind, method: "merge", onSuccess: "changedLoginState"},
|
||||
{name: "networkAlerts", kind: "NetworkAlerts", onTap: "onNetworkAlertsTapFn"},
|
||||
{name: "mockDbFinder", kind: "MockDb", dbKind: "loginstates/com.palm.imloginstate:3", method: "find", onSuccess: "gotLoginStates", subscribe: true, resubscribe: true, reCallWatches: true}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.fetchLoginStates();
|
||||
},
|
||||
showHideStatusSpinner: function(inShow) {
|
||||
this.$.status.setShowing(!inShow);
|
||||
this.$.spinner.setShowing(inShow);
|
||||
this.$.availability.addRemoveClass("adjust-text-for-no-spinner", !inShow);
|
||||
},
|
||||
fetchLoginStates: function() {
|
||||
this.$.loginStateFinder.cancel();
|
||||
if (window.PalmSystem) {
|
||||
this.$.loginStateFinder.call();
|
||||
} else {
|
||||
this.$.mockDbFinder.call();
|
||||
}
|
||||
},
|
||||
gotLoginStates: function(inSender, inResponse) {
|
||||
this.loginStates = this.getLoginStates(inResponse);
|
||||
//enyo.log("%%%%%%%%%%%%%%% this.loginStates", JSON.stringify(this.loginStates));
|
||||
this.doLoginStatesChange(this.loginStates);
|
||||
|
||||
this.accountsState = enyo.messaging.imLoginState.getAggregatedLoginState(this.loginStates);
|
||||
this.setStatusIndicator(this.accountsState);
|
||||
this.$.accounts.setLoginStates(this.loginStates);
|
||||
this.$.accounts.setAccountsState(this.accountsState);
|
||||
},
|
||||
getLoginStates: function(inResponse) {
|
||||
if (!inResponse || !inResponse.results) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var loginStates = inResponse.results;
|
||||
for (var i = 0; i < loginStates.length; i++) {
|
||||
loginStates[i].accountTypeName = this.getAccountTypeName(loginStates[i]);
|
||||
// The check for type_yahoo can be removed once the supportsInvisibleStatus flag is added to the Yahoo account template
|
||||
loginStates[i].supportsInvisibleStatus = (this.supportsInvisibleStatus(loginStates[i]) || loginStates[i].serviceName === "type_yahoo");
|
||||
}
|
||||
loginStates.sort(function(a, b) {
|
||||
return a.accountTypeName > b.accountTypeName || (a.accountTypeName === b.accountTypeName && a.username > b.username);
|
||||
});
|
||||
|
||||
return loginStates;
|
||||
},
|
||||
getAccountTypeName: function(loginState) {
|
||||
//enyo.log("%%%%%%%%%%%% loginState: ", loginState);
|
||||
var account = enyo.application.accountService.getAccount(loginState.serviceName, loginState.username);
|
||||
return account ? account.loc_name : "";
|
||||
},
|
||||
supportsInvisibleStatus: function(loginState) {
|
||||
var account = enyo.application.accountService.getAccount(loginState.serviceName, loginState.username);
|
||||
return !!account && account.supportsInvisibleStatus;
|
||||
},
|
||||
changedLoginState: function(inSender, inResponse) {
|
||||
if (inResponse.count > 0) {
|
||||
this.showHideStatusSpinner(false);
|
||||
this.$.availability.setContent(this.status);
|
||||
this.fetchLoginStates();
|
||||
}
|
||||
},
|
||||
setStatusIndicator: function(accountsState) {
|
||||
if (!accountsState) {
|
||||
this.setStatusNoAccount();
|
||||
} else {
|
||||
this.setAllAccountsStatus(accountsState);
|
||||
}
|
||||
},
|
||||
setStatusNoAccount: function() {
|
||||
this.status = enyo.messaging.im.availabilityCaptions["offline"];
|
||||
this.showHideStatusSpinner(false);
|
||||
this.updateStatusClass("status-offline");
|
||||
this.$.availability.setContent(this.status);
|
||||
},
|
||||
setAllAccountsStatus: function(accountsState) {
|
||||
var currentStatus = enyo.messaging.im.availabilityClasses[accountsState.bestAvailability];
|
||||
this.status = enyo.messaging.im.availabilityCaptions[currentStatus];
|
||||
if (accountsState.hasPending) {
|
||||
this.setStatusPendingAccount();
|
||||
} else {
|
||||
this.setStatusHaveAccount(accountsState, currentStatus);
|
||||
}
|
||||
},
|
||||
setStatusPendingAccount: function() {
|
||||
this.showHideStatusSpinner(true);
|
||||
this.$.availability.setContent($L("Signing in..."));
|
||||
this.startLoginTimer();
|
||||
|
||||
// if AccountLoginStatesPopup is opened, close it.
|
||||
if (this.$.accounts.isOpen) {
|
||||
this.$.accounts.close();
|
||||
}
|
||||
},
|
||||
// This function can be used to get a sign in caption for individual or all accounts sign in.
|
||||
getSignInCaption: function() {
|
||||
var accountName = undefined;
|
||||
for (var i=0; i<this.loginStates.length; i++) {
|
||||
if (this.loginStates[i].state === enyo.messaging.imLoginState.TRANSPORT_STATE.LOGGING_ON ||
|
||||
this.loginStates[i].state === enyo.messaging.imLoginState.TRANSPORT_STATE.RETRIEVING_DATA) {
|
||||
if (accountName === undefined) {
|
||||
accountName = this.loginStates[i].accountTypeName;
|
||||
} else {
|
||||
return $L("Signing into all accounts...");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (accountName === undefined) {
|
||||
return $L("Signing in...");
|
||||
} else {
|
||||
//FIXME: the following line can't be localized correctly since it uses the wrong method for localization.
|
||||
return $L("Signing into "+accountName+"...");
|
||||
}
|
||||
},
|
||||
setStatusHaveAccount: function(accountsState, currentStatus) {
|
||||
this.showHideStatusSpinner(false);
|
||||
this.updateStatusClass(this.getStatusClass(currentStatus));
|
||||
if (accountsState.bestAvailability === enyo.messaging.im.availability.OFFLINE) {
|
||||
this.$.availability.setContent(this.status);
|
||||
} else if (!accountsState.identicalAvailabilities || !accountsState.identicalStates || !accountsState.identicalCustomMessages) {
|
||||
this.$.availability.setContent($L("Custom Availability"));
|
||||
} else {
|
||||
if (accountsState.customMessage) {
|
||||
this.$.availability.setContent(accountsState.customMessage);
|
||||
} else {
|
||||
this.$.availability.setContent(this.status);
|
||||
}
|
||||
}
|
||||
this.stopLoginTimer();
|
||||
},
|
||||
updateStatusClass: function(newStatusClass) {
|
||||
if (this.statusClass) {
|
||||
this.$.status.removeClass(this.statusClass);
|
||||
}
|
||||
this.statusClass = newStatusClass;
|
||||
this.$.status.addClass(this.statusClass);
|
||||
},
|
||||
startLoginTimer: function() {
|
||||
if (!this.loginTimer) {
|
||||
// login will timeout after 1.5 minutes. this is really long, but intended to handle transports failing
|
||||
this.loginTimer = setTimeout(function() {
|
||||
this.loginTimer = undefined;
|
||||
this.saveAvailability(enyo.messaging.im.availability.OFFLINE);
|
||||
}.bind(this), 90000);
|
||||
}
|
||||
},
|
||||
stopLoginTimer: function() {
|
||||
if (this.loginTimer) {
|
||||
clearTimeout(this.loginTimer);
|
||||
this.loginTimer = undefined;
|
||||
}
|
||||
},
|
||||
saveAvailability: function(value) {
|
||||
this.$.loginStateSetter.call({
|
||||
"props": {
|
||||
"availability": value
|
||||
},
|
||||
"query": {
|
||||
"from": "com.palm.imloginstate:1"
|
||||
}
|
||||
});
|
||||
},
|
||||
onNetworkAlertsTapFn:function(inSender, inResponse){
|
||||
enyo.log("ImStatus:onNetworkAlertsTapFn:inResponse:", inResponse);
|
||||
},
|
||||
availabilityClicked: function(inSender, inEvent) {
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
if (this.loginStates.length === 0) {
|
||||
this.doAddAccount();
|
||||
} else if (!this.$.spinner.getShowing()) {
|
||||
this.$.accounts.openAtControl(inSender);
|
||||
this.$.accounts.setLoginStates(this.loginStates);
|
||||
this.$.accounts.setAccountsState(this.accountsState);
|
||||
}
|
||||
},
|
||||
availabilitySet: function(inSender, db, loginState) {
|
||||
this.$.networkAlerts.push({type: "data"});
|
||||
db.updateAvailability(loginState.availability);
|
||||
},
|
||||
inputCustomMessage: function(inSender, db, loginState) {
|
||||
this.db = db;
|
||||
if (loginState._id === undefined) {
|
||||
var status = enyo.messaging.im.availabilityClasses[loginState.bestAvailability];
|
||||
this.updateCustomMessageStatusClass(this.getStatusClass(status));
|
||||
} else {
|
||||
this.updateCustomMessageStatusClass(this.getStatusClass(enyo.messaging.im.buddyAvailabilities[enyo.messaging.imLoginState.getAvailability(loginState)]));
|
||||
}
|
||||
if (this.accountsState.customMessage && this.accountsState.customMessage !== "") {
|
||||
this.$.customMessage.setValue(this.accountsState.customMessage);
|
||||
} else if (loginState.customMessage && loginState.customMessage !== "") {
|
||||
this.$.customMessage.setValue(loginState.customMessage);
|
||||
} else {
|
||||
this.$.customMessage.setValue("");
|
||||
}
|
||||
this.showCustomMessageInput(true);
|
||||
this.$.customMessage.forceFocus();
|
||||
},
|
||||
updateCustomMessageStatusClass: function(newStatusClass) {
|
||||
if (this.customMessageStatusClass) {
|
||||
this.$.customMessageStatus.removeClass(this.customMessageStatusClass);
|
||||
}
|
||||
this.customMessageStatusClass = newStatusClass;
|
||||
this.$.customMessageStatus.addClass(this.customMessageStatusClass);
|
||||
},
|
||||
customMessageBlur: function(saveMsg) {
|
||||
if (saveMsg === true) {
|
||||
this.db.updateCustomMessage(this.$.customMessage.getValue());
|
||||
this._handledCustomMessage();
|
||||
} else {
|
||||
this.showCustomMessageInput(false);
|
||||
}
|
||||
},
|
||||
customMessageChanged: function() {
|
||||
if(this.$.customMessage.hasFocus()) {
|
||||
this.customMessageBlur(true);
|
||||
}
|
||||
},
|
||||
cancelButtonClicked: function(inSender, inEvent) {
|
||||
this._handledCustomMessage();
|
||||
},
|
||||
setButtonClicked: function(inSender, inEvent) {
|
||||
this.customMessageBlur(true);
|
||||
},
|
||||
_handledCustomMessage: function() {
|
||||
this.$.customMessage.setValue("");
|
||||
this.$.customMessage.forceBlur();
|
||||
this.db = undefined;
|
||||
this.showCustomMessageInput(false);
|
||||
},
|
||||
showCustomMessageInput: function(show) {
|
||||
this.$.availabilityContainer.setShowing(!show);
|
||||
this.$.customStatusContainer.setShowing(show);
|
||||
},
|
||||
windowHiddenHandler: function() {
|
||||
if(this.$.customStatusContainer.showing) {
|
||||
this._handledCustomMessage();
|
||||
}
|
||||
},
|
||||
/***********************************
|
||||
* Functions below are unit tested *
|
||||
***********************************/
|
||||
getStatusClass: function(value) {
|
||||
return value && value !== "offline" ?
|
||||
"status-" + value :
|
||||
"status-offline";
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
enyo.kind({
|
||||
name: "BaseLoginStateDB",
|
||||
kind: "enyo.Component",
|
||||
published: {
|
||||
loginStates: []
|
||||
},
|
||||
events: {
|
||||
onMerged: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{name: "stateMerger", method: "merge", onSuccess: "doMerged"}
|
||||
]}
|
||||
],
|
||||
updateAvailability: function(avail) {
|
||||
this.$.stateMerger.call(this._getAvailabilityMergeString(avail));
|
||||
},
|
||||
updateCustomMessage: function(message) {
|
||||
var mergeItems = [];
|
||||
for(var i=0; i<this.loginStates.length; i++) {
|
||||
var loginState = this.loginStates[i];
|
||||
var availability = enyo.messaging.imLoginState.getAvailability(loginState);
|
||||
if(availability <= enyo.messaging.im.availability.BUSY) {
|
||||
mergeItems.push({_id: loginState._id, customMessage: message});
|
||||
}
|
||||
}
|
||||
if (mergeItems.length > 0) {
|
||||
this.$.stateMerger.call({objects: mergeItems});
|
||||
}
|
||||
},
|
||||
_getAvailabilityMergeString: function(avail) {
|
||||
return {query: {from: enyo.messaging.imLoginState.dbKind}, props: {availability: avail}};
|
||||
}
|
||||
});
|
||||
|
||||
enyo.kind({
|
||||
name: "AccountLoginStateDB",
|
||||
kind: BaseLoginStateDB,
|
||||
published: {
|
||||
accountId: undefined
|
||||
},
|
||||
updateCustomMessage: function(message) {
|
||||
this.$.stateMerger.call({objects: [{_id: this.accountId, customMessage: message}]});
|
||||
},
|
||||
_getAvailabilityMergeString: function(avail) {
|
||||
return {objects: [{_id: this.accountId, availability: avail}]};
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,164 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "PreferencesView",
|
||||
className: "enyo-bg",
|
||||
kind: "VFlexBox",
|
||||
published: {
|
||||
accounts: null
|
||||
},
|
||||
events: {
|
||||
onClosePreferences: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "Pane", flex:1, components: [
|
||||
{name: "prefsAndAccounts", kind: "VFlexBox", components: [
|
||||
{kind: "Toolbar", className:"enyo-toolbar-light accounts-header", pack:"center", components: [
|
||||
{kind: "Image", src: "images/messaging-48x48.png"},
|
||||
{kind: "Control", content: $L("Preferences & Accounts")}
|
||||
]},
|
||||
{className:"accounts-header-shadow"},
|
||||
|
||||
{kind: "Scroller",flex:1, components:[
|
||||
{kind:"Control", className:"box-center", components: [
|
||||
{kind: "RowGroup", caption: $L("New Message"), className:"accounts-group", components: [
|
||||
{layoutKind: "enyo.HFlexLayout", align: "center", tapHighlight: false, components: [
|
||||
{content: $L("Show Notifications"), flex: 1},
|
||||
{kind: "ToggleButton", onChange: "showNotificationChange"}
|
||||
]},
|
||||
{kind: "ListSelector", label: $L("ALERT"), onChange: "notificationTypeChange", items: [
|
||||
{caption: $L("Vibrate"), value:"vibrate"},
|
||||
{caption: $L("System Sound"), value: "system"},
|
||||
{caption: $L("Ringtone"), value: "ringtone"},
|
||||
{caption: $L("Mute"), value: "mute"}
|
||||
]},
|
||||
{tapHighlight:true, kind: "HFlexBox", onclick:"showRingtonePicker", components: [
|
||||
{name:"ringtoneName", content: $L("Tap to pick a ringtone"), flex: 1},
|
||||
{content: $L("ringtone"), style: "color: rgb(31, 117, 191);text-transform: uppercase; "}
|
||||
]}
|
||||
]},
|
||||
{name: "accountgroup", kind: "RowGroup", caption: $L("Accounts"), className:"accounts-group", components: [
|
||||
{kind: "Accounts.accountsList", name: "accountsList", onAccountsList_AccountSelected: "editAccount"}
|
||||
]},
|
||||
{kind: "Button", caption: AccountsUtil.BUTTON_ADD_ACCOUNT, onclick: "AddAccount", className:"accounts-btn"}/*,
|
||||
{kind: "ConnectPhone", onComplete: "exitAccountPrefs"}*/
|
||||
]}
|
||||
]},
|
||||
{className:"accounts-footer-shadow"},
|
||||
{kind:"Toolbar", className:"enyo-toolbar-light", components:[
|
||||
{kind: "Button", caption: $L("Done"), className:"enyo-button-dark accounts-toolbar-btn", onclick: "exitAccountPrefs"}
|
||||
]}
|
||||
]},
|
||||
|
||||
{kind: "AccountsUI", name: "accountsView", capability: "MESSAGING", onAccountsUI_Done: "accountsDone"},
|
||||
{kind: "AccountsModify", name: "accountsModify", capability: "MESSAGING", onAccountsModify_Done: "accountsDone"}
|
||||
]},
|
||||
{name: "ringtonepicker", kind: "FilePicker", fileType: ["ringtone"], onPickFile: "ringtoneChosen"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.$.rowGroup.hide();
|
||||
//this.$.connectPhone.subscriptToTelephony();
|
||||
},
|
||||
rendered: function(){
|
||||
this.$.accountsList.getAccountsList(this.capability, "com.palm.palmprofile");
|
||||
if (enyo.application.prefsHandler) {
|
||||
enyo.application.prefsHandler.register(this, this.prefsUpdated.bind(this));
|
||||
}
|
||||
},
|
||||
exitAccountPrefs: function(){
|
||||
this.doClosePreferences();
|
||||
},
|
||||
//account stuff
|
||||
capability: "MESSAGING",
|
||||
// "Add Account" button was tapped
|
||||
AddAccount: function(button) {
|
||||
this.$.accountsView.AddAccount(enyo.application.accountService.getAccountTemplates());
|
||||
this.$.pane.selectViewByName("accountsView");
|
||||
},
|
||||
// User tapped on account to edit
|
||||
editAccount: function(inSender, inResults) {
|
||||
this.$.accountsModify.ModifyAccount(inResults.account, inResults.template, this.capability);
|
||||
this.$.pane.selectViewByName("accountsModify");
|
||||
},
|
||||
// Go to the prefs and accounts view
|
||||
accountsDone: function(inSender, e) {
|
||||
this.$.pane.selectViewByName("prefsAndAccounts");
|
||||
},
|
||||
prefsUpdated: function(inPrefs) {
|
||||
//enyo.log("---------##### preferences view: " ,inPrefs);
|
||||
if (!inPrefs) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._prefs = inPrefs;
|
||||
this.updatePrefsUI();
|
||||
},
|
||||
updatePrefsUI: function(){
|
||||
this.$.toggleButton.setState(this._prefs.enableNotification);
|
||||
this.$.listSelector.setValue(this._prefs.notificationSound);
|
||||
if (this._prefs.enableNotification) {
|
||||
this._showNotification();
|
||||
}
|
||||
else {
|
||||
this._hideNotification();
|
||||
}
|
||||
this.$.rowGroup.show();
|
||||
},
|
||||
_hideNotification: function(){
|
||||
this.$.rowGroup.hideRow(1);
|
||||
this.$.rowGroup.hideRow(2);
|
||||
},
|
||||
_showNotification: function(){
|
||||
this.$.rowGroup.showRow(1);
|
||||
if (this._prefs.notificationSound === "ringtone") {
|
||||
if (this._prefs.ringtone && this._prefs.ringtone.name) {
|
||||
this.$.ringtoneName.setContent(this._prefs.ringtone.name);
|
||||
}
|
||||
this.$.rowGroup.showRow(2);
|
||||
}
|
||||
else {
|
||||
this.$.rowGroup.hideRow(2);
|
||||
}
|
||||
},
|
||||
showNotificationChange: function(inSender, inState){
|
||||
if(inState){
|
||||
this._showNotification();
|
||||
}
|
||||
else{
|
||||
this._hideNotification();
|
||||
}
|
||||
this._prefs.enableNotification = inState;
|
||||
//this._dbMerge({enableNotification:inState});
|
||||
this.mergePrefs();
|
||||
},
|
||||
notificationTypeChange: function(inSender, inNewValue, inOldValue){
|
||||
if(inNewValue === "ringtone"){
|
||||
this.$.rowGroup.showRow(2);
|
||||
}
|
||||
else{
|
||||
this.$.rowGroup.hideRow(2);
|
||||
}
|
||||
this._prefs.notificationSound = inNewValue;
|
||||
this.mergePrefs();
|
||||
//this._dbMerge({notificationSound:inNewValue});
|
||||
},
|
||||
mergePrefs: function() {
|
||||
enyo.application.prefsHandler.setPrefs(this._prefs);
|
||||
},
|
||||
showRingtonePicker: function(){
|
||||
this.$.ringtonepicker.pickFile();
|
||||
},
|
||||
ringtoneChosen: function(inSender, inRingtone){
|
||||
if (inRingtone.length > 0) {
|
||||
this.$.ringtoneName.setContent(inRingtone[0].name);
|
||||
this._prefs.ringtone.name = inRingtone[0].name;
|
||||
this._prefs.ringtone.fullPath = inRingtone[0].fullPath;
|
||||
this.mergePrefs();
|
||||
}
|
||||
},
|
||||
unloadHandler: function(){
|
||||
// enyo.log("-------unregistering PreferencesView from PrefsHandler");
|
||||
enyo.application.prefsHandler.unregister(this);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
enyo.kind({
|
||||
name: "PrefsHandler",
|
||||
kind: enyo.Component,
|
||||
published: {
|
||||
prefs: undefined
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", dbKind: "com.palm.app.messagingprefs:1", components: [
|
||||
{name: "prefsGetter", method: "find", onSuccess: "_gotPrefs", subscribe: true, resubscribe: true, reCallWatches: true},
|
||||
{name: "prefsPutter", method: "put"},
|
||||
{name: "prefsMerger", method: "merge"}
|
||||
]},
|
||||
{name: "mockDbFinder", kind: "MockDb", dbKind: "prefs/com.palm.app.messagingprefs:1", method: "find", onSuccess: "_gotPrefs", subscribe: true, resubscribe: true, reCallWatches: true}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this._getPrefs();
|
||||
this._notifier = new Notifier(this.prefs);
|
||||
},
|
||||
_getPrefs: function() {
|
||||
if (window.PalmSystem) {
|
||||
this.$.prefsGetter.cancel();
|
||||
this.$.prefsGetter.call();
|
||||
} else {
|
||||
this.$.mockDbFinder.call();
|
||||
}
|
||||
},
|
||||
_gotPrefs: function(inSender, inResponse){
|
||||
if (!inResponse || !inResponse.results || inResponse.results.length === 0) {
|
||||
// no preferences exist, so create a default preferences record in db.
|
||||
enyo.log("******** Messaging prefs doesn't exist in database, so create default prefs in database");
|
||||
|
||||
this.prefs = this._getDefaultPrefs();
|
||||
this._putPrefs();
|
||||
} else {
|
||||
this.prefs = inResponse.results[0];
|
||||
// remove db system properties
|
||||
delete this.prefs._kind;
|
||||
delete this.prefs._rev;
|
||||
delete this.prefs._sync;
|
||||
|
||||
// migrate first launch mode setting from preferences into account service
|
||||
if (this.prefs.firstUseMode !== undefined && this.prefs.firstUseMode !== null) {
|
||||
enyo.application.firstLaunchHandler.migratePrefsSettingIntoAccountService(this.prefs);
|
||||
}
|
||||
|
||||
this._notify();
|
||||
}
|
||||
},
|
||||
_getDefaultPrefs: function() {
|
||||
return {
|
||||
"_kind": "com.palm.app.messagingprefs:1",
|
||||
"enableNotification": true,
|
||||
"notificationSound":"system",
|
||||
"ringtone": {},
|
||||
"isHistoryViewSelected": true,
|
||||
"showOnlineBuddiesOnly": true,
|
||||
"useImmediateMmsRetrieval": true,
|
||||
"firstUseMode": true
|
||||
};
|
||||
},
|
||||
prefsChanged: function() {
|
||||
this.$.prefsMerger.call({
|
||||
objects: [this.prefs]
|
||||
});
|
||||
},
|
||||
_putPrefs: function() {
|
||||
this.$.prefsPutter.call({
|
||||
objects: [this.prefs]
|
||||
});
|
||||
},
|
||||
_notify: function() {
|
||||
//enyo.log("------------##### notifying prefs changes: ", this.prefs);
|
||||
this._notifier.notify(this.prefs);
|
||||
},
|
||||
register: function(listener, callback) {
|
||||
this._notifier.register(listener, callback);
|
||||
},
|
||||
unregister: function(listener) {
|
||||
this._notifier.unregister(listener);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
enyo.kind({
|
||||
name: "DeleteThreadService",
|
||||
kind: "enyo.Component",
|
||||
published: {
|
||||
id: null
|
||||
},
|
||||
components:[
|
||||
{name: "dbDelete", kind: "DbService", dbKind: "com.palm.db", method: "del"},
|
||||
{name: "dbMerge", kind: "DbService", dbKind: "com.palm.db", method: "merge"},
|
||||
{name: "messageServiceFind", kind: "DbService", dbKind: "com.palm.message:1", method: "find", onSuccess: "gotMessageForDelete"}
|
||||
],
|
||||
deleteThread: function() {
|
||||
if (!this.id) {
|
||||
enyo.log("Unable to delete chat thread since no id is provided");
|
||||
return;
|
||||
}
|
||||
|
||||
// Inform the DashboardManagers that this thread has been removed.
|
||||
enyo.application.messageDashboardManager.removeThread(this.id);
|
||||
enyo.application.inviteDashboardManager.removeThread(this.id);
|
||||
|
||||
this.$.dbDelete.call({
|
||||
ids: [this.id]
|
||||
});
|
||||
this.deleteMessageForThread();
|
||||
},
|
||||
deleteMessageForThread: function(){
|
||||
this.$.messageServiceFind.call({
|
||||
query: {
|
||||
where: [{
|
||||
prop: "conversations",
|
||||
op: "=",
|
||||
val: this.id
|
||||
}],
|
||||
select: [
|
||||
"_id",
|
||||
"_kind",
|
||||
"conversations"
|
||||
]
|
||||
}
|
||||
});
|
||||
},
|
||||
gotMessageForDelete: function(inSender, inResponse){
|
||||
if (inResponse.returnValue && inResponse.results && inResponse.results.length > 0) {
|
||||
var message;
|
||||
for (var i = 0; i < inResponse.results.length; i++) {
|
||||
message = inResponse.results[i];
|
||||
var params = this.getMergeParams(message);
|
||||
if (params && params.objects) {
|
||||
this.$.dbMerge.call(params);
|
||||
}
|
||||
else {
|
||||
if (message._kind === "com.palm.mmsmessage:1") {
|
||||
//todo: delete attachments for mms on phone
|
||||
//future = enyo.messaging.MMS.deleteAttachments(message._id);
|
||||
}
|
||||
|
||||
//delete message
|
||||
this.$.dbDelete.call({
|
||||
ids: [message._id]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/***********************************
|
||||
* Functions below are unit tested *
|
||||
***********************************/
|
||||
getMergeParams: function(message){
|
||||
if (message.conversations.length > 1) {
|
||||
//need merge the db by remove chatThreadId
|
||||
var newConversations = [];
|
||||
for (var j = 0; j < message.conversations.length; j++) {
|
||||
if (message.conversations[j] !== this.id) {
|
||||
newConversations.push(message.conversations[j]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
objects: [{
|
||||
"_id": message._id,
|
||||
"conversations": newConversations
|
||||
}]
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
/*globals enyo */
|
||||
|
||||
enyo.kind({
|
||||
name: "ThreadItem",
|
||||
kind: "SwipeableItem",
|
||||
confirmCaption: $L("Delete"),
|
||||
layoutKind: "HFlexLayout",
|
||||
components: [
|
||||
{components: [
|
||||
{className: "contact-image-border"},
|
||||
{name: "contactImage", kind: "Image", className: "contact-image"}
|
||||
]},
|
||||
{kind: "VFlexBox", className: "status-box", pack: "justify", align: "center", components: [
|
||||
{name: "status", className: "status"},
|
||||
{name: "outgoing", className: "sent-received"}
|
||||
]},
|
||||
{kind: "VFlexBox", className: "message-summary", flex: 1, components: [
|
||||
{layoutKind: "HFlexLayout", align: "center", className:"contact-name-box", components: [
|
||||
{name: "displayName", className: "contact-name"},
|
||||
{name: "unreadCount", className: "unread-count", showing: false}
|
||||
]},
|
||||
{name: "summary", className: "message-preview", allowHtml:true}
|
||||
]}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.addClass("contactItem");
|
||||
},
|
||||
updateStatus: function(inStatus) {
|
||||
this.$.status.setClassName(this.getStatusClassName(inStatus));
|
||||
},
|
||||
updateUnreadCount: function(inThread){
|
||||
var inUnreadCount = inThread.unreadCount;
|
||||
var hasUnread = !this.isThreadSelected(inThread) && Number(inUnreadCount) > 0;
|
||||
this.$.unreadCount.setContent(inUnreadCount || 0);
|
||||
this.$.unreadCount.setShowing(hasUnread);
|
||||
this.$.displayName.setClassName("contact-name" + (hasUnread ? " contact-name-unread" : ""));
|
||||
},
|
||||
updateContactImage: function(inPerson) {
|
||||
this.$.contactImage.setAttribute("src", enyo.messaging.person.getDisplayImage(inPerson));
|
||||
},
|
||||
updateOutgoing: function(inFlags) {
|
||||
this.$.outgoing.setClassName("sent-received " + (Boolean(inFlags && inFlags.outgoing) ? "message-outgoing" : ""));
|
||||
},
|
||||
setThread: function(inThread) {
|
||||
this.updateStatus(inThread.status);
|
||||
this.updateContactImage(inThread.person);
|
||||
this.updateUnreadCount(inThread);
|
||||
this.updateOutgoing(inThread.flags);
|
||||
|
||||
if (inThread.person) {
|
||||
var displayName = enyo.messaging.person.getDisplayName(inThread.person);
|
||||
this.$.displayName.setContent(displayName);
|
||||
}
|
||||
else {
|
||||
this.$.displayName.setContent(inThread.displayName ? inThread.displayName : inThread.replyAddress);
|
||||
}
|
||||
|
||||
var summary = this.getThreadSummary(inThread);
|
||||
this.$.summary.setContent(summary);
|
||||
},
|
||||
/***********************************
|
||||
* Functions below are unit tested *
|
||||
***********************************/
|
||||
getStatusClassName: function(inStatus) {
|
||||
var status = inStatus && (inStatus.offline ? "offline" : enyo.messaging.im.availabilityClasses[inStatus.availability]);
|
||||
var statusClass = !status ? "" : " status-" + status;
|
||||
return "status" + statusClass;
|
||||
},
|
||||
getThreadSummary: function(inThread){
|
||||
var summary;
|
||||
if (enyo.messaging.message.isMMSThread(inThread)) {
|
||||
summary = enyo.messaging.message.getMMSThreadSummary();
|
||||
} else if(inThread.summary){
|
||||
if (inThread.flags && inThread.flags.outgoing) {
|
||||
// only outgoing messages needs to be escaped since it is not
|
||||
// sanitized.
|
||||
summary = enyo.string.escapeHtml(inThread.summary);
|
||||
} else {
|
||||
summary = inThread.summary;
|
||||
}
|
||||
}
|
||||
else {
|
||||
summary = "";
|
||||
}
|
||||
return summary;
|
||||
},
|
||||
isThreadSelected: function(inThread) {
|
||||
return (enyo.application.selectedThread && inThread._id === enyo.application.selectedThread._id && enyo.application.messageDashboardManager.getAppDeactivated() === false);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,149 @@
|
||||
enyo.kind({
|
||||
name: "ThreadList",
|
||||
kind: "VFlexBox",
|
||||
events: {
|
||||
onSelectThread: "",
|
||||
onDeleteThread: "",
|
||||
onThreadLocked: "",
|
||||
onUnreadCountChanged: ""
|
||||
},
|
||||
components: [
|
||||
{kind: "ThreadService", onSuccess: "gotThreads", onWatch: "threadsWatch"},
|
||||
{name: "deleteService", kind: "DeleteThreadService"},
|
||||
{name: "search", kind: "SearchInput", hint: $L("Search"), className:"enyo-middle", onchange: "filterList", onCancel: "filterList", changeOnInput: true, autoCapitalize: "lowercase", onfocus: "disableKeyboardMannualMode"},
|
||||
{className:"header-shadow header-app-shadow"},
|
||||
{name: "emptyMessage", content: "", className:"messageTexts", showing: false},
|
||||
{flex: 1, name: "list", kind: "DbList", desc: true, onQuery: "listQuery", className:"messaging-listsDivider", onSetupRow: "listSetupRow", components: [
|
||||
{kind: "Divider", className:"threadList-Divider"},
|
||||
{kind: "ThreadItem", tapHighlight:true, onConfirm: "swipeDelete", onclick: "selectThread", onCancel: "disableKeyboardMannualMode"}
|
||||
]}
|
||||
],
|
||||
disableKeyboardMannualMode: function(){
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
},
|
||||
|
||||
filterList: function(inSender, inValue){
|
||||
this.filterString = this.$.search.getValue();
|
||||
this.$.list.punt();
|
||||
},
|
||||
listQuery: function(inSender, inQuery) {
|
||||
if (this.filterString && this.filterString.length > 0) {
|
||||
inQuery.where = [{prop: "flags.visible", op: "=", val: true},{prop: "displayName", op:"%", val:this.filterString, collate: "primary", "tokenize":"all"}];
|
||||
inQuery.search = true;
|
||||
}
|
||||
else{
|
||||
inQuery.where = [{
|
||||
prop: "flags.visible",
|
||||
op: "=",
|
||||
val: true
|
||||
}];
|
||||
}
|
||||
inQuery.orderBy = "timestamp";
|
||||
return this.$.threadService.call({query: inQuery});
|
||||
},
|
||||
gotThreads: function(inSender, inResponse, inRequest) {
|
||||
this.$.list.queryResponse(inResponse, inRequest);
|
||||
if ((inRequest.index === 0) && (inResponse.returnValue && inResponse.results && inResponse.results.length === 0)){
|
||||
if (this.filterString && this.filterString.length > 0) {
|
||||
// No threads found using search
|
||||
this.showEmptyMessage(enyo.messaging.CONSTANTS.NO_SEARCH_RESULTS);
|
||||
} else {
|
||||
this.showEmptyMessage($L("Your conversation list is empty."));
|
||||
}
|
||||
} else {
|
||||
this.$.emptyMessage.hide();
|
||||
}
|
||||
|
||||
var cnt = 0;
|
||||
if (inResponse && inResponse.results) {
|
||||
for (var i = 0; i < inResponse.results.length; i++) {
|
||||
var thread = inResponse.results[i];
|
||||
if (!enyo.application.selectedThread || thread._id !== enyo.application.selectedThread._id || enyo.application.messageDashboardManager.getAppDeactivated() === true) {
|
||||
cnt += thread.unreadCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
//enyo.log("***************** total unread count in thread list: ", cnt);
|
||||
this.doUnreadCountChanged(cnt);
|
||||
},
|
||||
showEmptyMessage: function(message) {
|
||||
this.$.emptyMessage.setContent(message);
|
||||
this.$.emptyMessage.show();
|
||||
},
|
||||
threadsWatch: function() {
|
||||
this.$.list.reset();
|
||||
},
|
||||
selectThread: function(inSender, inEvent) {
|
||||
this.selectedRecord = this.$.list.fetch(inEvent.rowIndex);
|
||||
if(enyo.application.selectedThread && this.selectedRecord && enyo.application.selectedThread._id === this.selectedRecord._id){
|
||||
//tap on current selected record, do nothing
|
||||
return;
|
||||
}
|
||||
if (this.selectedRecord) {
|
||||
this.$.list.refresh();
|
||||
this.doSelectThread(this.selectedRecord);
|
||||
}
|
||||
},
|
||||
setupDivider: function(inThread, inIndex) {
|
||||
var date = new Date(inThread.timestamp);
|
||||
var caption = BucketDateFormatter.getDateBucket(date);
|
||||
var pt = this.$.list.fetch(inIndex - 1);
|
||||
var previousCaption = pt && BucketDateFormatter.getDateBucket(new Date(pt.timestamp));
|
||||
var showDivider = caption != previousCaption;
|
||||
if (showDivider) {
|
||||
this.$.divider.setCaption(caption);
|
||||
this.$.threadItem.addClass("enyo-first");
|
||||
}
|
||||
this.$.divider.canGenerate = showDivider;
|
||||
// this.$.threadItem.applyStyle("border-top", Boolean(showDivider) ? "none" : "1px solid silver;");
|
||||
},
|
||||
listSetupRow: function(inSender, inThread, inIndex) {
|
||||
// Do not perform model changes in this rendering method
|
||||
if (this.selectedRecord && this.selectedRecord._id === inThread._id && this.selectedRecord.flags.locked !== inThread.flags.locked){
|
||||
this.doThreadLocked(inThread);
|
||||
}
|
||||
this.setupDivider(inThread, inIndex);
|
||||
this.$.threadItem.setThread(inThread);
|
||||
|
||||
// make to the row to be selected if it is selected by user
|
||||
this.$.threadItem.addRemoveClass("enyo-item-selected", this.selectedRecord && this.selectedRecord._id === inThread._id ? true : false);
|
||||
},
|
||||
swipeDelete: function(inSender, inIndex) {
|
||||
enyo.messaging.keyboard.setKeyboardAutoMode();
|
||||
var record = this.$.list.fetch(inIndex);
|
||||
if (record && record._id) {
|
||||
this.$.deleteService.setId(record._id);
|
||||
this.$.deleteService.deleteThread();
|
||||
this.doDeleteThread(record);
|
||||
}
|
||||
},
|
||||
setSelection: function(inChatThread){
|
||||
if (this.selectedRecord && inChatThread && this.selectedRecord._id === inChatThread._id) {
|
||||
//case selection didn't change, do nothing
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.selectedRecord = inChatThread;
|
||||
// if (!inChatThread) {
|
||||
this.$.list.refresh();
|
||||
// }
|
||||
}
|
||||
},
|
||||
updateList: function() {
|
||||
this.$.list.update();
|
||||
},
|
||||
resetList: function() {
|
||||
this.$.list.reset();
|
||||
},
|
||||
windowHiddenHandler: function(){
|
||||
// dump data buffer
|
||||
this.$.list.punt();
|
||||
// cancel service call
|
||||
//this.$.threadService.cancel();
|
||||
|
||||
// clear out filter search
|
||||
this.filterString = "";
|
||||
this.$.search.setValue(this.filterString);
|
||||
this.$.search.forceBlur();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,118 @@
|
||||
enyo.kind({
|
||||
name: "ThreadService",
|
||||
kind: enyo.Service,
|
||||
requestKind: "ThreadRequest",
|
||||
events: {
|
||||
onWatch: ""
|
||||
}});
|
||||
|
||||
enyo.kind({
|
||||
name: "ThreadRequest",
|
||||
kind: enyo.Request,
|
||||
events: {
|
||||
onWatch: "doWatch"
|
||||
},
|
||||
components: [
|
||||
{kind: "DbService", onFailure: "fail", components: [
|
||||
{dbKind: "com.palm.chatthread:1", onSuccess: "gotThreads", components:[
|
||||
{name: "threads", method: "find", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/},
|
||||
{name: "threadsSearch", method: "search", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/}
|
||||
]},
|
||||
{name: "people", dbKind: "com.palm.person:1", method: "find", onSuccess: "gotPeople", onFailure: "gotPeople", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/}
|
||||
]},
|
||||
{name: "status", kind: enyo.TempDbService, dbKind: "com.palm.imbuddystatus:1", method: "find", onSuccess: "gotStatus", subscribe: true, onWatch: "doWatch"/*, resubscribe: true, reCallWatches: true*/},
|
||||
{name: "mockThreads", kind: "MockDb", dbKind: "chatthreads/com.palm.chatthread:1", method: "find", onSuccess: "gotThreads", onFailure: "fail", onWatch: "doWatch"},
|
||||
{name: "mockBuddies", kind: "MockDb", dbKind: "buddies/com.palm.imbuddystatus:1", method: "find", onSuccess: "gotStatus", onFailure: "fail", onWatch: "doWatch"},
|
||||
{name: "mockPeople", kind: "MockDb", dbKind: "persons/com.palm.person:1", method: "find", onSuccess: "gotPeople", onFailure: "fail", onWatch: "doWatch"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
},
|
||||
initComponents: function() {
|
||||
this.inherited(arguments);
|
||||
if (!window.PalmSystem) {
|
||||
this.$.threads = this.$.mockThreads;
|
||||
this.$.buddies = this.$.mockBuddies;
|
||||
this.$.people = this.$.mockPeople;
|
||||
}
|
||||
},
|
||||
finish: function() {
|
||||
// don't destroy automatically, so we can see watches
|
||||
},
|
||||
call: function() {
|
||||
if (this.params && this.params.query && this.params.query.search) {
|
||||
this.params.query.search = undefined;
|
||||
this.$.threadsSearch.cancel();
|
||||
this.$.threadsSearch.call(this.params);
|
||||
}
|
||||
else{
|
||||
this.$.threads.cancel();
|
||||
this.$.threads.call(this.params);
|
||||
}
|
||||
},
|
||||
reply: function() {
|
||||
this.receive(this.data);
|
||||
},
|
||||
fail: function(inSender, inResponse) {
|
||||
enyo.log(inResponse);
|
||||
this.reply();
|
||||
},
|
||||
gotThreads: function(inSender, inResponse) {
|
||||
this.data = inResponse;
|
||||
this.assembleIds();
|
||||
//query people and status at same time, so the list will be updated which ever returned early, might cause to refresh list one more time though
|
||||
this.queryPeople();
|
||||
this.queryStatus();
|
||||
},
|
||||
assembleIds: function() {
|
||||
// acquire ids for merging person data
|
||||
this.pids = [];
|
||||
for (var i=0, d; d=this.data.results[i]; i++) {
|
||||
if (d.personId) {
|
||||
this.pids.push(d.personId);
|
||||
}
|
||||
}
|
||||
},
|
||||
queryPeople: function() {
|
||||
// query person data
|
||||
var where = [{
|
||||
prop: "_id",
|
||||
op: "=",
|
||||
val: this.pids
|
||||
}];
|
||||
|
||||
this.$.people.id = "threadRequest_people";
|
||||
this.$.people.cancel();
|
||||
this.$.people.call({query: {
|
||||
where: where,
|
||||
select: enyo.messaging.person.selectAttributes
|
||||
}});
|
||||
},
|
||||
gotPeople: function(inSender, inResponse){
|
||||
if (inResponse.results && inResponse.results.length > 0) {
|
||||
// merge linked data
|
||||
enyo.messaging.utils.joinData(this.data, inResponse.results, "_id", "personId", "person");
|
||||
}
|
||||
this.reply();
|
||||
},
|
||||
queryStatus: function() {
|
||||
// query buddy status data
|
||||
var where = [{
|
||||
prop: "personId",
|
||||
op: "=",
|
||||
val: this.pids
|
||||
}];
|
||||
this.$.status.id = "threadRequest_status";
|
||||
this.$.status.cancel();
|
||||
this.$.status.call({query: {
|
||||
where: where
|
||||
}});
|
||||
},
|
||||
gotStatus: function(inSender, inResponse){
|
||||
// merge linked data
|
||||
if (inResponse.results && inResponse.results.length > 0) {
|
||||
enyo.messaging.utils.joinData(this.data, inResponse.results, "personId", "personId", "status");
|
||||
}
|
||||
this.reply();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"id": "com.palm.app.messaging",
|
||||
"version": "3.0.6606",
|
||||
"uiRevision": 2,
|
||||
"vendor": "HP",
|
||||
"type": "web",
|
||||
"main": "nowindow.html",
|
||||
"noWindow": true,
|
||||
"removable": false,
|
||||
"title": "Messaging",
|
||||
"icon": "icon.png",
|
||||
"splashicon": "icon-256x256.png",
|
||||
"keywords": ["Text","SMS","MMS","IM","Instant","Chat"],
|
||||
"universalSearch":{
|
||||
"action":{
|
||||
"displayName": "New Message",
|
||||
"url":"com.palm.app.messaging",
|
||||
"launchParam":{"compose":{"messageText":"#{searchTerms}"}}
|
||||
}
|
||||
},
|
||||
"requiredPermissions": ["activities.manage", "applications.internal", "database", "database.internal", "networking", "networking.internal", "system", "services", "settings"]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
#cd trunk
|
||||
|
||||
source $HOME/.rvm/scripts/rvm && source .rvmrc
|
||||
|
||||
# install bundler if necessary
|
||||
gem list --local bundler | grep bundler || gem install bundler || exit 1
|
||||
|
||||
# debugging info
|
||||
echo USER=$USER && ruby --version && which ruby && which bundle
|
||||
|
||||
# conditionally install project gems from Gemfile
|
||||
bundle check || bundle install || exit 1
|
||||
|
||||
IS_CI_BOX=true bundle exec rake jasmine:ci
|
||||
@@ -0,0 +1,28 @@
|
||||
enyo.kind({
|
||||
name: "TelephonyService",
|
||||
kind: enyo.Component,
|
||||
connected: false,
|
||||
components: [
|
||||
{name: "telephony", kind: "PalmService", service: "palm://com.palm.telephony/", method: "platformQuery", onSuccess: "gotPlatformData", subscribe: true}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.$.telephony.call();
|
||||
this._notifier = new Notifier(this.connected);
|
||||
},
|
||||
gotPlatformData: function(inSender, inResponse) {
|
||||
enyo.log("------------ platformDate: ", inResponse);
|
||||
this.connected = inResponse && inResponse.extended && inResponse.extended.capabilities && inResponse.extended.capabilities.mapcenable;
|
||||
this._notify();
|
||||
},
|
||||
_notify: function() {
|
||||
//enyo.log("------------##### notifying phone connected changes: ", this.connected);
|
||||
this._notifier.notify(this.connected);
|
||||
},
|
||||
register: function(listener, callback) {
|
||||
this._notifier.register(listener, callback);
|
||||
},
|
||||
unregister: function(listener) {
|
||||
this._notifier.unregister(listener);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,256 @@
|
||||
enyo.kind({
|
||||
name: "accountService",
|
||||
kind: "PalmService",
|
||||
components: [
|
||||
{kind: "Accounts.getAccounts", name: "listAccounts", onGetAccounts_AccountsAvailable: "gotAccounts"}
|
||||
],
|
||||
create: function() {
|
||||
this.inherited(arguments);
|
||||
this.data = [];
|
||||
this._cleanedImAccounts = undefined;
|
||||
this._accountTypes = undefined;
|
||||
this._accountTemplates = undefined;
|
||||
this._notifier = new Notifier();
|
||||
},
|
||||
getCleanedImAccounts: function(){
|
||||
this.marshallCleanedImAccounts();
|
||||
return this._cleanedImAccounts;
|
||||
},
|
||||
marshallCleanedImAccounts: function(){
|
||||
// enyo.log("------------accountService::marshallCleanedImAccounts this._cleanedImAccounts: ", this._cleanedImAccounts);
|
||||
if (!this._cleanedImAccounts) {
|
||||
// enyo.log("------------accountService::marshallCleanedImAccounts::this.data:", this.data);
|
||||
if(!this.data || this.data.length === 0){
|
||||
enyo.warn("------------accountService::marshallCleanedImAccounts::should wait for app to load and get accounts info");
|
||||
return;
|
||||
}
|
||||
this._cleanedImAccounts = [];
|
||||
this._accountTypes = {};
|
||||
for (var i=0, a; a=this.data[i]; i++) {
|
||||
this.cleanAccount(a);
|
||||
}
|
||||
// enyo.log("------------accountService::marshallCleanedImAccounts before return, got: ", this._cleanedImAccounts.length);
|
||||
}
|
||||
},
|
||||
getChatWithNonBuddies: function(serviceName){
|
||||
var accountTypes = this.getMyAccountTypesHash();
|
||||
if(!accountTypes){
|
||||
return null;
|
||||
}
|
||||
var account = accountTypes[serviceName];
|
||||
if (account) {
|
||||
return account.chatWithNonBuddies;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getIcons: function(serviceName){
|
||||
var accountTypes = this.getMyAccountTypesHash();
|
||||
if(!accountTypes){
|
||||
return null;
|
||||
}
|
||||
var account = accountTypes[serviceName];
|
||||
if (account && account.icon) {
|
||||
return account.icon;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getDbKinds: function(serviceName) {
|
||||
var dbkind;
|
||||
var accountTypes = this.getMyAccountTypesHash();
|
||||
if(!accountTypes){
|
||||
return null;
|
||||
}
|
||||
var account = accountTypes[serviceName];
|
||||
if (account === undefined || account.dbkinds === undefined) {
|
||||
enyo.warn("accountService::getDbKinds::Warning, no account for ", serviceName, " returning empty object");
|
||||
return {};
|
||||
} else {
|
||||
return account.dbkinds;
|
||||
}
|
||||
},
|
||||
getAccountTemplates: function(){
|
||||
return this._accountTemplates;
|
||||
},
|
||||
cleanAccount: function(account) {
|
||||
id = account._id;
|
||||
for (var i=0, c; c = account.capabilityProviders[i]; i++) {
|
||||
if (c.capability === "MESSAGING") {
|
||||
this.cleanCapability(account, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
cleanCapability: function(account, cap) {
|
||||
cap.accountId = account._id; // "2+Mr",
|
||||
cap.templateId = account.templateId; // "com.palm.aol",
|
||||
cap.username = account.username; // "flyingsquirrel",
|
||||
cap.alias = account.alias; // "flyingsquirrel @ AIM",
|
||||
if (!cap.loc_name) {
|
||||
cap.loc_name = account.loc_name || "";
|
||||
}
|
||||
if (cap.loc_shortName === undefined) {
|
||||
//enyo.log("-------capability.loc_shortName undefined. using part of loc_name", cap.loc_name);
|
||||
cap.loc_shortName = cap.loc_name.substr(0, 5);
|
||||
}
|
||||
this._cleanedImAccounts.push(cap);
|
||||
//enyo.log("----------capability.serviceName:", cap.serviceName);
|
||||
if (cap.capabilitySubtype === "SMS") {
|
||||
//ensure that a serviceName is specified for the SMS capability
|
||||
cap.serviceName = "sms";
|
||||
this._accountTypes.sms = cap;
|
||||
this._accountTypes.mms = cap; // MMS is also this capability
|
||||
} else if (cap.serviceName !== undefined && this._accountTypes[cap.accountId] === undefined) {
|
||||
if (this._accountTypes[cap.serviceName] === undefined) {//for generic info such icons for service type
|
||||
this._accountTypes[cap.serviceName] = cap;
|
||||
}
|
||||
// enyo.log("----------add accountId:", cap.accountId, " to this._accountTypes[]");
|
||||
this._accountTypes[cap.accountId] = cap;
|
||||
}
|
||||
else{
|
||||
enyo.warn("Duplicate capabilities: ---------cap.serviceName should be undefined:", cap.serviceName, " this._accountTypes[cap.accontId] exist:", this._accountTypes[cap.accountId]);
|
||||
}
|
||||
},
|
||||
getAccount: function(serviceName, username){
|
||||
//enyo.log("---------acountService::getAccount:servicename:", serviceName, " username:", username, " this._clearnedImAccounts:", this._cleanedImAccounts.length)
|
||||
if (this._cleanedImAccounts) {
|
||||
// Phone numbers can have multiple servicenames so normalize it to "sms"
|
||||
if (enyo.messaging.utils.isTextMessage(serviceName)) {
|
||||
serviceName = "sms";
|
||||
}
|
||||
for (var i=0, a; a=this._cleanedImAccounts[i]; i++) {
|
||||
// enyo.log("--------------accountService::getAccount::account.serviceName:", a.serviceName);
|
||||
if (serviceName === a.serviceName) {
|
||||
//enyo.log("--------------accountService::getAccount::account:", a);
|
||||
if (username === undefined || username === this._cleanedImAccounts[i].username) {
|
||||
return this._cleanedImAccounts[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
getMyAccountTypesHash: function(){
|
||||
//enyo.log("---------acountService::getMyAccountTypesHash::this._accountTypes:", this._accountTypes);
|
||||
this.marshallCleanedImAccounts();
|
||||
return this._accountTypes;
|
||||
},
|
||||
getAccounts: function(){
|
||||
// enyo.log("-----------------messagingAccountService::getAccounts");
|
||||
//include com.palm.palmprofile here, so transportpicker will include sms
|
||||
if (window.PalmSystem || enyo.messaging.utils.getAppRootPath().indexOf("http") !== -1) {
|
||||
this.$.listAccounts.getAccounts({
|
||||
capability: "MESSAGING"
|
||||
});
|
||||
} else {
|
||||
this.getMockAccounts();
|
||||
}
|
||||
},
|
||||
gotAccounts: function(inSender, inResponse) {
|
||||
// enyo.log("-----------------messagingAccountService::gotAccounts inResponse.accounts.length:", inResponse.accounts.length);
|
||||
if (inResponse.accounts.length > 0) {
|
||||
this.data = inResponse.accounts;
|
||||
this._cleanedImAccounts = undefined;
|
||||
this._accountTypes = undefined;
|
||||
this.getCleanedImAccounts();
|
||||
}
|
||||
if(inResponse.templates){
|
||||
this._accountTemplates = inResponse.templates;
|
||||
}
|
||||
this.notify();
|
||||
},
|
||||
getMockAccounts: function() {
|
||||
var data = enyo.g11n.Utils.getNonLocaleFile({
|
||||
root: enyo.messaging.utils.getAppRootPath(),
|
||||
path: "mock/accounts/com.palm.messaging.accounts-1.json"
|
||||
});
|
||||
this.gotAccounts(this, data);
|
||||
},
|
||||
getImCommandKind: function(serviceName){
|
||||
var dbkinds = this.getDbKinds(serviceName);
|
||||
if (dbkinds.imcommand) {
|
||||
return dbkinds.imcommand;
|
||||
} else {
|
||||
enyo.warn("No imcommand dbkind for ", serviceName ,", so using default");
|
||||
return "com.palm.imcommand:1";
|
||||
}
|
||||
},
|
||||
getImAccounts: function() {
|
||||
var accounts = this.getCleanedImAccounts(); // may contain SMS account
|
||||
var ims = [];
|
||||
|
||||
if (accounts) {
|
||||
for (var i = 0; i < accounts.length; i++) {
|
||||
if (accounts[i].capabilitySubtype === "IM") {
|
||||
ims.push(accounts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// enyo.log("-----------------messagingAccountService::gotImAccounts: ", ims);
|
||||
return ims;
|
||||
},
|
||||
notify: function() {
|
||||
this._notifier.notify();
|
||||
},
|
||||
register: function(listener, callback) {
|
||||
this._notifier.register(listener, callback);
|
||||
},
|
||||
unregister: function(listener) {
|
||||
this._notifier.unregister(listener);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
IMAccounts:[]
|
||||
from capabilityProviders:
|
||||
"_id": "260",
|
||||
"capability": "MESSAGING",
|
||||
"id": "com.palm.yahoo.im",
|
||||
"capabilitySubtype": "IM",
|
||||
"dbkinds": {
|
||||
"imcommand": "com.palm.imcommand.yahoo:1",
|
||||
"immessage": "com.palm.immessage.yahoo:1"
|
||||
},
|
||||
"icon": {
|
||||
"loc_32x32": "\/usr\/palm\/public\/accounts\/com.palm.yahoo\/images\/yim-32x32.png",
|
||||
"loc_48x48": "\/usr\/palm\/public\/accounts\/com.palm.yahoo\/images\/yim-48x48.png",
|
||||
"splitter": "\/usr\/palm\/public\/accounts\/com.palm.yahoo\/images\/yim-32x32.png"
|
||||
},
|
||||
"implementation": "palm:\/\/com.palm.imyahoo\/",
|
||||
"invites": {
|
||||
"supportsCustomMessage": true
|
||||
},
|
||||
"loc_shortName": "Yahoo!",
|
||||
"onCredentialsChanged": "palm:\/\/com.palm.imyahoo\/onCredentialsChanged",
|
||||
"onEnabled": "palm:\/\/com.palm.imyahoo\/onEnabled",
|
||||
"serviceName": "type_yahoo"
|
||||
|
||||
from accounts:
|
||||
capability.accountId = account._id; // "2+Mr",
|
||||
capability.templateId = account.templateId; // "com.palm.aol",
|
||||
capability.username = account.username; // "flyingsquirrel",
|
||||
capability.alias = account.alias; // "flyingsquirrel @ AIM",
|
||||
|
||||
if (!capability.loc_name) {
|
||||
capability.loc_name = account.loc_name || "";
|
||||
}
|
||||
|
||||
if (capability.loc_shortName === undefined) {
|
||||
Mojo.Log.warn("capability.loc_shortName undefined. using part of loc_name", capability.loc_name);
|
||||
capability.loc_shortName = capability.loc_name.substr(0,5);
|
||||
}
|
||||
|
||||
this._cleanedImAccounts.push(capability);
|
||||
if (capability.capabilitySubtype === "SMS") {
|
||||
//ensure that a serviceName is specified for the SMS capability
|
||||
if (capability.serviceName !== "sms") {
|
||||
capability.serviceName = "sms";
|
||||
}
|
||||
this._accountTypes.sms = capability;
|
||||
this._accountTypes.mms = capability; // MMS is also this capability
|
||||
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,3 @@
|
||||
[0612/142510.117:ERROR:exit_code_watcher_win.cc(87)] Failed to wait for process exit or stop event
|
||||
[0612/142510.245:ERROR:registration_protocol_win.cc(131)] TransactNamedPipe: The pipe has been ended. (0x6D)
|
||||
[0612/142638.699:ERROR:registration_protocol_win.cc(131)] TransactNamedPipe: The pipe has been ended. (0x6D)
|
||||
@@ -0,0 +1,83 @@
|
||||
enyo.depends(
|
||||
"$enyo/palm/list2/",
|
||||
"$enyo/g11n/phone/",
|
||||
|
||||
"$enyo-lib/systemui/",
|
||||
"$enyo-lib/accounts/",
|
||||
"$enyo-lib/contactsui/",
|
||||
"$enyo-lib/addressing/",
|
||||
"$enyo-lib/networkalerts/",
|
||||
|
||||
"data/accountService.js",
|
||||
"data/TelephonyService.js",
|
||||
"utilities/utils.js",
|
||||
|
||||
"app/AppInit.js",
|
||||
"app/AddBuddyDialog.js",
|
||||
"app/ChatButton.js",
|
||||
"app/DefaultView.js",
|
||||
// "app/ImStatus.js",
|
||||
"app/MessagingApp.js",
|
||||
"app/Messaging.js",
|
||||
"app/PopupDialog.js",
|
||||
"app/ChatView.js",
|
||||
|
||||
"app/buddies/BuddyConstants.js",
|
||||
"app/buddies/BuddyItem.js",
|
||||
"app/buddies/BuddyList.js",
|
||||
"app/buddies/BuddyService.js",
|
||||
"app/buddies/DeleteBuddyService.js",
|
||||
|
||||
"app/compose/ComposeView.js",
|
||||
"app/compose/ConnectPhoneDialog.js",
|
||||
"app/compose/ImTransportList.js",
|
||||
|
||||
"app/conversations/BlockPersonService.js",
|
||||
"app/conversations/ConversationItem.js",
|
||||
"app/conversations/ConversationList.js",
|
||||
"app/conversations/ConversationService.js",
|
||||
"app/conversations/InviteResponseService.js",
|
||||
"app/conversations/TransportSelector.js",
|
||||
"app/conversations/transportPicker.js",
|
||||
|
||||
"app/dashboards/BannerThrottler.js",
|
||||
"app/dashboards/Class0AlertManager.js",
|
||||
"app/dashboards/DashboardManager.js",
|
||||
"app/dashboards/InviteDashboardManager.js",
|
||||
"app/dashboards/InviteWatcher.js",
|
||||
"app/dashboards/MessageDashboardManager.js",
|
||||
"app/dashboards/MessageDashboardUtil.js",
|
||||
|
||||
"app/firstlaunch/FirstLaunchConstants.js",
|
||||
"app/firstlaunch/ConnectPhone.js",
|
||||
"app/firstlaunch/FirstLaunch.js",
|
||||
"app/firstlaunch/FirstLaunchHandler.js",
|
||||
|
||||
"app/favorites/FavoriteConstants.js",
|
||||
"app/favorites/FavoriteItem.js",
|
||||
"app/favorites/FavoriteList.js",
|
||||
"app/favorites/FavoriteService.js",
|
||||
|
||||
"app/imstatus/AccountStatuses.js",
|
||||
"app/imstatus/AccountLoginState.js",
|
||||
"app/imstatus/AccountLoginStatesPopup.js",
|
||||
"app/imstatus/ImStatus.js",
|
||||
"app/imstatus/LoginStateServices.js",
|
||||
|
||||
"app/preferences/PrefsHandler.js",
|
||||
"app/preferences/PreferencesView.js",
|
||||
|
||||
"app/threads/DeleteThreadService.js",
|
||||
"app/threads/ThreadItem.js",
|
||||
"app/threads/ThreadList.js",
|
||||
"app/threads/ThreadService.js",
|
||||
|
||||
"utilities/UtilsFormatter.js",
|
||||
"utilities/BucketDateFormatter.js",
|
||||
"utilities/Notifier.js",
|
||||
"utilities/Launcher.js",
|
||||
|
||||
"stylesheets/app.css",
|
||||
"stylesheets/conversation.css",
|
||||
"stylesheets/preferences.css"
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"localEnyoRoot": "../",
|
||||
"ciEnyoRoot": "/usr/palm/frameworks/"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"logLevel": 0
|
||||
}
|
||||
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 1000 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 801 B |
|
After Width: | Height: | Size: 848 B |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 999 B |
|
After Width: | Height: | Size: 999 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.3 KiB |