Skip to content

Commit 0090aff

Browse files
committed
Copy over updates and fixes from Fastmail repo
1 parent 213ee70 commit 0090aff

12 files changed

+172
-79
lines changed

source/api/Connection.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -660,9 +660,14 @@ const Connection = Class({
660660
handler = handlers[ response[0] ];
661661
if ( handler ) {
662662
id = response[2];
663-
request = remoteCalls[+id];
663+
request = id && remoteCalls[+id] || null;
664664
try {
665-
handler.call( this, response[1], request[0], request[1] );
665+
handler.call(
666+
this,
667+
response[1],
668+
request ? request[0] : '',
669+
request ? request[1] : {}
670+
);
666671
} catch ( error ) {
667672
RunLoop.didError( error );
668673
}
@@ -1155,11 +1160,21 @@ const Connection = Class({
11551160
},
11561161

11571162
didFetchUpdates: function ( Type, args, hasDataForUpdated ) {
1163+
var created = args.created;
1164+
var updated = null;
1165+
if ( !hasDataForUpdated ) {
1166+
if ( updated ) {
1167+
updated = args.updated;
1168+
}
1169+
if ( created ) {
1170+
updated = updated ? updated.concat( created ) : created;
1171+
}
1172+
}
11581173
this.get( 'store' )
11591174
.sourceDidFetchUpdates(
11601175
args.accountId,
11611176
Type,
1162-
hasDataForUpdated ? null : args.updated || null,
1177+
updated,
11631178
args.destroyed || null,
11641179
args.oldState,
11651180
args.newState

source/api/Sequence.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@
1010

1111
( function ( JMAP ) {
1212

13+
const Class = O.Class;
14+
const Obj = O.Object;
15+
16+
// ---
17+
1318
const noop = function () {};
1419

15-
const Sequence = O.Class({
20+
const Sequence = Class({
1621

17-
Extends: O.Object,
22+
Extends: Obj,
1823

1924
init: function () {
2025
this.queue = [];

source/calendar/CalendarEvent.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ const CalendarEvent = Class({
234234

235235
// locale: attr( String ),
236236
// localizations: attr( Object ),
237+
// NOTE: If adding support for localizations, you need to handle this in
238+
// the mayPatchKey function too, because you can't patch a patch.
237239

238240
// keywords: attr( Object ),
239241
// categories: attr( Object ),
@@ -765,9 +767,11 @@ const normaliseRecurrenceRule = function ( recurrenceRuleJSON ) {
765767
}
766768
};
767769

768-
const mayPatchKey = function ( path, original, current ) {
769-
if ( path.startsWith( 'recurrenceOverrides/' ) &&
770-
( original.excluded || current.excluded ) ) {
770+
const mayPatchKey = function ( path/*, original, current*/ ) {
771+
// We can't patch inside a patch as there's no way to distinguish whether
772+
// patch { "recurrenceOverrides/2000-01-01T00:00:00/key~1bar": null } is
773+
// deleting "bar" from the master or deleting the key~1bar patch.
774+
if ( path.startsWith( 'recurrenceOverrides/' ) ) {
771775
return false;
772776
}
773777
return true;

source/contacts/AmbiguousDate.js

+21-18
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,27 @@
99

1010
( function ( JMAP ) {
1111

12-
const AmbiguousDate = O.Class({
12+
const i18n = O.i18n;
1313

14-
init: function ( day, month, year ) {
14+
// ---
15+
16+
class AmbiguousDate {
17+
constructor ( day, month, year ) {
1518
this.day = day || 0;
1619
this.month = month || 0;
1720
this.year = year || 0;
18-
},
21+
}
1922

20-
toJSON: function () {
23+
toJSON () {
2124
return "%'04n-%'02n-%'02n".format(
2225
this.year, this.month, this.day );
23-
},
26+
}
2427

25-
hasValue: function () {
28+
hasValue () {
2629
return !!( this.day || this.month || this.year );
27-
},
30+
}
2831

29-
yearsAgo: function () {
32+
yearsAgo () {
3033
if ( !this.year ) { return -1; }
3134
var now = new Date(),
3235
ago = now.getFullYear() - this.year,
@@ -37,17 +40,17 @@ const AmbiguousDate = O.Class({
3740
ago -= 1;
3841
}
3942
return ago;
40-
},
43+
}
4144

42-
prettyPrint: function () {
45+
prettyPrint () {
4346
var day = this.day,
4447
month = this.month,
4548
year = this.year,
46-
dateElementOrder = O.i18n.get( 'dateElementOrder' ),
49+
dateElementOrder = i18n.get( 'dateElementOrder' ),
4750
dayString = day ?
4851
day + ( year && dateElementOrder === 'mdy' ? ', ' : ' ' ) : '',
4952
monthString = month ?
50-
O.i18n.get( 'monthNames' )[ month - 1 ] + ' ' : '',
53+
i18n.get( 'monthNames' )[ month - 1 ] + ' ' : '',
5154
yearString = year ? year + ' ' : '';
5255

5356
return (
@@ -58,13 +61,13 @@ const AmbiguousDate = O.Class({
5861
( dayString + monthString + yearString )
5962
).trim();
6063
}
61-
});
6264

63-
AmbiguousDate.fromJSON = function ( json ) {
64-
var parts = /^(\d{4})-(\d{2})-(\d{2})$/.exec( json || '' );
65-
return parts ?
66-
new AmbiguousDate( +parts[3], +parts[2], +parts[1] ) : null;
67-
};
65+
static fromJSON ( json ) {
66+
var parts = /^(\d{4})-(\d{2})-(\d{2})$/.exec( json || '' );
67+
return parts ?
68+
new AmbiguousDate( +parts[3], +parts[2], +parts[1] ) : null;
69+
}
70+
}
6871

6972
JMAP.AmbiguousDate = AmbiguousDate;
7073

source/contacts/ContactGroup.js

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const ContactGroup = Class({
5050
contacts: Record.toMany({
5151
recordType: Contact,
5252
key: 'contactIds',
53+
isNullable: false,
5354
defaultValue: [],
5455
willSet: function () {
5556
return true;

source/contacts/contacts-model.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const StoreUndoManager = O.StoreUndoManager;
1919

2020
const Contact = JMAP.Contact;
2121
const ContactGroup = JMAP.ContactGroup;
22+
const auth = JMAP.auth;
23+
const CONTACTS_DATA = JMAP.auth.CONTACTS_DATA;
2224
const store = JMAP.store;
2325
const contacts = JMAP.contacts;
2426

@@ -102,13 +104,18 @@ const vips = new Obj({
102104
getGroup: function ( storeForContact, createIfNotFound ) {
103105
var group = this._group;
104106
var groupCacheState = this._groupCacheState;
107+
var primaryAccountId;
105108
if ( groupCacheState === CACHED && ( group || !createIfNotFound ) ) {
106109
// Nothing to do
107110
} else if ( groupCacheState === REVALIDATE &&
108111
group && group.is( READY ) ) {
109112
this._groupCacheState = CACHED;
110113
} else {
111-
group = store.getOne( ContactGroup, data => data.uid === 'vips' );
114+
primaryAccountId = auth.get( 'primaryAccounts' )[ CONTACTS_DATA ];
115+
group = store.getOne( ContactGroup, data =>
116+
data.accountId === primaryAccountId &&
117+
data.uid === 'vips'
118+
);
112119
if ( !group && createIfNotFound ) {
113120
group = new ContactGroup( store )
114121
.set( 'name', loc( 'VIPS' ) )

source/mail/Mailbox.js

+16-6
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,25 @@ const makeSetRequest = JMAP.Connection.makeSetRequest;
2626

2727
// ---
2828

29+
const roleSortOrder = {
30+
inbox: 1,
31+
snoozed: 2,
32+
archive: 3,
33+
drafts: 4,
34+
xtemplates: 5,
35+
sent: 6,
36+
junk: 7,
37+
trash: 8,
38+
all: 9,
39+
};
40+
2941
const bySortOrderRoleOrName = function ( a, b ) {
30-
var aRole = a.role;
31-
var bRole = b.role;
3242
return (
3343
a.sortOrder - b.sortOrder
3444
) || (
35-
aRole === 'inbox' ? -1 :
36-
bRole === 'inbox' ? 1 :
37-
aRole && !bRole ? -1 :
38-
bRole && !aRole ? 1 :
45+
( roleSortOrder[ a.role ] || 99 ) -
46+
( roleSortOrder[ b.role ] || 99 )
47+
) || (
3948
i18n.compare( a.name, b.name )
4049
) || (
4150
a.id < b.id ? -1 : 1
@@ -337,6 +346,7 @@ connection.handle( Mailbox, {
337346
this.didCommit( Mailbox, args );
338347
},
339348
});
349+
Mailbox.roleSortOrder = roleSortOrder;
340350
Mailbox.bySortOrderRoleOrName = bySortOrderRoleOrName;
341351

342352
// --- Export

source/mail/Message.js

+22-27
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,6 @@ const mail = JMAP.mail;
2727

2828
// ---
2929

30-
const bodyType = {
31-
'text/plain': 'text',
32-
'text/enriched': 'text',
33-
'text/richtext': 'text',
34-
'text/html': 'html',
35-
};
36-
3730
const parseStructure = function ( parts, multipartType, inAlternative,
3831
htmlParts, textParts, fileParts ) {
3932

@@ -45,13 +38,13 @@ const parseStructure = function ( parts, multipartType, inAlternative,
4538
for ( i = 0; i < parts.length; i += 1 ) {
4639
var part = parts[i];
4740
var type = part.type;
48-
var isTextOrHTML = false;
41+
var isText = false;
4942
var isMultipart = false;
5043
var isImage = false;
5144
var isInline, subMultiType;
5245

53-
if ( bodyType[ type ] ) {
54-
isTextOrHTML = true;
46+
if ( type.startsWith( 'text/' ) ) {
47+
isText = true;
5548
} else if ( type.startsWith( 'multipart/' ) ) {
5649
isMultipart = true;
5750
} else if ( type.startsWith( 'image/' ) ) {
@@ -61,7 +54,7 @@ const parseStructure = function ( parts, multipartType, inAlternative,
6154
// Is this a body part rather than an attachment
6255
isInline =
6356
// Must be one of the allowed body types
64-
( isTextOrHTML || isImage ) &&
57+
( isText || isImage ) && type !== 'text/calendar' &&
6558
// Must not be explicitly marked as an attachment
6659
part.disposition !== 'attachment' &&
6760
// If multipart/related, only the first part can be inline
@@ -77,26 +70,26 @@ const parseStructure = function ( parts, multipartType, inAlternative,
7770
htmlParts, textParts, fileParts );
7871
} else if ( isInline ) {
7972
if ( multipartType === 'alternative' ) {
80-
switch ( bodyType[ type ] ) {
81-
case 'text':
82-
textParts.push( part );
83-
break;
84-
case 'html':
73+
if ( type === 'text/html' ) {
8574
htmlParts.push( part );
86-
break;
87-
default:
75+
} else if ( isText && textParts.length === textLength ) {
76+
textParts.push( part );
77+
} else if ( type === 'text/plain' ) {
78+
// We've found a text/plain but already chose a text part.
79+
// Replace it and move the other part to files instead.
80+
fileParts.push( textParts.pop() );
81+
textParts.push( part );
82+
} else {
8883
fileParts.push( part );
89-
break;
9084
}
9185
continue;
9286
} else if ( inAlternative ) {
93-
switch ( bodyType[ type ] ) {
94-
case 'text':
95-
htmlParts = null;
96-
break;
97-
case 'html':
98-
textParts = null;
99-
break;
87+
if ( isText ) {
88+
if ( type === 'text/html' ) {
89+
textParts = null;
90+
} else {
91+
htmlParts = null;
92+
}
10093
}
10194
}
10295
if ( textParts ) {
@@ -163,6 +156,7 @@ const Message = Class({
163156
recordType: Mailbox,
164157
key: 'mailboxIds',
165158
Type: Object,
159+
isNullable: false,
166160
}),
167161

168162
keywords: attr( Object, {
@@ -358,7 +352,8 @@ const Message = Class({
358352

359353
hasTextBody: function () {
360354
return this.get( 'bodyParts' ).text.some( function ( part ) {
361-
return bodyType[ part.type ] === 'text';
355+
const type = part.type;
356+
return type.startsWith( 'text/' ) && type !== 'text/html';
362357
});
363358
}.property( 'bodyParts' ),
364359

0 commit comments

Comments
 (0)