Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit43fc0ef

Browse files
author
Bogdan Tsechoev
committed
Merge branch 'chats_email_notifications' into 'master'
Send email notifications from AISee merge request postgres-ai/database-lab!945
2 parents4575b4a +f2cddf6 commit43fc0ef

File tree

13 files changed

+327
-54
lines changed

13 files changed

+327
-54
lines changed

‎ui/packages/platform/src/actions/actions.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const Actions = Reflux.createActions([{
3838
ASYNC_ACTION:ASYNC_ACTION,
3939
doAuth:ASYNC_ACTION,
4040
getUserProfile:ASYNC_ACTION,
41+
updateUserProfile:ASYNC_ACTION,
4142
getAccessTokens:ASYNC_ACTION,
4243
getAccessToken:ASYNC_ACTION,
4344
hideGeneratedAccessToken:{},
@@ -267,6 +268,42 @@ Actions.getUserProfile.listen(function (token) {
267268
);
268269
});
269270

271+
Actions.updateUserProfile.listen(function(token,data){
272+
letaction=this;
273+
274+
if(!api){
275+
settings.init(function(){
276+
api=newApi(settings);
277+
});
278+
}
279+
280+
this.progressed();
281+
282+
timeoutPromise(REQUEST_TIMEOUT,api.updateUserProfile(token,data))
283+
.then(result=>{
284+
result.json()
285+
.then(json=>{
286+
if(json){
287+
action.completed({data:json?.result});
288+
}else{
289+
action.failed(newError('wrong_reply'));
290+
}
291+
})
292+
.catch(err=>{
293+
console.error(err);
294+
action.failed(newError('wrong_reply'));
295+
});
296+
})
297+
.catch(err=>{
298+
console.error(err);
299+
if(err&&err.message&&err.message==='timeout'){
300+
action.failed(newError('failed_fetch'));
301+
}else{
302+
action.failed(newError('wrong_reply'));
303+
}
304+
});
305+
});
306+
270307
Actions.getAccessTokens.listen(function(token,orgId){
271308
letaction=this;
272309

‎ui/packages/platform/src/api/api.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,31 @@ class Api {
112112
});
113113
}
114114

115+
updateUserProfile(token,data){
116+
letheaders={
117+
Authorization:'Bearer '+token,
118+
Accept:'application/vnd.pgrst.object+json'
119+
};
120+
121+
letbody={};
122+
123+
if(data.is_chats_email_notifications_enabled!=='undefined'){
124+
body.chats_email_notifications_enabled=data.is_chats_email_notifications_enabled;
125+
}
126+
127+
if(data.first_name!=='undefined'){
128+
body.first_name=data.first_name;
129+
}
130+
131+
if(data.last_name!=='undefined'){
132+
body.last_name=data.last_name;
133+
}
134+
135+
returnthis.post(`${this.apiServer}/rpc/update_user_profile`,body,{
136+
headers:headers
137+
});
138+
}
139+
115140
getAccessTokens(token,orgId){
116141
letparams={};
117142
letheaders={

‎ui/packages/platform/src/components/BotSettingsForm/BotSettingsForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ const BotSettingsForm: React.FC<BotSettingsFormProps> = (props) => {
161161
enableReinitialize:true,
162162
initialValues:{
163163
threadVisibility:
164-
data?.orgProfile?.data?.is_chat_public_by_default ?'public' :'private',
164+
data?.orgProfile?.data?.is_chat_public_by_default ?'public' :'private'
165165
},
166166
onSubmit:()=>{
167167
constcurrentOrgId=orgId||null;

‎ui/packages/platform/src/pages/Bot/BotWrapper.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export interface BotWrapperProps {
77
orgId?:number;
88
envData:{
99
info?:{
10+
id?:number|null
1011
user_name?:string
1112
}
1213
};
@@ -38,7 +39,8 @@ export const BotWrapper = (props: BotWrapperProps) => {
3839
args={{
3940
threadId:props.match.params.threadId,
4041
orgId:props.orgData.id,
41-
isPublicByDefault:props.orgData.is_chat_public_by_default
42+
isPublicByDefault:props.orgData.is_chat_public_by_default,
43+
userId:props.envData.info?.id,
4244
}}>
4345
<BotPage{...props}/>
4446
</AiBotProvider>

‎ui/packages/platform/src/pages/Bot/Messages/Message/Message.tsx

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
importReact,{useMemo,useState}from'react'
1+
importReact,{useEffect,useMemo,useRef,useState}from'react'
22
importcnfrom"classnames";
33
importReactMarkdown,{Components}from"react-markdown";
44
importrehypeRawfrom"rehype-raw";
@@ -9,8 +9,9 @@ import { icons } from "@postgres.ai/shared/styles/icons";
99
import{DebugDialog}from"../../DebugDialog/DebugDialog";
1010
import{CodeBlock}from"./CodeBlock";
1111
import{disallowedHtmlTagsForMarkdown,permalinkLinkBuilder}from"../../utils";
12-
import{StateMessage}from"../../../../types/api/entities/bot";
12+
import{MessageStatus,StateMessage}from"../../../../types/api/entities/bot";
1313
import{MermaidDiagram}from"./MermaidDiagram";
14+
import{useAiBot}from"../../hooks";
1415

1516

1617
typeBaseMessageProps={
@@ -20,17 +21,19 @@ type BaseMessageProps = {
2021
name?:string;
2122
isLoading?:boolean;
2223
formattedTime?:string;
23-
aiModel?:string
24-
stateMessage?:StateMessage|null
25-
isCurrentStreamMessage?:boolean
24+
aiModel?:string;
25+
stateMessage?:StateMessage|null;
26+
isCurrentStreamMessage?:boolean;
2627
isPublic?:boolean;
28+
threadId?:string;
29+
status?:MessageStatus
2730
}
2831

2932
typeAiMessageProps=BaseMessageProps&{
3033
isAi:true;
3134
content:string;
32-
aiModel:string
33-
isCurrentStreamMessage?:boolean
35+
aiModel:string;
36+
isCurrentStreamMessage?:boolean;
3437
}
3538

3639
typeHumanMessageProps=BaseMessageProps&{
@@ -42,8 +45,8 @@ type HumanMessageProps = BaseMessageProps & {
4245
typeLoadingMessageProps=BaseMessageProps&{
4346
isLoading:true;
4447
isAi:true;
45-
content?:undefined
46-
stateMessage:StateMessage|null
48+
content?:undefined;
49+
stateMessage:StateMessage|null;
4750
}
4851

4952
typeMessageProps=AiMessageProps|HumanMessageProps|LoadingMessageProps;
@@ -261,14 +264,44 @@ export const Message = React.memo((props: MessageProps) => {
261264
aiModel,
262265
stateMessage,
263266
isCurrentStreamMessage,
264-
isPublic
267+
isPublic,
268+
threadId,
269+
status
265270
}=props;
266271

272+
const{ updateMessageStatus}=useAiBot()
273+
274+
constelementRef=useRef<HTMLDivElement|null>(null);
275+
276+
267277
const[isDebugVisible,setDebugVisible]=useState(false);
268278

269279

270280
constclasses=useStyles();
271281

282+
useEffect(()=>{
283+
if(!isAi||isCurrentStreamMessage||status==='read')return;
284+
285+
constobserver=newIntersectionObserver(
286+
(entries)=>{
287+
constentry=entries[0];
288+
if(entry.isIntersecting&&threadId&&id){
289+
updateMessageStatus(threadId,id,'read');
290+
observer.disconnect();
291+
}
292+
},
293+
{threshold:0.1}
294+
);
295+
296+
if(elementRef.current){
297+
observer.observe(elementRef.current);
298+
}
299+
300+
return()=>{
301+
observer.disconnect();
302+
};
303+
},[id,updateMessageStatus,isCurrentStreamMessage,isAi,threadId,status]);
304+
272305
constcontentToRender:string=content?.replace(/\n/g,' \n')||''
273306

274307
consttoggleDebugDialog=()=>{
@@ -301,7 +334,7 @@ export const Message = React.memo((props: MessageProps) => {
301334
onClose={toggleDebugDialog}
302335
messageId={id}
303336
/>}
304-
<divclassName={classes.message}>
337+
<divref={elementRef}className={classes.message}>
305338
<divclassName={classes.messageAvatar}>
306339
{isAi
307340
?<img

‎ui/packages/platform/src/pages/Bot/Messages/Messages.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ type FormattedTime = {
125125
[id:string]:Time
126126
}
127127

128-
exportconstMessages=React.memo(({orgId}:{orgId:number})=>{
128+
exportconstMessages=React.memo(({orgId, threadId}:{orgId:number,threadId?:string})=>{
129129
const{
130130
messages,
131131
loading:isLoading,
@@ -254,7 +254,8 @@ export const Messages = React.memo(({orgId}: {orgId: number}) => {
254254
created_at,
255255
content,
256256
ai_model,
257-
is_public
257+
is_public,
258+
status
258259
}=message;
259260
letname='You';
260261

@@ -283,6 +284,8 @@ export const Messages = React.memo(({orgId}: {orgId: number}) => {
283284
formattedTime={formattedTime}
284285
aiModel={ai_model}
285286
isPublic={is_public}
287+
threadId={threadId}
288+
status={status}
286289
/>
287290
)
288291
})}

‎ui/packages/platform/src/pages/Bot/hooks.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
AiModel,
1515
StateMessage,
1616
StreamMessage,
17-
ErrorMessage
17+
ErrorMessage,MessageStatus
1818
}from"../../types/api/entities/bot";
1919
import{getChatsWithWholeThreads}from"../../api/bot/getChatsWithWholeThreads";
2020
import{getChats}from"api/bot/getChats";
@@ -73,16 +73,18 @@ type UseAiBotReturnType = {
7373
isStreamingInProcess:boolean;
7474
currentStreamMessage:StreamMessage|null;
7575
errorMessage:ErrorMessage|null;
76+
updateMessageStatus:(threadId:string,messageId:string,status:MessageStatus)=>void
7677
}
7778

7879
typeUseAiBotArgs={
7980
threadId?:string;
8081
orgId?:number
8182
isPublicByDefault?:boolean
83+
userId?:number|null
8284
}
8385

8486
exportconstuseAiBotProviderValue=(args:UseAiBotArgs):UseAiBotReturnType=>{
85-
const{ threadId, orgId, isPublicByDefault}=args;
87+
const{ threadId, orgId, isPublicByDefault, userId}=args;
8688
const{ showMessage, closeSnackbar}=useAlertSnackbar();
8789
const{
8890
aiModels,
@@ -413,6 +415,27 @@ export const useAiBotProviderValue = (args: UseAiBotArgs): UseAiBotReturnType =>
413415
}))
414416
}
415417

418+
constupdateMessageStatus=(threadId:string,messageId:string,status:MessageStatus)=>{
419+
wsSendMessage(JSON.stringify({
420+
action:'message_status_update',
421+
payload:{
422+
thread_id:threadId,
423+
message_id:messageId,
424+
read_by:userId,
425+
status
426+
}
427+
}))
428+
if(messages&&messages.length>0){
429+
constupdatedMessages=messages.map((item)=>{
430+
if(item.id===messageId){
431+
item["status"]=status
432+
}
433+
returnitem
434+
});
435+
setMessages(updatedMessages)
436+
}
437+
}
438+
416439
constgetDebugMessagesForWholeThread=async()=>{
417440
setDebugMessagesLoading(true)
418441
if(threadId){
@@ -478,7 +501,8 @@ export const useAiBotProviderValue = (args: UseAiBotArgs): UseAiBotReturnType =>
478501
stateMessage,
479502
isStreamingInProcess,
480503
currentStreamMessage,
481-
errorMessage
504+
errorMessage,
505+
updateMessageStatus
482506
}
483507
}
484508

‎ui/packages/platform/src/pages/Bot/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ export const BotPage = (props: BotPageProps) => {
268268
</Box>
269269
</Box>
270270
<BoxclassName={cn(classes.contentContainer,{[classes.isChatsListVisible]:isChatsListVisible})}>
271-
<MessagesorgId={orgData.id}/>
271+
<MessagesorgId={orgData.id}threadId={match.params.threadId}/>
272272
<Command
273273
threadId={match.params.threadId}
274274
orgId={orgData.id}

‎ui/packages/platform/src/pages/Profile/ProfileWrapper.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,28 @@ export const ProfileWrapper = () => {
1717
marginLeft:theme.spacing(1),
1818
marginRight:theme.spacing(1),
1919
},
20+
formControlLabel:{
21+
marginLeft:theme.spacing(0),
22+
marginRight:theme.spacing(1),
23+
},
24+
formControlLabelCheckbox:{
25+
'& svg':{
26+
fontSize:18
27+
}
28+
},
29+
updateButtonContainer:{
30+
marginTop:theme.spacing(3),
31+
marginLeft:theme.spacing(1),
32+
marginRight:theme.spacing(1),
33+
},
34+
label:{
35+
marginTop:theme.spacing(2),
36+
marginBottom:theme.spacing(1),
37+
marginLeft:theme.spacing(1),
38+
marginRight:theme.spacing(1),
39+
color:'#000!important',
40+
fontWeight:'bold',
41+
},
2042
dense:{
2143
marginTop:16,
2244
},

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp