Skip to content

Commit ba926bf

Browse files
committed
Merge branch 'main' into fix-priority-field
2 parents febc15f + e00154f commit ba926bf

37 files changed

+1054
-526
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,13 @@ psql -h localhost -p 5435 -U postgres
134134

135135
When the realm server starts up it will automatically run DB migrations that live in the `packages/realm-server/migrations` folder. As part of development you may wish to run migrations manually as well as to create a new migration.
136136

137-
To create a new migration, from `packages/realm-server`, execute:
137+
To create a new migration, from `packages/postgres`, execute:
138138

139139
```
140140
pnpm migrate create name-of-migration
141141
```
142142

143-
This creates a new migration file in `packages/realm-server/migrations`. You can then edit the newly created migration file with the details of your migration. We use `node-pg-migrate` to handle our migrations. You can find the API at https://salsita.github.io/node-pg-migrate.
143+
This creates a new migration file in `packages/postgres/migrations`. You can then edit the newly created migration file with the details of your migration. We use `node-pg-migrate` to handle our migrations. You can find the API at https://salsita.github.io/node-pg-migrate.
144144

145145
To run the migration, execute:
146146

@@ -154,7 +154,7 @@ To revert the migration, execute:
154154
pnpm migrate down
155155
```
156156

157-
Boxel also uses SQLite in order to run the DB in the browser as part of running browser tests (and eventually we may run the realm server in the browser to provide a local index). We treat the Postgres database schema as the source of truth and derive the SQLite schema from it. Therefore, once you author and apply a migration, you should generate a new schema SQL file for SQLite. To generate a new SQLite schema, from `packages/realm-server`, execute:
157+
Boxel also uses SQLite in order to run the DB in the browser as part of running browser tests (and eventually we may run the realm server in the browser to provide a local index). We treat the Postgres database schema as the source of truth and derive the SQLite schema from it. Therefore, once you author and apply a migration, you should generate a new schema SQL file for SQLite. To generate a new SQLite schema, from `packages/postgres`, execute:
158158

159159
```
160160
pnpm make-schema

packages/billing/stripe-webhook-handlers/checkout-session-completed.ts

