@indexnetwork/protocol 66.0.0-rc.621.1 → 66.0.0-rc.623.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,6 +29,8 @@ export interface OpportunityCard {
29
29
  };
30
30
  /** Viewer's role in this opportunity (e.g. 'party', 'agent', 'patient', 'peer'). */
31
31
  viewerRole?: string;
32
+ /** This viewer already committed. Status can still be pending until the other person accepts. */
33
+ viewerCommitted?: boolean;
32
34
  /**
33
35
  * True for a skeleton card: identity fields are real but mainText/cta are
34
36
  * empty because the presenter was skipped. Never cached.
@@ -6,7 +6,7 @@
6
6
  import { protocolLogger } from '../shared/observability/protocol.logger.js';
7
7
  import { buildOpportunityCardCacheKey, gatherPresenterContext } from './opportunity.presentation.js';
8
8
  import { loadNegotiationContext } from './negotiation-context.loader.js';
9
- import { canUserSeeOpportunity, isActionableForViewer } from './opportunity.utils.js';
9
+ import { canUserSeeOpportunity } from './opportunity.utils.js';
10
10
  import { getPrimaryActionLabel, SECONDARY_ACTION_LABEL } from './opportunity.labels.js';
11
11
  const logger = protocolLogger('OpportunityCards');
12
12
  const PRESENTATION_CONCURRENCY = 50;
@@ -30,6 +30,12 @@ function pickDisplayCounterpartActor(opportunity, viewerId) {
30
30
  function counterpartUserIds(opportunity, viewerId) {
31
31
  return new Set(opportunity.actors.map((a) => a.userId).filter((id) => id && id !== viewerId));
32
32
  }
33
+ function viewerHasCommitted(opportunity, viewerId) {
34
+ return (opportunity.committedActorIds ?? []).includes(viewerId);
35
+ }
36
+ function stampViewerCommitted(card, viewerCommitted) {
37
+ return { ...card, viewerCommitted };
38
+ }
33
39
  /**
34
40
  * Present one opportunity as a card for the viewer.
35
41
  *
@@ -46,13 +52,14 @@ export async function presentOpportunityCard(deps, opportunity, viewerId, option
46
52
  const counterpart = pickDisplayCounterpartActor(opportunity, viewerId);
47
53
  if (!counterpart)
48
54
  return null;
55
+ const viewerCommitted = viewerHasCommitted(opportunity, viewerId);
49
56
  const cacheable = opportunity.status !== 'negotiating';
50
57
  const cacheKey = buildOpportunityCardCacheKey(opportunity.id, viewerId, options.intentId);
51
58
  if (cacheable && !options.skeleton && !options.noCache) {
52
59
  try {
53
60
  const hit = await deps.cache.get(cacheKey);
54
61
  if (hit)
55
- return { ...hit, status: opportunity.status };
62
+ return stampViewerCommitted({ ...hit, status: opportunity.status }, viewerCommitted);
56
63
  }
57
64
  catch (error) {
58
65
  logger.warn('card cache read failed, presenting', { opportunityId: opportunity.id, error });
@@ -78,7 +85,7 @@ export async function presentOpportunityCard(deps, opportunity, viewerId, option
78
85
  viewerRole,
79
86
  };
80
87
  if (options.skeleton) {
81
- return { ...identity, mainText: '', cta: '', mutualIntentsLabel: 'Shared interests', presentationPending: true };
88
+ return stampViewerCommitted({ ...identity, mainText: '', cta: '', mutualIntentsLabel: 'Shared interests', presentationPending: true }, viewerCommitted);
82
89
  }
83
90
  const [context, negotiationContext] = await Promise.all([
84
91
  gatherPresenterContext(database, opportunity, viewerId, counterpart.userId, options.intentId),
@@ -89,14 +96,14 @@ export async function presentOpportunityCard(deps, opportunity, viewerId, option
89
96
  opportunityStatus: opportunity.status,
90
97
  ...(negotiationContext ? { negotiationContext } : {}),
91
98
  });
92
- const card = {
99
+ const card = stampViewerCommitted({
93
100
  ...identity,
94
101
  mainText: presentation.personalizedSummary,
95
102
  cta: presentation.suggestedAction,
96
103
  headline: presentation.headline,
97
104
  mutualIntentsLabel: presentation.mutualIntentsLabel,
98
105
  narratorChip: { name: 'Index', text: presentation.narratorRemark },
99
- };
106
+ }, viewerCommitted);
100
107
  if (cacheable) {
101
108
  await deps.cache.set(cacheKey, card).catch((error) => {
102
109
  logger.warn('card cache write failed', { opportunityId: opportunity.id, error });
@@ -124,8 +131,6 @@ export async function listOpportunityCards(deps, input) {
124
131
  const requested = new Set(input.statuses);
125
132
  const visible = raw
126
133
  .filter((opp) => requested.has(opp.status) && canUserSeeOpportunity(opp.actors, opp.status, viewerId))
127
- .filter((opp) => opp.status !== 'pending'
128
- || isActionableForViewer(opp.actors, opp.status, viewerId, opp.committedActorIds ?? []))
129
134
  .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
130
135
  const seen = new Set();
131
136
  const opportunities = visible.filter((opp) => {
@@ -146,7 +151,7 @@ export async function listOpportunityCards(deps, input) {
146
151
  cacheableOpps.forEach((opp, i) => {
147
152
  const hit = hits[i];
148
153
  if (hit)
149
- cached.set(opp.id, { ...hit, status: opp.status });
154
+ cached.set(opp.id, stampViewerCommitted({ ...hit, status: opp.status }, viewerHasCommitted(opp, viewerId)));
150
155
  });
151
156
  }
152
157
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indexnetwork/protocol",
3
- "version": "66.0.0-rc.621.1",
3
+ "version": "66.0.0-rc.623.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",