Skip to content

Commit fbc6e48

Browse files
committed
WIP
- [ ] make computeds run all the time by updating getter - [ ] make relationshipMeta always return a descriptive value, and make field accessor never throw NotLoaded Note from LM: The issue I have run into is that if the field accessor doesn't throw, there is no way for the indexer to wait for the card to be fully loaded (currently we are catching NotLoaded errors when attempting to render a card for indexing) ef4: makes sense. we can make the relationship loading process register itself somehow. It's not unlike test waiters or fastboot deferred.
1 parent 8e1fe92 commit fbc6e48

File tree

7 files changed

+154
-129
lines changed

7 files changed

+154
-129
lines changed

Diff for: packages/base/card-api.gts

+54-44
Original file line numberDiff line numberDiff line change
@@ -367,14 +367,13 @@ function getter<CardT extends BaseDefConstructor>(
367367
cardTracking.get(instance);
368368

369369
if (field.computeVia) {
370-
let value = deserialized.get(field.name);
371-
if (isStaleValue(value)) {
372-
value = value.staleValue;
373-
} else if (!deserialized.has(field.name)) {
374-
value = field.computeVia.bind(instance)();
375-
deserialized.set(field.name, value);
370+
let result = field.computeVia.bind(instance)() as unknown as
371+
| undefined
372+
| BaseInstanceType<CardT>;
373+
if (result !== undefined) {
374+
return result;
376375
}
377-
return value;
376+
return field.emptyValue(instance);
378377
} else {
379378
if (deserialized.has(field.name)) {
380379
return deserialized.get(field.name);
@@ -787,11 +786,23 @@ class LinksTo<CardT extends CardDefConstructor> implements Field<CardT> {
787786
cardTracking.get(instance);
788787
let maybeNotLoaded = deserialized.get(this.name);
789788
if (isNotLoadedValue(maybeNotLoaded)) {
790-
throw new NotLoaded(instance, maybeNotLoaded.reference, this.name);
789+
return this.emptyValue(instance) as unknown as BaseInstanceType<CardT>;
790+
// throw new NotLoaded(instance, maybeNotLoaded.reference, this.name);
791791
}
792792
return getter(instance, this);
793793
}
794794

795+
getRelationshipMeta(instance: CardDef) {
796+
let deserialized = getDataBucket(instance);
797+
let maybeNotLoaded = deserialized.get(this.name);
798+
799+
if (isNotLoadedValue(maybeNotLoaded)) {
800+
return { type: 'not-loaded', reference: maybeNotLoaded.reference };
801+
} else {
802+
return { type: 'loaded', card: maybeNotLoaded ?? null };
803+
}
804+
}
805+
795806
queryableValue(instance: any, stack: CardDef[]): any {
796807
if (primitive in this.card) {
797808
throw new Error(
@@ -1099,22 +1110,40 @@ class LinksToMany<FieldT extends CardDefConstructor>
10991110
}
11001111

11011112
getter(instance: CardDef): BaseInstanceType<FieldT> {
1113+
// let deserialized = getDataBucket(instance);
1114+
// cardTracking.get(instance);
1115+
// let maybeNotLoaded = deserialized.get(this.name);
1116+
// if (maybeNotLoaded) {
1117+
// let notLoadedRefs: string[] = [];
1118+
// for (let entry of maybeNotLoaded) {
1119+
// if (isNotLoadedValue(entry)) {
1120+
// notLoadedRefs = [...notLoadedRefs, entry.reference];
1121+
// }
1122+
// }
1123+
// if (notLoadedRefs.length > 0) {
1124+
// throw new NotLoaded(instance, notLoadedRefs, this.name);
1125+
// }
1126+
// }
1127+
1128+
return getter(instance, this);
1129+
}
1130+
1131+
getRelationshipMeta(instance: CardDef) {
11021132
let deserialized = getDataBucket(instance);
1103-
cardTracking.get(instance);
11041133
let maybeNotLoaded = deserialized.get(this.name);
1105-
if (maybeNotLoaded) {
1106-
let notLoadedRefs: string[] = [];
1107-
for (let entry of maybeNotLoaded) {
1108-
if (isNotLoadedValue(entry)) {
1109-
notLoadedRefs = [...notLoadedRefs, entry.reference];
1110-
}
1111-
}
1112-
if (notLoadedRefs.length > 0) {
1113-
throw new NotLoaded(instance, notLoadedRefs, this.name);
1114-
}
1115-
}
11161134

1117-
return getter(instance, this);
1135+
if (!Array.isArray(maybeNotLoaded)) {
1136+
throw new Error(
1137+
`expected ${this.name} to be an array but was ${typeof maybeNotLoaded}`,
1138+
);
1139+
}
1140+
return maybeNotLoaded.map((rel) => {
1141+
if (isNotLoadedValue(rel)) {
1142+
return { type: 'not-loaded', reference: rel.reference };
1143+
} else {
1144+
return { type: 'loaded', card: rel ?? null };
1145+
}
1146+
});
11181147
}
11191148

11201149
queryableValue(instances: any[] | null, stack: CardDef[]): any[] | null {
@@ -2318,27 +2347,7 @@ export function relationshipMeta(
23182347
if (!(field.fieldType === 'linksTo' || field.fieldType === 'linksToMany')) {
23192348
return undefined;
23202349
}
2321-
let related = getter(instance, field) as CardDef; // only compound cards can be linksTo fields
2322-
if (field.fieldType === 'linksToMany') {
2323-
if (!Array.isArray(related)) {
2324-
throw new Error(
2325-
`expected ${fieldName} to be an array but was ${typeof related}`,
2326-
);
2327-
}
2328-
return related.map((rel) => {
2329-
if (isNotLoadedValue(rel)) {
2330-
return { type: 'not-loaded', reference: rel.reference };
2331-
} else {
2332-
return { type: 'loaded', card: rel ?? null };
2333-
}
2334-
});
2335-
}
2336-
2337-
if (isNotLoadedValue(related)) {
2338-
return { type: 'not-loaded', reference: related.reference };
2339-
} else {
2340-
return { type: 'loaded', card: related ?? null };
2341-
}
2350+
return field.getRelationshipMeta(instance);
23422351
}
23432352

23442353
function serializedGet<CardT extends BaseDefConstructor>(
@@ -2855,7 +2864,7 @@ function makeDescriptor<
28552864
}
28562865
}
28572866
notifySubscribers(this, field.name, value);
2858-
logger.log(recompute(this));
2867+
cardTracking.set(this, true);
28592868
};
28602869
}
28612870
if (field.description) {
@@ -3092,7 +3101,8 @@ export function getFields(
30923101
if (
30933102
opts?.usedFieldsOnly &&
30943103
!usedFields.includes(maybeFieldName) &&
3095-
!maybeField.isUsed
3104+
!maybeField.isUsed &&
3105+
!maybeField.computeVia
30963106
) {
30973107
return [];
30983108
}

Diff for: packages/base/room.gts

+41-50
Original file line numberDiff line numberDiff line change
@@ -330,12 +330,6 @@ interface RoomState {
330330
created?: number;
331331
}
332332

333-
// in addition to acting as a cache, this also ensures we have
334-
// triple equal equivalence for the interior cards of RoomField
335-
const eventCache = initSharedState(
336-
'eventCache',
337-
() => new WeakMap<RoomField, Map<string, MatrixEvent>>(),
338-
);
339333
const messageCache = initSharedState(
340334
'messageCache',
341335
() => new WeakMap<RoomField, Map<string, MessageField>>(),
@@ -353,39 +347,34 @@ const fragmentCache = initSharedState(
353347
() => new WeakMap<RoomField, Map<string, CardFragmentContent>>(),
354348
);
355349

350+
function newEvents(events: () => MatrixEvent[]): () => MatrixEvent[] {
351+
let seen = new Set<string>();
352+
return function () {
353+
return events().filter((e) => {
354+
if (seen.has(e.event_id)) {
355+
return false;
356+
}
357+
seen.add(e.event_id);
358+
return true;
359+
});
360+
};
361+
}
362+
356363
export class RoomField extends FieldDef {
357364
static displayName = 'Room';
358365

359366
// the only writeable field for this card should be the "events" field.
360367
// All other fields should derive from the "events" field.
361368
@field events = containsMany(MatrixEventField);
362369

363-
// This works well for synchronous computeds only
364-
@field newEvents = containsMany(MatrixEventField, {
365-
computeVia: function (this: RoomField) {
366-
let cache = eventCache.get(this);
367-
if (!cache) {
368-
cache = new Map();
369-
eventCache.set(this, cache);
370-
}
371-
let newEvents = new Map<string, MatrixEvent>();
372-
for (let event of this.events) {
373-
if (cache.has(event.event_id)) {
374-
continue;
375-
}
376-
cache.set(event.event_id, event);
377-
newEvents.set(event.event_id, event);
378-
}
379-
return [...newEvents.values()];
380-
},
381-
});
382-
383370
@field roomId = contains(StringField, {
384371
computeVia: function (this: RoomField) {
385372
return this.events.length > 0 ? this.events[0].room_id : undefined;
386373
},
387374
});
388375

376+
private nameEvents = newEvents(() => this.events);
377+
389378
@field name = contains(StringField, {
390379
computeVia: function (this: RoomField) {
391380
let roomState = roomStateCache.get(this);
@@ -394,9 +383,7 @@ export class RoomField extends FieldDef {
394383
roomStateCache.set(this, roomState);
395384
}
396385

397-
// Read from this.events instead of this.newEvents to avoid a race condition bug where
398-
// newEvents never returns the m.room.name while the event is present in events
399-
let events = this.events
386+
let events = this.nameEvents()
400387
.filter((e) => e.type === 'm.room.name')
401388
.sort(
402389
(a, b) => a.origin_server_ts - b.origin_server_ts,
@@ -409,6 +396,8 @@ export class RoomField extends FieldDef {
409396
},
410397
});
411398

399+
private creatorEvents = newEvents(() => this.events);
400+
412401
@field creator = contains(RoomMemberField, {
413402
computeVia: function (this: RoomField) {
414403
let roomState = roomStateCache.get(this);
@@ -420,9 +409,9 @@ export class RoomField extends FieldDef {
420409
if (creator) {
421410
return creator;
422411
}
423-
let event = this.newEvents.find((e) => e.type === 'm.room.create') as
424-
| RoomCreateEvent
425-
| undefined;
412+
let event = this.creatorEvents().find(
413+
(e) => e.type === 'm.room.create',
414+
) as RoomCreateEvent | undefined;
426415
if (event) {
427416
roomState.creator = upsertRoomMember({
428417
room: this,
@@ -433,6 +422,8 @@ export class RoomField extends FieldDef {
433422
},
434423
});
435424

425+
private createdEvents = newEvents(() => this.events);
426+
436427
@field created = contains(DateTimeField, {
437428
computeVia: function (this: RoomField) {
438429
let roomState = roomStateCache.get(this);
@@ -444,9 +435,9 @@ export class RoomField extends FieldDef {
444435
if (created != null) {
445436
return new Date(created);
446437
}
447-
let event = this.newEvents.find((e) => e.type === 'm.room.create') as
448-
| RoomCreateEvent
449-
| undefined;
438+
let event = this.createdEvents().find(
439+
(e) => e.type === 'm.room.create',
440+
) as RoomCreateEvent | undefined;
450441
if (event) {
451442
roomState.created = event.origin_server_ts;
452443
}
@@ -456,19 +447,23 @@ export class RoomField extends FieldDef {
456447
},
457448
});
458449

450+
private roomMemberEvents = newEvents(() => this.events);
451+
459452
@field roomMembers = containsMany(RoomMemberField, {
460453
computeVia: function (this: RoomField) {
454+
console.log('computing roomMembers');
461455
let roomMembers = roomMemberCache.get(this);
462456
if (!roomMembers) {
463457
roomMembers = new Map();
464458
roomMemberCache.set(this, roomMembers);
465459
}
466460

467-
for (let event of this.newEvents) {
461+
for (let event of this.roomMemberEvents()) {
468462
if (event.type !== 'm.room.member') {
469463
continue;
470464
}
471465
let userId = event.state_key;
466+
console.log('upsertRoomMember');
472467
upsertRoomMember({
473468
room: this,
474469
userId,
@@ -482,6 +477,8 @@ export class RoomField extends FieldDef {
482477
},
483478
});
484479

480+
private messageEvents = newEvents(() => this.events);
481+
485482
@field messages = containsMany(MessageField, {
486483
// since we are rendering this card without the isolated renderer, we cannot use
487484
// the rendering mechanism to test if a field is used or not, so we explicitely
@@ -502,8 +499,7 @@ export class RoomField extends FieldDef {
502499
messageCache.set(this, cache);
503500
}
504501
let index = cache.size;
505-
let newMessages = new Map<string, MessageField>();
506-
for (let event of this.events) {
502+
for (let event of this.messageEvents()) {
507503
if (event.type !== 'm.room.message') {
508504
continue;
509505
}
@@ -611,26 +607,17 @@ export class RoomField extends FieldDef {
611607
if (messageField) {
612608
// if the message is a replacement for other messages,
613609
// use `created` from the oldest one.
614-
if (newMessages.has(event_id)) {
615-
messageField.created = newMessages.get(event_id)!.created;
610+
if (cache.has(event_id)) {
611+
messageField.created = cache.get(event_id)!.created;
616612
}
617-
newMessages.set(
613+
cache.set(
618614
(event.content as CardMessageContent).clientGeneratedId ?? event_id,
619615
messageField,
620616
);
621617
index++;
622618
}
623619
}
624620

625-
// update the cache with the new messages
626-
for (let [id, message] of newMessages) {
627-
// The `id` can either be an `eventId` or `clientGeneratedId`.
628-
// For messages sent by the user, we prefer to use `clientGeneratedId`
629-
// because `eventId` can change in certain scenarios,
630-
// such as when resending a failed message or updating its status from sending to sent.
631-
cache.set(id, message);
632-
}
633-
634621
// this sort should hopefully be very optimized since events will
635622
// be close to chronological order
636623
return [...cache.values()].sort(
@@ -641,12 +628,16 @@ export class RoomField extends FieldDef {
641628

642629
@field joinedMembers = containsMany(RoomMemberField, {
643630
computeVia: function (this: RoomField) {
631+
console.log('computing joinedMembers');
632+
console.log('this.roomMembers', this.roomMembers);
644633
return this.roomMembers.filter((m) => m.membership === 'join');
645634
},
646635
});
647636

648637
@field invitedMembers = containsMany(RoomMemberField, {
649638
computeVia: function (this: RoomField) {
639+
console.log('computing invitedMembers');
640+
console.log('this.roomMembers', this.roomMembers);
650641
return this.roomMembers.filter((m) => m.membership === 'invite');
651642
},
652643
});

Diff for: packages/host/app/components/ai-assistant/panel.gts

+9-1
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,9 @@ export default class AiAssistantPanel extends Component<Signature> {
375375
@cached
376376
private get aiSessionRooms() {
377377
let rooms: RoomField[] = [];
378-
for (let resource of this.roomResources.values()) {
378+
let roomResources = this.roomResources.values();
379+
console.log('roomResources', roomResources);
380+
for (let resource of roomResources) {
379381
if (!resource.room) {
380382
continue;
381383
}
@@ -385,12 +387,18 @@ export default class AiAssistantPanel extends Component<Signature> {
385387
// rooms don't immediately have a created date
386388
room.created = new Date();
387389
}
390+
console.log('this.matrixService.userId', this.matrixService.userId);
391+
console.log('room.invitedMembers', room.invitedMembers);
392+
console.log('room.joinedMembers', room.joinedMembers);
388393
if (
389394
(room.invitedMembers.find((m) => aiBotUserId === m.userId) ||
390395
room.joinedMembers.find((m) => aiBotUserId === m.userId)) &&
391396
room.joinedMembers.find((m) => this.matrixService.userId === m.userId)
392397
) {
398+
console.log('pushing room to result set', room);
393399
rooms.push(room);
400+
} else {
401+
console.log('excluding room from result set', room);
394402
}
395403
}
396404
// sort in reverse chronological order of last activity

Diff for: packages/host/tests/integration/components/operator-mode-test.gts

-1
Original file line numberDiff line numberDiff line change
@@ -1697,7 +1697,6 @@ module('Integration | operator-mode', function (hooks) {
16971697
await waitFor(`[data-test-open-ai-assistant]`);
16981698
await click('[data-test-open-ai-assistant]');
16991699
await waitFor(`[data-room-settled]`);
1700-
17011700
assert
17021701
.dom(`[data-test-room="${room3Id}"]`)
17031702
.exists(

0 commit comments

Comments
 (0)