+20-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { type DBAdapter } from '@cardstack/runtime-common';
1+
import { decodeWebSafeBase64, type DBAdapter } from '@cardstack/runtime-common';
22
import {
33
addToCreditsLedger,
44
getCurrentActiveSubscription,
55
getMostRecentSubscriptionCycle,
6+
getUserByMatrixUserId,
67
getUserByStripeId,
78
insertStripeEvent,
89
markStripeEventAsProcessed,
@@ -28,23 +29,31 @@ export async function handleCheckoutSessionCompleted(
2829
await txManager.withTransaction(async () => {
2930
await insertStripeEvent(dbAdapter, event);
3031

31-
const stripeCustomerId = event.data.object.customer;
32-
const matrixUserName = event.data.object.client_reference_id;
32+
let stripeCustomerId = event.data.object.customer;
33+
let encodedMatrixId = event.data.object.client_reference_id;
34+
let matrixUserId = decodeWebSafeBase64(encodedMatrixId);
3335

34-
if (matrixUserName) {
35-
// The matrix user id was encoded to be alphanumeric by replacing + with - and / with _
36-
// Now we need to reverse that encoding to get back the original base64 string
37-
const base64MatrixUserId = matrixUserName
38-
.replace(/-/g, '+')
39-
.replace(/_/g, '/');
40-
const matrixUserId = Buffer.from(base64MatrixUserId, 'base64').toString(
41-
'utf8',
36+
if (!matrixUserId) {
37+
throw new Error(
38+
'No matrix user id found in checkout session completed event - this should be populated using client_reference_id query param in the payment link',
4239
);
40+
}
41+
42+
// Stripe customer id will be present when user is subscribing to the free plan, but not when they are adding extra credits
43+
if (stripeCustomerId) {
4344
await updateUserStripeCustomerId(
4445
dbAdapter,
4546
matrixUserId,
4647
stripeCustomerId,
4748
);
49+
} else {
50+
let user = await getUserByMatrixUserId(dbAdapter, matrixUserId);
51+
52+
if (!user) {
53+
throw new Error(`User not found for matrix user id: ${matrixUserId}`);
54+
}
55+
56+
stripeCustomerId = user.stripeCustomerId;
4857
}
4958

5059
let creditReloadAmount =

packages/billing/stripe-webhook-handlers/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export type StripeCheckoutSessionCompletedWebhookEvent = StripeEvent & {
7979
id: string;
8080
object: 'checkout.session';
8181
client_reference_id: string;
82-
customer: string;
82+
customer: string | null; // string when payment link is for subscribing to the free plan, null when buying extra credits
8383
metadata:
8484
| {
8585
credit_reload_amount: string;

packages/boxel-ui/addon/src/components/avatar/index.gts

+22-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { htmlSafe } from '@ember/template';
12
import Component from '@glimmer/component';
23

34
import { getContrastColor } from '../../helpers/contrast-color.ts';
@@ -8,32 +9,49 @@ interface Signature {
89
Args: {
910
displayName?: string;
1011
isReady: boolean;
12+
thumbnailURL?: string;
1113
userId: string;
1214
};
1315
Element: HTMLDivElement;
1416
}
1517

18+
const setBackgroundImage = (backgroundURL: string | null | undefined) => {
19+
if (!backgroundURL) {
20+
return;
21+
}
22+
return htmlSafe(`background-image: url(${backgroundURL});`);
23+
};
24+
1625
export default class Avatar extends Component<Signature> {
1726
<template>
1827
{{#let (deterministicColorFromString @userId) as |bgColor|}}
1928
<div
2029
class='profile-icon'
21-
style={{cssVar
22-
profile-avatar-icon-background=bgColor
23-
profile-avatar-text-color=(getContrastColor bgColor)
30+
style={{if
31+
@thumbnailURL
32+
(setBackgroundImage @thumbnailURL)
33+
(cssVar
34+
profile-avatar-icon-background=bgColor
35+
profile-avatar-text-color=(getContrastColor bgColor)
36+
)
2437
}}
2538
data-test-profile-icon={{@isReady}}
2639
data-test-profile-icon-userId={{@userId}}
40+
role={{if @thumbnailURL 'img' 'presentation'}}
2741
aria-label={{if @displayName @displayName @userId}}
2842
...attributes
2943
>
30-
{{this.profileInitials}}
44+
{{#unless @thumbnailURL}}
45+
{{this.profileInitials}}
46+
{{/unless}}
3147
</div>
3248
{{/let}}
3349
<style scoped>
3450
.profile-icon {
3551
--icon-size: var(--profile-avatar-icon-size, 40px);
3652
background: var(--profile-avatar-icon-background, var(--boxel-dark));
53+
background-position: center;
54+
background-size: cover;
3755
border-radius: 50%;
3856
border: var(--profile-avatar-icon-border, 2px solid white);
3957
display: inline-flex;

packages/boxel-ui/addon/src/components/avatar/usage.gts

+9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import Avatar from './index.gts';
1313
export default class AvatarUsage extends Component {
1414
@tracked userId = 'user123';
1515
@tracked displayName = 'John Doe';
16+
@tracked thumbnailURL =
17+
'https://images.pexels.com/photos/4571943/pexels-photo-4571943.jpeg?auto=compress&cs=tinysrgb&w=300&h=300&dpr=1';
1618
@tracked isReady = true;
1719

1820
@cssVariable({ cssClassName: 'avatar-freestyle-container' })
@@ -39,6 +41,7 @@ export default class AvatarUsage extends Component {
3941
<Avatar
4042
@userId={{this.userId}}
4143
@displayName={{this.displayName}}
44+
@thumbnailURL={{this.thumbnailURL}}
4245
@isReady={{this.isReady}}
4346
/>
4447
</:example>
@@ -57,6 +60,12 @@ export default class AvatarUsage extends Component {
5760
@value={{this.displayName}}
5861
@onInput={{fn (mut this.displayName)}}
5962
/>
63+
<Args.String
64+
@name='thumbnailURL'
65+
@description='URL of the user thumbnail'
66+
@value={{this.thumbnailURL}}
67+
@onInput={{fn (mut this.thumbnailURL)}}
68+
/>
6069
<Args.Bool
6170
@name='isReady'
6271
@required={{true}}

packages/boxel-ui/addon/src/helpers/deterministic-color-from-string.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
// Selects a random color from a set of colors based on the input string
1010
export function deterministicColorFromString(str: string): string {
1111
if (!str) {
12-
return 'transparent';
12+
return '#EEEEEE';
1313
}
1414

1515
// Generate hash value between 0-1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"data": {
3+
"type": "card",
4+
"attributes": {
5+
"name": "Cardstack Studio Malaysia",
6+
"industry": "Computer Programming",
7+
"headquartersAddress": {
8+
"addressLine1": "Lot 3A-01A, Level 3A",
9+
"addressLine2": " Glo Damansara Shopping Mall",
10+
"city": "Kuala Lumpur",
11+
"state": null,
12+
"postalCode": "60000",
13+
"country": {
14+
"name": "Malaysia",
15+
"code": "MY"
16+
},
17+
"poBoxNumber": null
18+
},
19+
"phone": 60102345234,
20+
"website": "https://cardstack.com/",
21+
"stockSymbol": null,
22+
"description": null,
23+
"thumbnailURL": null
24+
},
25+
"meta": {
26+
"adoptsFrom": {
27+
"module": "../crm/company",
28+
"name": "Company"
29+
}
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"data": {
3+
"type": "card",
4+
"attributes": {
5+
"name": "Innovative Tech Solutions",
6+
"industry": "Technology",
7+
"headquartersAddress": {
8+
"addressLine1": "123 Innovation Drive",
9+
"addressLine2": "Suite 400",
10+
"city": "San Francisco",
11+
"state": "California",
12+
"postalCode": "94105",
13+
"country": {
14+
"name": "United States",
15+
"code": "US"
16+
},
17+
"poBoxNumber": null
18+
},
19+
"phone": "+14155552671",
20+
"website": "https://innovativetechsolutions.com/",
21+
"stockSymbol": "ITS",
22+
"description": "A leading provider of cutting-edge AI and cloud solutions for enterprises worldwide.",
23+
"thumbnailURL": null
24+
},
25+
"meta": {
26+
"adoptsFrom": {
27+
"module": "../crm/company",
28+
"name": "Company"
29+
}
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"data": {
3+
"type": "card",
4+
"attributes": {
5+
"name": "Tech Innovators Japan",
6+
"industry": "Technology",
7+
"headquartersAddress": {
8+
"addressLine1": "2-1-1 Otemachi",
9+
"addressLine2": "Chiyoda-ku",
10+
"city": "Tokyo",
11+
"state": null,
12+
"postalCode": "100-0004",
13+
"country": {
14+
"name": "Japan",
15+
"code": "JP"
16+
},
17+
"poBoxNumber": null
18+
},
19+
"phone": 81312345678,
20+
"website": "https://techinnovators.jp/",
21+
"stockSymbol": "",
22+
"description": "",
23+
"thumbnailURL": ""
24+
},
25+
"meta": {
26+
"adoptsFrom": {
27+
"module": "../crm/company",
28+
"name": "Company"
29+
}
30+
}
31+
}
32+
}

packages/experiments-realm/Map/20ad25ba-de18-40dc-979e-4603f62b5a2b.json

+12-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22
"data": {
33
"type": "card",
44
"attributes": {
5-
"address": "Livarska Ulica 10, Ljubljana, Slovenia",
5+
"address": {
6+
"addressLine1": "Livarska Ulica 10",
7+
"addressLine2": null,
8+
"city": null,
9+
"state": null,
10+
"postalCode": null,
11+
"country": {
12+
"name": "Slovenia",
13+
"code": "SI"
14+
},
15+
"poBoxNumber": null
16+
},
617
"mapWidth": 600,
718
"mapHeight": 400,
819
"title": null,

packages/experiments-realm/Person/1.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
"isCool": false,
88
"isHuman": true,
99
"address": {
10-
"streetAddress": null,
10+
"addressLine1": null,
1111
"city": null,
12-
"region": null,
12+
"state": null,
1313
"postalCode": null,
1414
"poBoxNumber": null,
1515
"country": null

packages/experiments-realm/TaskBase/c0bf3231-435c-4363-ad92-a1d756aea8d9.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@
3838
}
3939
}
4040
}
41-
}
41+
}

0 commit comments

Comments
 (0)