Skip to content

Commit 768cc78

Browse files
committed
feat(paidtier): detect Free Tier, show "Upgrade" button
- control MynahUI from Flare server
1 parent 4192b63 commit 768cc78

File tree

15 files changed

+580
-36
lines changed

15 files changed

+580
-36
lines changed

chat-client/src/client/chat.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@ export const createChat = (
390390
promptInputOptionChange: (params: PromptInputOptionChangeParams) => {
391391
sendMessageToClient({ command: PROMPT_INPUT_OPTION_CHANGE_METHOD, params })
392392
},
393+
promptInputButtonClick: params => {
394+
// TODO
395+
sendMessageToClient({ command: BUTTON_CLICK_REQUEST_METHOD, params })
396+
},
393397
stopChatResponse: (tabId: string) => {
394398
sendMessageToClient({ command: STOP_CHAT_RESPONSE, params: { tabId } })
395399
},

chat-client/src/client/messager.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export interface OutboundChatApi {
9292
tabBarAction(params: TabBarActionParams): void
9393
onGetSerializedChat(requestId: string, result: GetSerializedChatResult | ErrorResult): void
9494
promptInputOptionChange(params: PromptInputOptionChangeParams): void
95+
promptInputButtonClick(params: ButtonClickParams): void
9596
stopChatResponse(tabId: string): void
9697
sendButtonClickEvent(params: ButtonClickParams): void
9798
onOpenSettings(settingKey: string): void
@@ -229,6 +230,10 @@ export class Messager {
229230
this.chatApi.promptInputOptionChange(params)
230231
}
231232

233+
onPromptInputButtonClick = (params: ButtonClickParams): void => {
234+
this.chatApi.promptInputButtonClick(params)
235+
}
236+
232237
onStopChatResponse = (tabId: string): void => {
233238
this.chatApi.stopChatResponse(tabId)
234239
}

chat-client/src/client/mynahUi.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ describe('MynahUI', () => {
6464
tabBarAction: sinon.stub(),
6565
onGetSerializedChat: sinon.stub(),
6666
promptInputOptionChange: sinon.stub(),
67+
promptInputButtonClick: sinon.stub(),
6768
stopChatResponse: sinon.stub(),
6869
sendButtonClickEvent: sinon.stub(),
6970
onOpenSettings: sinon.stub(),

chat-client/src/client/mynahUi.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
import { ChatHistory, ChatHistoryList } from './features/history'
5959
import { pairProgrammingModeOff, pairProgrammingModeOn, programmerModeCard } from './texts/pairProgramming'
6060
import { getModelSelectionChatItem } from './texts/modelSelection'
61+
import { paidTierSuccessCard, freeTierLimitReachedCard, upgradeQButton } from './texts/paidTier'
6162

6263
export interface InboundChatApi {
6364
addChatResponse(params: ChatResult, tabId: string, isPartialResult: boolean): void
@@ -488,6 +489,14 @@ export const createMynahUi = (
488489
}
489490
messager.onPromptInputOptionChange({ tabId, optionsValues })
490491
},
492+
onPromptInputButtonClick: (tabId, buttonId, eventId) => {
493+
const payload: ButtonClickParams = {
494+
tabId,
495+
messageId: 'not-a-message',
496+
buttonId: buttonId,
497+
}
498+
messager.onPromptInputButtonClick(payload)
499+
},
491500
onMessageDismiss: (tabId, messageId) => {
492501
if (messageId === programmerModeCard.messageId) {
493502
programmingModeCardActive = false
@@ -836,7 +845,42 @@ export const createMynahUi = (
836845
})
837846
}
838847

848+
/**
849+
* Adjusts the UI when the user changes to/from free-tier/paid-tier.
850+
* Shows a message if the user reaches free-tier limit.
851+
* Shows a message if the user just upgraded to paid-tier.
852+
*/
853+
const onPaidTierModeChange = (
854+
tabId: string,
855+
mode: 'paidtier' | 'paidtier-success' | 'freetier' | 'freetier-limit'
856+
) => {
857+
if (!['paidtier', 'paidtier-success', 'freetier', 'freetier-limit'].includes(mode)) {
858+
return // invalid mode
859+
}
860+
861+
tabId = tabId !== '' ? tabId : getOrCreateTabId()!
862+
863+
// Detect if the tab is already showing the "Upgrade Q" calls-to-action.
864+
const didShowLimitReached = mynahUi.getTabData(tabId)?.getStore()?.promptInputButtons?.[0] === upgradeQButton
865+
if (mode === 'freetier-limit' && !didShowLimitReached) {
866+
mynahUi.addChatItem(tabId, freeTierLimitReachedCard)
867+
} else if (mode === 'paidtier-success') {
868+
mynahUi.addChatItem(tabId, paidTierSuccessCard)
869+
}
870+
871+
mynahUi.updateStore(tabId, {
872+
promptInputButtons: mode === 'freetier-limit' ? [upgradeQButton] : [],
873+
promptInputDisabledState: mode === 'freetier-limit',
874+
})
875+
}
876+
839877
const updateChat = (params: ChatUpdateParams) => {
878+
// HACK: Special field sent by `agenticChatController.ts:setPaidTierMode()`.
879+
if ((params as any).paidTierMode) {
880+
onPaidTierModeChange(params.tabId, (params as any).paidTierMode as any)
881+
return
882+
}
883+
840884
const isChatLoading = params.state?.inProgress
841885
mynahUi.updateStore(params.tabId, {
842886
loadingChat: isChatLoading,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { ChatItem, ChatItemButton, ChatItemFormItem, ChatItemType, TextBasedFormItem } from '@aws/mynah-ui'
2+
3+
export const freeTierLimitReachedCard: ChatItem = {
4+
type: ChatItemType.ANSWER,
5+
title: 'FREE TIER LIMIT REACHED',
6+
header: {
7+
icon: 'q',
8+
iconStatus: 'primary',
9+
body: 'Upgrade to Amazon Q Pro',
10+
},
11+
messageId: 'freetier-limit',
12+
fullWidth: true,
13+
canBeDismissed: false,
14+
body: 'You have reached the free tier limit. Upgrade to Amazon Q Pro.\n\n[Learn More...](https://aws.amazon.com/q/pricing/)',
15+
}
16+
17+
export const paidTierSuccessCard: ChatItem = {
18+
type: ChatItemType.ANSWER,
19+
title: 'UPGRADED TO AMAZON Q PRO',
20+
header: {
21+
icon: 'q',
22+
iconStatus: 'primary',
23+
body: 'Welcome to Amazon Q Pro',
24+
status: {
25+
status: 'success',
26+
icon: 'q',
27+
text: 'Success',
28+
},
29+
},
30+
messageId: 'paidtier-success',
31+
fullWidth: true,
32+
canBeDismissed: true,
33+
body: 'Upgraded to Amazon Q Pro\n\n[Learn More...](https://aws.amazon.com/q/)',
34+
}
35+
36+
export const paidTierPromptInput: TextBasedFormItem = {
37+
placeholder: '111111111111',
38+
type: 'textinput',
39+
id: 'paid-tier',
40+
tooltip: 'Upgrade to Amazon Q Pro',
41+
value: 'true',
42+
icon: 'magic',
43+
}
44+
45+
export const paidTierStep0: ChatItem = {
46+
type: ChatItemType.DIRECTIVE,
47+
body: 'You have upgraded to Amazon Q Pro',
48+
}
49+
50+
export const paidTierStep1: ChatItem = {
51+
type: ChatItemType.DIRECTIVE,
52+
body: 'You have upgraded to Amazon Q Pro',
53+
}
54+
55+
export const upgradeQButton: ChatItemButton = {
56+
flash: 'once',
57+
fillState: 'hover',
58+
position: 'outside',
59+
id: 'upgrade-q',
60+
// https://github.com/aws/mynah-ui/blob/main/src/components/icon/icons/q.svg
61+
// https://github.com/aws/mynah-ui/blob/main/src/components/icon/icons/rocket.svg
62+
// icon: MynahIcons.Q,
63+
description: 'Upgrade to Amazon Q Pro',
64+
text: 'Upgrade Q',
65+
status: 'info',
66+
}

chat-client/src/client/withAdapter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const withAdapter = (
5757
onChatPromptProgressActionButtonClicked: addDefaultRouting('onChatPromptProgressActionButtonClicked'),
5858
onTabbedContentTabChange: addDefaultRouting('onTabbedContentTabChange'),
5959
onPromptInputOptionChange: addDefaultRouting('onPromptInputOptionChange'),
60+
onPromptInputButtonClick: addDefaultRouting('onPromptInputButtonClick'),
6061
onMessageDismiss: addDefaultRouting('onMessageDismiss'),
6162

6263
/**

chat-client/src/contracts/chatClientAdapter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface ChatEventHandler
3636
| 'onResetStore'
3737
| 'onReady'
3838
| 'onPromptInputOptionChange'
39+
| 'onPromptInputButtonClick'
3940
| 'onMessageDismiss'
4041
> {}
4142

0 commit comments

Comments
 (0)