diff --git a/client/react-native/android/app/src/main/java/chat/berty/core/notification/NotificationNative.java b/client/react-native/android/app/src/main/java/chat/berty/core/notification/NotificationNative.java index fa11088bc0..dbfb16f00d 100644 --- a/client/react-native/android/app/src/main/java/chat/berty/core/notification/NotificationNative.java +++ b/client/react-native/android/app/src/main/java/chat/berty/core/notification/NotificationNative.java @@ -19,7 +19,7 @@ public class NotificationNative implements NativeNotificationDriver { public static int PERMISSION_CODE = 200; private Logger logger = new Logger("chat.berty.io"); - private static final String CHANNEL_ID = "berty-chat-android-notification-id"; + private static final String CHANNEL_ID = "berty-chat-wandroid-notification-id"; private static final String CHANNEL_NAME = "berty.chat.android.core.notification.name"; private static final String CHANNEL_DESCRIPTION = "berty.chat.android.core.notification.description"; diff --git a/client/react-native/common/components/Library/ContactIdentityActions/ActionsUnknown.js b/client/react-native/common/components/Library/ContactIdentityActions/ActionsUnknown.js new file mode 100644 index 0000000000..4ac63a08bf --- /dev/null +++ b/client/react-native/common/components/Library/ContactIdentityActions/ActionsUnknown.js @@ -0,0 +1,47 @@ +import React from 'react' +import { colors } from '../../../constants' +import ActionList from './ActionList' +import RelayContext from '../../../relay/RelayContext' +import { withNamespaces } from 'react-i18next' + +const ActionsUnknown = ({ data, inModal, t }) => ( + + {({ mutations }) => ( + + { + const contact = Object.keys(data) + .filter(key => key.substring(0, 2) !== '__') + .reduce( + (acc, key) => ({ + ...acc, + [key]: data[key], + }), + {} + ) + return mutations.contactRequest({ + contactId: contact.id, + contactOverrideDisplayName: + contact.overrideDisplayName || contact.displayName || '', + introText: '', + }) + }} + successMessage={t('contacts.request-action-feedback')} + /> + mutations.contactRemove({ id: data.id })} + successMessage={t('contacts.cancel-request-action-feedback')} + /> + + )} + +) + +export default withNamespaces()(ActionsUnknown) diff --git a/client/react-native/common/components/Screens/Chats/Detail.js b/client/react-native/common/components/Screens/Chats/Detail.js index f01e6c0a15..74898c508a 100644 --- a/client/react-native/common/components/Screens/Chats/Detail.js +++ b/client/react-native/common/components/Screens/Chats/Detail.js @@ -70,9 +70,9 @@ class Message extends React.Component { data: { seenAt }, } = this.props if (seenAt !== nextProps.data.seenAt) { - return false + return true } - return true + return false } render () { @@ -87,9 +87,8 @@ class Message extends React.Component { const isMyself = contact && contact.status === 42 const isOneToOne = conversation.kind === enums.BertyEntityConversationInputKind.OneToOne - - // TODO: implement message seen - if (this.props.data.seenAt === null) { + // TODO: implement message seeni + if (new Date(this.props.data.seenAt).getTime() === 0) { this.messageSeen() } return ( @@ -147,9 +146,7 @@ class Message extends React.Component { {dateFns.fuzzyTimeOrFull(new Date(data.createdAt))}{' '} {isMyself ? ( 0 ? 'check-circle' : 'circle' - } + name={utils.isReadByOthers(data) ? 'check-circle' : 'circle'} size={10} /> ) : null}{' '} diff --git a/client/react-native/common/components/Screens/Chats/List.js b/client/react-native/common/components/Screens/Chats/List.js index 4a22f4d66a..ff961667e3 100644 --- a/client/react-native/common/components/Screens/Chats/List.js +++ b/client/react-native/common/components/Screens/Chats/List.js @@ -139,8 +139,7 @@ const ItemBase = fragments.Conversation( const { data, navigation, t } = this.props const { connected, unread } = this.state - const isRead = utils.isRead(data) - + const isRead = utils.isReadByMe(data) // fix when contact request is send after conversation invite if ( data.members.length === 2 && diff --git a/client/react-native/common/components/Screens/Contacts/List/Item.js b/client/react-native/common/components/Screens/Contacts/List/Item.js index 49d749a80e..742db29ced 100644 --- a/client/react-native/common/components/Screens/Contacts/List/Item.js +++ b/client/react-native/common/components/Screens/Contacts/List/Item.js @@ -5,60 +5,64 @@ import { borderBottom, marginLeft, padding } from '../../../../styles' import { colors } from '../../../../constants' import { showContact } from '../../../../helpers/contacts' import { withNavigation } from 'react-navigation' +import ActionsUnknown from '../../../Library/ContactIdentityActions/ActionsUnknown' import ActionsReceived from '../../../Library/ContactIdentityActions/ActionsReceived' import ActionsSent from '../../../Library/ContactIdentityActions/ActionsSent' import { withNamespaces } from 'react-i18next' -const Item = fragments.Contact(class Item extends PureComponent { - async showDetails () { - const { data, context, navigation } = this.props +const Item = fragments.Contact( + class Item extends PureComponent { + async showDetails () { + const { data, context, navigation } = this.props - return showContact({ data, context, navigation }) - } + return showContact({ data, context, navigation }) + } - render () { - const { data, ignoreMyself, t } = this.props - const { overrideDisplayName, displayName, status } = data + render () { + const { data, ignoreMyself, t } = this.props + const { overrideDisplayName, displayName, status } = data - if ( - ignoreMyself && + if ( + ignoreMyself && status === enums.BertyEntityContactInputStatus.Myself - ) { - return null - } + ) { + return null + } - return ( - this.showDetails()} - > - - - - - {overrideDisplayName || displayName} - - - {t(`contacts.statuses.${ - enums.ValueBertyEntityContactInputStatus[status] - }`)} - - + return ( + this.showDetails()} + > + + + + + {overrideDisplayName || displayName} + + + {t( + `contacts.statuses.${ + enums.ValueBertyEntityContactInputStatus[status] + }` + )} + + + + {status === enums.BertyEntityContactInputStatus.Unknown && ( + + )} + {status === enums.BertyEntityContactInputStatus.RequestedMe && ( + + )} + {status === enums.BertyEntityContactInputStatus.IsRequested && ( + + )} - {status === enums.BertyEntityContactInputStatus.RequestedMe && ( - - )} - {status === enums.BertyEntityContactInputStatus.IsRequested && ( - - )} - - ) + ) + } } -}) +) export default withNavigation(withNamespaces()(Item)) diff --git a/client/react-native/common/graphql/fragments/Conversation.js b/client/react-native/common/graphql/fragments/Conversation.js index 76b827a5ae..12b6c921be 100644 --- a/client/react-native/common/graphql/fragments/Conversation.js +++ b/client/react-native/common/graphql/fragments/Conversation.js @@ -9,6 +9,7 @@ export default component => createdAt updatedAt readAt + wroteAt title topic infos @@ -18,6 +19,7 @@ export default component => createdAt updatedAt readAt + wroteAt status contact { id diff --git a/client/react-native/common/graphql/queries/Conversation.js b/client/react-native/common/graphql/queries/Conversation.js index e05fce0f49..a085a108b8 100644 --- a/client/react-native/common/graphql/queries/Conversation.js +++ b/client/react-native/common/graphql/queries/Conversation.js @@ -9,6 +9,7 @@ const query = graphql` $createdAt: GoogleProtobufTimestampInput $updatedAt: GoogleProtobufTimestampInput $readAt: GoogleProtobufTimestampInput + $wroteAt: GoogleProtobufTimestampInput $title: String! $topic: String! $infos: String! @@ -19,6 +20,7 @@ const query = graphql` createdAt: $createdAt updatedAt: $updatedAt readAt: $readAt + wroteAt: $wroteAt title: $title topic: $topic infos: $infos @@ -29,6 +31,7 @@ const query = graphql` createdAt updatedAt readAt + wroteAt title topic infos @@ -36,6 +39,7 @@ const query = graphql` id createdAt updatedAt + wroteAt status contact { id diff --git a/client/react-native/common/i18n/en/messages.json b/client/react-native/common/i18n/en/messages.json index 990a5f0c1d..959ff7e8bf 100644 --- a/client/react-native/common/i18n/en/messages.json +++ b/client/react-native/common/i18n/en/messages.json @@ -169,6 +169,8 @@ "decline-action-feedback": "Contact request has been declined", "resend-action": "Resend", "resend-action-feedback": "Contact request has been sent again", + "request-action": "Request", + "request-action-feedback": "Contact request has been sent", "cancel-request-action": "Remove", "cancel-request-action-feedback": "Contact invitation has been removed", "block-confirm-question": "Are you sure you want to block this contact?", diff --git a/client/react-native/common/i18n/fr/messages.json b/client/react-native/common/i18n/fr/messages.json index 60363b42ec..63372697c5 100644 --- a/client/react-native/common/i18n/fr/messages.json +++ b/client/react-native/common/i18n/fr/messages.json @@ -169,6 +169,8 @@ "decline-action-feedback": "L'invitation a été refusée", "resend-action": "Renvoyer", "resend-action-feedback": "L'invitation a été envoyée à nouveau", + "request-action": "Ajouter", + "request-action-feedback": "L'invitation a été envoyée", "cancel-request-action": "Annuler", "cancel-request-action-feedback": "L'invitation a été annulée", "block-confirm-question": "Êtes vous sûr de vouloir bloquer ce contact ?", diff --git a/client/react-native/common/schema.graphql b/client/react-native/common/schema.graphql index 69f0eac413..ec0a9a8bc2 100644 --- a/client/react-native/common/schema.graphql +++ b/client/react-native/common/schema.graphql @@ -298,6 +298,7 @@ type BertyEntityConversation implements Node { createdAt: GoogleProtobufTimestamp updatedAt: GoogleProtobufTimestamp readAt: GoogleProtobufTimestamp + wroteAt: GoogleProtobufTimestamp title: String! topic: String! infos: String! @@ -310,6 +311,7 @@ type BertyEntityConversationMember implements Node { createdAt: GoogleProtobufTimestamp updatedAt: GoogleProtobufTimestamp readAt: GoogleProtobufTimestamp + wroteAt: GoogleProtobufTimestamp status: Enum contact: BertyEntityContact conversationId: ID! @@ -794,27 +796,29 @@ input BertyEntityContactInput { overrideDisplayName: String! overrideDisplayStatus: String! } +input BertyEntityConversationMemberInput { + id: ID! + createdAt: GoogleProtobufTimestampInput + updatedAt: GoogleProtobufTimestampInput + readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput + status: Enum + contact: BertyEntityContactInput + conversationId: ID! + contactId: ID! +} input BertyEntityConversationInput { id: ID! createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! kind: Enum members: [BertyEntityConversationMemberInput] } -input BertyEntityConversationMemberInput { - id: ID! - createdAt: GoogleProtobufTimestampInput - updatedAt: GoogleProtobufTimestampInput - readAt: GoogleProtobufTimestampInput - status: Enum - contact: BertyEntityContactInput - conversationId: ID! - contactId: ID! -} input BertyEntityMessageInput { text: String! } @@ -882,6 +886,7 @@ type Query { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! @@ -893,6 +898,7 @@ type Query { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput status: Enum contact: BertyEntityContactInput conversationId: ID! @@ -1010,6 +1016,7 @@ type Mutation { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! diff --git a/client/react-native/common/utils/conversation.js b/client/react-native/common/utils/conversation.js index 20232709a5..34f9b8ec6d 100644 --- a/client/react-native/common/utils/conversation.js +++ b/client/react-native/common/utils/conversation.js @@ -28,12 +28,26 @@ export const getTitle = ({ title, members }) => export const getRelayID = id => btoa(`conversation:${id}`) export const getCoreID = id => atob(id).match(/:(.*)$/)[1] -export const isRead = ({ members }) => { +export const isReadByMe = ({ wroteAt, members }) => { const myself = members.find( - _ => _.contact.status === BertyEntityContactInputStatus.Myself + _ => _.contact && _.contact.status === BertyEntityContactInputStatus.Myself ) if (myself == null) { return true } - return new Date(myself.readAt).getTime() > 0 + return new Date(myself.readAt).getTime() >= new Date(wroteAt).getTime() } + +export const isMessageReadByMe = ({ members }, { createdAt }) => { + const myself = members.find( + _ => _.contact && _.contact.status === BertyEntityContactInputStatus.Myself + ) + if (myself == null) { + return true + } + return new Date(myself.readAt).getTime() >= new Date(createdAt).getTime() +} + +export const isReadByOthers = message => + message.dispatches && + message.dispatches.some(_ => new Date(_.ackedAt).getTime() > 0) diff --git a/core/api/node/graphql/gqlgen.gen.yml b/core/api/node/graphql/gqlgen.gen.yml index 78311b5915..065c6c1e54 100644 --- a/core/api/node/graphql/gqlgen.gen.yml +++ b/core/api/node/graphql/gqlgen.gen.yml @@ -874,6 +874,7 @@ models: createdAt: updatedAt: readAt: + wroteAt: title: topic: infos: @@ -887,6 +888,7 @@ models: createdAt: updatedAt: readAt: + wroteAt: title: topic: infos: @@ -900,6 +902,7 @@ models: createdAt: updatedAt: readAt: + wroteAt: title: topic: infos: @@ -914,6 +917,7 @@ models: createdAt: updatedAt: readAt: + wroteAt: status: contact: conversationId: @@ -928,6 +932,7 @@ models: createdAt: updatedAt: readAt: + wroteAt: status: contact: conversationId: @@ -942,6 +947,7 @@ models: createdAt: updatedAt: readAt: + wroteAt: status: contact: conversationId: diff --git a/core/api/node/graphql/graph/generated/generated.gen.go b/core/api/node/graphql/graph/generated/generated.gen.go index b595d8e584..f252ca7ed6 100644 --- a/core/api/node/graphql/graph/generated/generated.gen.go +++ b/core/api/node/graphql/graph/generated/generated.gen.go @@ -118,6 +118,7 @@ type ComplexityRoot struct { CreatedAt func(childComplexity int) int UpdatedAt func(childComplexity int) int ReadAt func(childComplexity int) int + WroteAt func(childComplexity int) int Title func(childComplexity int) int Topic func(childComplexity int) int Infos func(childComplexity int) int @@ -134,6 +135,7 @@ type ComplexityRoot struct { CreatedAt func(childComplexity int) int UpdatedAt func(childComplexity int) int ReadAt func(childComplexity int) int + WroteAt func(childComplexity int) int Status func(childComplexity int) int Contact func(childComplexity int) int ConversationId func(childComplexity int) int @@ -753,7 +755,7 @@ type ComplexityRoot struct { ContactRemove func(childComplexity int, id string, createdAt *time.Time, updatedAt *time.Time, sigchain []byte, status *int32, devices []*entity.Device, displayName string, displayStatus string, overrideDisplayName string, overrideDisplayStatus string) int ContactUpdate func(childComplexity int, id string, createdAt *time.Time, updatedAt *time.Time, sigchain []byte, status *int32, devices []*entity.Device, displayName string, displayStatus string, overrideDisplayName string, overrideDisplayStatus string) int ConversationCreate func(childComplexity int, contacts []*entity.Contact, title string, topic string, kind *int32) int - ConversationUpdate func(childComplexity int, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, title string, topic string, infos string, kind *int32, members []*entity.ConversationMember) int + ConversationUpdate func(childComplexity int, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, wroteAt *time.Time, title string, topic string, infos string, kind *int32, members []*entity.ConversationMember) int ConversationInvite func(childComplexity int, conversation *entity.Conversation, contacts []*entity.Contact) int ConversationExclude func(childComplexity int, conversation *entity.Conversation, contacts []*entity.Contact) int ConversationAddMessage func(childComplexity int, conversation *entity.Conversation, message *entity.Message) int @@ -781,8 +783,8 @@ type ComplexityRoot struct { Contact func(childComplexity int, filter *entity.Contact) int ContactCheckPublicKey func(childComplexity int, filter *entity.Contact) int ConversationList func(childComplexity int, filter *entity.Conversation, orderBy string, orderDesc bool, first *int32, after *string, last *int32, before *string) int - Conversation func(childComplexity int, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, title string, topic string, infos string, kind *int32, members []*entity.ConversationMember) int - ConversationMember func(childComplexity int, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, status *int32, contact *entity.Contact, conversationId string, contactId string) int + Conversation func(childComplexity int, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, wroteAt *time.Time, title string, topic string, infos string, kind *int32, members []*entity.ConversationMember) int + ConversationMember func(childComplexity int, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, wroteAt *time.Time, status *int32, contact *entity.Contact, conversationId string, contactId string) int ConversationLastEvent func(childComplexity int, id string) int DevicePushConfigList func(childComplexity int, T bool) int DeviceInfos func(childComplexity int, T bool) int @@ -864,7 +866,7 @@ type MutationResolver interface { ContactRemove(ctx context.Context, id string, createdAt *time.Time, updatedAt *time.Time, sigchain []byte, status *int32, devices []*entity.Device, displayName string, displayStatus string, overrideDisplayName string, overrideDisplayStatus string) (*entity.Contact, error) ContactUpdate(ctx context.Context, id string, createdAt *time.Time, updatedAt *time.Time, sigchain []byte, status *int32, devices []*entity.Device, displayName string, displayStatus string, overrideDisplayName string, overrideDisplayStatus string) (*entity.Contact, error) ConversationCreate(ctx context.Context, contacts []*entity.Contact, title string, topic string, kind *int32) (*entity.Conversation, error) - ConversationUpdate(ctx context.Context, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, title string, topic string, infos string, kind *int32, members []*entity.ConversationMember) (*entity.Conversation, error) + ConversationUpdate(ctx context.Context, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, wroteAt *time.Time, title string, topic string, infos string, kind *int32, members []*entity.ConversationMember) (*entity.Conversation, error) ConversationInvite(ctx context.Context, conversation *entity.Conversation, contacts []*entity.Contact) (*entity.Conversation, error) ConversationExclude(ctx context.Context, conversation *entity.Conversation, contacts []*entity.Contact) (*entity.Conversation, error) ConversationAddMessage(ctx context.Context, conversation *entity.Conversation, message *entity.Message) (*entity.Event, error) @@ -891,8 +893,8 @@ type QueryResolver interface { Contact(ctx context.Context, filter *entity.Contact) (*entity.Contact, error) ContactCheckPublicKey(ctx context.Context, filter *entity.Contact) (*node.Bool, error) ConversationList(ctx context.Context, filter *entity.Conversation, orderBy string, orderDesc bool, first *int32, after *string, last *int32, before *string) (*node.ConversationListConnection, error) - Conversation(ctx context.Context, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, title string, topic string, infos string, kind *int32, members []*entity.ConversationMember) (*entity.Conversation, error) - ConversationMember(ctx context.Context, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, status *int32, contact *entity.Contact, conversationId string, contactId string) (*entity.ConversationMember, error) + Conversation(ctx context.Context, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, wroteAt *time.Time, title string, topic string, infos string, kind *int32, members []*entity.ConversationMember) (*entity.Conversation, error) + ConversationMember(ctx context.Context, id string, createdAt *time.Time, updatedAt *time.Time, readAt *time.Time, wroteAt *time.Time, status *int32, contact *entity.Contact, conversationId string, contactId string) (*entity.ConversationMember, error) ConversationLastEvent(ctx context.Context, id string) (*entity.Event, error) DevicePushConfigList(ctx context.Context, T bool) (*node.DevicePushConfigListOutput, error) DeviceInfos(ctx context.Context, T bool) (*deviceinfo.DeviceInfos, error) @@ -1529,48 +1531,62 @@ func field_Mutation_ConversationUpdate_args(rawArgs map[string]interface{}) (map } } args["readAt"] = arg3 - var arg4 string - if tmp, ok := rawArgs["title"]; ok { + var arg4 *time.Time + if tmp, ok := rawArgs["wroteAt"]; ok { var err error - arg4, err = models.UnmarshalString(tmp) + var ptr1 time.Time + if tmp != nil { + ptr1, err = models.UnmarshalTime(tmp) + arg4 = &ptr1 + } + if err != nil { return nil, err } } - args["title"] = arg4 + args["wroteAt"] = arg4 var arg5 string - if tmp, ok := rawArgs["topic"]; ok { + if tmp, ok := rawArgs["title"]; ok { var err error arg5, err = models.UnmarshalString(tmp) if err != nil { return nil, err } } - args["topic"] = arg5 + args["title"] = arg5 var arg6 string - if tmp, ok := rawArgs["infos"]; ok { + if tmp, ok := rawArgs["topic"]; ok { var err error arg6, err = models.UnmarshalString(tmp) if err != nil { return nil, err } } - args["infos"] = arg6 - var arg7 *int32 + args["topic"] = arg6 + var arg7 string + if tmp, ok := rawArgs["infos"]; ok { + var err error + arg7, err = models.UnmarshalString(tmp) + if err != nil { + return nil, err + } + } + args["infos"] = arg7 + var arg8 *int32 if tmp, ok := rawArgs["kind"]; ok { var err error var ptr1 int32 if tmp != nil { ptr1, err = models.UnmarshalEnum(tmp) - arg7 = &ptr1 + arg8 = &ptr1 } if err != nil { return nil, err } } - args["kind"] = arg7 - var arg8 []*entity.ConversationMember + args["kind"] = arg8 + var arg9 []*entity.ConversationMember if tmp, ok := rawArgs["members"]; ok { var err error var rawIf1 []interface{} @@ -1581,19 +1597,19 @@ func field_Mutation_ConversationUpdate_args(rawArgs map[string]interface{}) (map rawIf1 = []interface{}{tmp} } } - arg8 = make([]*entity.ConversationMember, len(rawIf1)) + arg9 = make([]*entity.ConversationMember, len(rawIf1)) for idx1 := range rawIf1 { var ptr2 entity.ConversationMember if rawIf1[idx1] != nil { ptr2, err = UnmarshalBertyEntityConversationMemberInput(rawIf1[idx1]) - arg8[idx1] = &ptr2 + arg9[idx1] = &ptr2 } } if err != nil { return nil, err } } - args["members"] = arg8 + args["members"] = arg9 return args, nil } @@ -2589,48 +2605,62 @@ func field_Query_Conversation_args(rawArgs map[string]interface{}) (map[string]i } } args["readAt"] = arg3 - var arg4 string - if tmp, ok := rawArgs["title"]; ok { + var arg4 *time.Time + if tmp, ok := rawArgs["wroteAt"]; ok { var err error - arg4, err = models.UnmarshalString(tmp) + var ptr1 time.Time + if tmp != nil { + ptr1, err = models.UnmarshalTime(tmp) + arg4 = &ptr1 + } + if err != nil { return nil, err } } - args["title"] = arg4 + args["wroteAt"] = arg4 var arg5 string - if tmp, ok := rawArgs["topic"]; ok { + if tmp, ok := rawArgs["title"]; ok { var err error arg5, err = models.UnmarshalString(tmp) if err != nil { return nil, err } } - args["topic"] = arg5 + args["title"] = arg5 var arg6 string - if tmp, ok := rawArgs["infos"]; ok { + if tmp, ok := rawArgs["topic"]; ok { var err error arg6, err = models.UnmarshalString(tmp) if err != nil { return nil, err } } - args["infos"] = arg6 - var arg7 *int32 + args["topic"] = arg6 + var arg7 string + if tmp, ok := rawArgs["infos"]; ok { + var err error + arg7, err = models.UnmarshalString(tmp) + if err != nil { + return nil, err + } + } + args["infos"] = arg7 + var arg8 *int32 if tmp, ok := rawArgs["kind"]; ok { var err error var ptr1 int32 if tmp != nil { ptr1, err = models.UnmarshalEnum(tmp) - arg7 = &ptr1 + arg8 = &ptr1 } if err != nil { return nil, err } } - args["kind"] = arg7 - var arg8 []*entity.ConversationMember + args["kind"] = arg8 + var arg9 []*entity.ConversationMember if tmp, ok := rawArgs["members"]; ok { var err error var rawIf1 []interface{} @@ -2641,19 +2671,19 @@ func field_Query_Conversation_args(rawArgs map[string]interface{}) (map[string]i rawIf1 = []interface{}{tmp} } } - arg8 = make([]*entity.ConversationMember, len(rawIf1)) + arg9 = make([]*entity.ConversationMember, len(rawIf1)) for idx1 := range rawIf1 { var ptr2 entity.ConversationMember if rawIf1[idx1] != nil { ptr2, err = UnmarshalBertyEntityConversationMemberInput(rawIf1[idx1]) - arg8[idx1] = &ptr2 + arg9[idx1] = &ptr2 } } if err != nil { return nil, err } } - args["members"] = arg8 + args["members"] = arg9 return args, nil } @@ -2711,52 +2741,66 @@ func field_Query_ConversationMember_args(rawArgs map[string]interface{}) (map[st } } args["readAt"] = arg3 - var arg4 *int32 + var arg4 *time.Time + if tmp, ok := rawArgs["wroteAt"]; ok { + var err error + var ptr1 time.Time + if tmp != nil { + ptr1, err = models.UnmarshalTime(tmp) + arg4 = &ptr1 + } + + if err != nil { + return nil, err + } + } + args["wroteAt"] = arg4 + var arg5 *int32 if tmp, ok := rawArgs["status"]; ok { var err error var ptr1 int32 if tmp != nil { ptr1, err = models.UnmarshalEnum(tmp) - arg4 = &ptr1 + arg5 = &ptr1 } if err != nil { return nil, err } } - args["status"] = arg4 - var arg5 *entity.Contact + args["status"] = arg5 + var arg6 *entity.Contact if tmp, ok := rawArgs["contact"]; ok { var err error var ptr1 entity.Contact if tmp != nil { ptr1, err = UnmarshalBertyEntityContactInput(tmp) - arg5 = &ptr1 + arg6 = &ptr1 } if err != nil { return nil, err } } - args["contact"] = arg5 - var arg6 string + args["contact"] = arg6 + var arg7 string if tmp, ok := rawArgs["conversationId"]; ok { var err error - arg6, err = models.UnmarshalID(tmp) + arg7, err = models.UnmarshalID(tmp) if err != nil { return nil, err } } - args["conversationId"] = arg6 - var arg7 string + args["conversationId"] = arg7 + var arg8 string if tmp, ok := rawArgs["contactId"]; ok { var err error - arg7, err = models.UnmarshalID(tmp) + arg8, err = models.UnmarshalID(tmp) if err != nil { return nil, err } } - args["contactId"] = arg7 + args["contactId"] = arg8 return args, nil } @@ -3513,6 +3557,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.BertyEntityConversation.ReadAt(childComplexity), true + case "BertyEntityConversation.wroteAt": + if e.complexity.BertyEntityConversation.WroteAt == nil { + break + } + + return e.complexity.BertyEntityConversation.WroteAt(childComplexity), true + case "BertyEntityConversation.title": if e.complexity.BertyEntityConversation.Title == nil { break @@ -3583,6 +3634,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.BertyEntityConversationMember.ReadAt(childComplexity), true + case "BertyEntityConversationMember.wroteAt": + if e.complexity.BertyEntityConversationMember.WroteAt == nil { + break + } + + return e.complexity.BertyEntityConversationMember.WroteAt(childComplexity), true + case "BertyEntityConversationMember.status": if e.complexity.BertyEntityConversationMember.Status == nil { break @@ -5903,7 +5961,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Mutation.ConversationUpdate(childComplexity, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["title"].(string), args["topic"].(string), args["infos"].(string), args["kind"].(*int32), args["members"].([]*entity.ConversationMember)), true + return e.complexity.Mutation.ConversationUpdate(childComplexity, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["wroteAt"].(*time.Time), args["title"].(string), args["topic"].(string), args["infos"].(string), args["kind"].(*int32), args["members"].([]*entity.ConversationMember)), true case "Mutation.ConversationInvite": if e.complexity.Mutation.ConversationInvite == nil { @@ -6203,7 +6261,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.Conversation(childComplexity, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["title"].(string), args["topic"].(string), args["infos"].(string), args["kind"].(*int32), args["members"].([]*entity.ConversationMember)), true + return e.complexity.Query.Conversation(childComplexity, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["wroteAt"].(*time.Time), args["title"].(string), args["topic"].(string), args["infos"].(string), args["kind"].(*int32), args["members"].([]*entity.ConversationMember)), true case "Query.ConversationMember": if e.complexity.Query.ConversationMember == nil { @@ -6215,7 +6273,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.ConversationMember(childComplexity, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["status"].(*int32), args["contact"].(*entity.Contact), args["conversationId"].(string), args["contactId"].(string)), true + return e.complexity.Query.ConversationMember(childComplexity, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["wroteAt"].(*time.Time), args["status"].(*int32), args["contact"].(*entity.Contact), args["conversationId"].(string), args["contactId"].(string)), true case "Query.ConversationLastEvent": if e.complexity.Query.ConversationLastEvent == nil { @@ -7593,6 +7651,8 @@ func (ec *executionContext) _BertyEntityConversation(ctx context.Context, sel as out.Values[i] = ec._BertyEntityConversation_updatedAt(ctx, field, obj) case "readAt": out.Values[i] = ec._BertyEntityConversation_readAt(ctx, field, obj) + case "wroteAt": + out.Values[i] = ec._BertyEntityConversation_wroteAt(ctx, field, obj) case "title": out.Values[i] = ec._BertyEntityConversation_title(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -7706,6 +7766,26 @@ func (ec *executionContext) _BertyEntityConversation_readAt(ctx context.Context, return models.MarshalTime(res) } +// nolint: vetshadow +func (ec *executionContext) _BertyEntityConversation_wroteAt(ctx context.Context, field graphql.CollectedField, obj *entity.Conversation) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "BertyEntityConversation", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.WroteAt, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(time.Time) + rctx.Result = res + return models.MarshalTime(res) +} + // nolint: vetshadow func (ec *executionContext) _BertyEntityConversation_title(ctx context.Context, field graphql.CollectedField, obj *entity.Conversation) graphql.Marshaler { rctx := &graphql.ResolverContext{ @@ -7934,6 +8014,8 @@ func (ec *executionContext) _BertyEntityConversationMember(ctx context.Context, out.Values[i] = ec._BertyEntityConversationMember_updatedAt(ctx, field, obj) case "readAt": out.Values[i] = ec._BertyEntityConversationMember_readAt(ctx, field, obj) + case "wroteAt": + out.Values[i] = ec._BertyEntityConversationMember_wroteAt(ctx, field, obj) case "status": out.Values[i] = ec._BertyEntityConversationMember_status(ctx, field, obj) case "contact": @@ -8050,6 +8132,26 @@ func (ec *executionContext) _BertyEntityConversationMember_readAt(ctx context.Co return models.MarshalTime(res) } +// nolint: vetshadow +func (ec *executionContext) _BertyEntityConversationMember_wroteAt(ctx context.Context, field graphql.CollectedField, obj *entity.ConversationMember) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "BertyEntityConversationMember", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.WroteAt, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(time.Time) + rctx.Result = res + return models.MarshalTime(res) +} + // nolint: vetshadow func (ec *executionContext) _BertyEntityConversationMember_status(ctx context.Context, field graphql.CollectedField, obj *entity.ConversationMember) graphql.Marshaler { rctx := &graphql.ResolverContext{ @@ -21179,7 +21281,7 @@ func (ec *executionContext) _Mutation_ConversationUpdate(ctx context.Context, fi ctx = graphql.WithResolverContext(ctx, rctx) resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().ConversationUpdate(rctx, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["title"].(string), args["topic"].(string), args["infos"].(string), args["kind"].(*int32), args["members"].([]*entity.ConversationMember)) + return ec.resolvers.Mutation().ConversationUpdate(rctx, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["wroteAt"].(*time.Time), args["title"].(string), args["topic"].(string), args["infos"].(string), args["kind"].(*int32), args["members"].([]*entity.ConversationMember)) }) if resTmp == nil { return graphql.Null @@ -22178,7 +22280,7 @@ func (ec *executionContext) _Query_Conversation(ctx context.Context, field graph ctx = graphql.WithResolverContext(ctx, rctx) resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().Conversation(rctx, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["title"].(string), args["topic"].(string), args["infos"].(string), args["kind"].(*int32), args["members"].([]*entity.ConversationMember)) + return ec.resolvers.Query().Conversation(rctx, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["wroteAt"].(*time.Time), args["title"].(string), args["topic"].(string), args["infos"].(string), args["kind"].(*int32), args["members"].([]*entity.ConversationMember)) }) if resTmp == nil { return graphql.Null @@ -22209,7 +22311,7 @@ func (ec *executionContext) _Query_ConversationMember(ctx context.Context, field ctx = graphql.WithResolverContext(ctx, rctx) resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().ConversationMember(rctx, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["status"].(*int32), args["contact"].(*entity.Contact), args["conversationId"].(string), args["contactId"].(string)) + return ec.resolvers.Query().ConversationMember(rctx, args["id"].(string), args["createdAt"].(*time.Time), args["updatedAt"].(*time.Time), args["readAt"].(*time.Time), args["wroteAt"].(*time.Time), args["status"].(*int32), args["contact"].(*entity.Contact), args["conversationId"].(string), args["contactId"].(string)) }) if resTmp == nil { return graphql.Null @@ -24493,6 +24595,12 @@ func UnmarshalBertyEntityConversationInput(v interface{}) (entity.Conversation, if err != nil { return it, err } + case "wroteAt": + var err error + it.WroteAt, err = models.UnmarshalTime(v) + if err != nil { + return it, err + } case "title": var err error it.Title, err = models.UnmarshalString(v) @@ -24577,6 +24685,12 @@ func UnmarshalBertyEntityConversationMemberInput(v interface{}) (entity.Conversa if err != nil { return it, err } + case "wroteAt": + var err error + it.WroteAt, err = models.UnmarshalTime(v) + if err != nil { + return it, err + } case "status": var err error @@ -25480,6 +25594,7 @@ type BertyEntityConversation implements Node { createdAt: GoogleProtobufTimestamp updatedAt: GoogleProtobufTimestamp readAt: GoogleProtobufTimestamp + wroteAt: GoogleProtobufTimestamp title: String! topic: String! infos: String! @@ -25492,6 +25607,7 @@ type BertyEntityConversationMember implements Node { createdAt: GoogleProtobufTimestamp updatedAt: GoogleProtobufTimestamp readAt: GoogleProtobufTimestamp + wroteAt: GoogleProtobufTimestamp status: Enum contact: BertyEntityContact conversationId: ID! @@ -25976,27 +26092,29 @@ input BertyEntityContactInput { overrideDisplayName: String! overrideDisplayStatus: String! } +input BertyEntityConversationMemberInput { + id: ID! + createdAt: GoogleProtobufTimestampInput + updatedAt: GoogleProtobufTimestampInput + readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput + status: Enum + contact: BertyEntityContactInput + conversationId: ID! + contactId: ID! +} input BertyEntityConversationInput { id: ID! createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! kind: Enum members: [BertyEntityConversationMemberInput] } -input BertyEntityConversationMemberInput { - id: ID! - createdAt: GoogleProtobufTimestampInput - updatedAt: GoogleProtobufTimestampInput - readAt: GoogleProtobufTimestampInput - status: Enum - contact: BertyEntityContactInput - conversationId: ID! - contactId: ID! -} input BertyEntityMessageInput { text: String! } @@ -26064,6 +26182,7 @@ type Query { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! @@ -26075,6 +26194,7 @@ type Query { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput status: Enum contact: BertyEntityContactInput conversationId: ID! @@ -26192,6 +26312,7 @@ type Mutation { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! diff --git a/core/api/node/graphql/resolver.go b/core/api/node/graphql/resolver.go index b297deb8c9..b23c1e8eb4 100644 --- a/core/api/node/graphql/resolver.go +++ b/core/api/node/graphql/resolver.go @@ -756,7 +756,7 @@ func (r *queryResolver) ContactCheckPublicKey(ctx context.Context, contact *enti }) } -func (r *mutationResolver) ConversationUpdate(ctx context.Context, id string, createdAt, updatedAt, readAt *time.Time, title, topic string, infos string, kind *int32, members []*entity.ConversationMember) (*entity.Conversation, error) { +func (r *mutationResolver) ConversationUpdate(ctx context.Context, id string, createdAt, updatedAt, readAt *time.Time, wroteAt *time.Time, title, topic string, infos string, kind *int32, members []*entity.ConversationMember) (*entity.Conversation, error) { if id == "" { return nil, errors.New("no id supplied") } @@ -798,7 +798,7 @@ func (r *mutationResolver) ConversationRemove(ctx context.Context, id string) (* }) } -func (r *queryResolver) Conversation(ctx context.Context, id string, createdAt, updatedAt, readAt *time.Time, title, topic string, infos string, kind *int32, members []*entity.ConversationMember) (*entity.Conversation, error) { +func (r *queryResolver) Conversation(ctx context.Context, id string, createdAt, updatedAt, readAt *time.Time, wroteAt *time.Time, title, topic string, infos string, kind *int32, members []*entity.ConversationMember) (*entity.Conversation, error) { if id != "" { id = strings.SplitN(id, ":", 2)[1] } @@ -815,7 +815,7 @@ func (r *queryResolver) Conversation(ctx context.Context, id string, createdAt, ID: id, }) } -func (r *queryResolver) ConversationMember(ctx context.Context, id string, createAt, updatedAt *time.Time, readAt *time.Time, status *int32, contact *entity.Contact, conversationID, contactID string) (*entity.ConversationMember, error) { +func (r *queryResolver) ConversationMember(ctx context.Context, id string, createAt, updatedAt *time.Time, readAt *time.Time, wroteAt *time.Time, status *int32, contact *entity.Contact, conversationID, contactID string) (*entity.ConversationMember, error) { if id != "" { id = strings.SplitN(id, ":", 2)[1] } diff --git a/core/api/node/graphql/service.gen.graphql b/core/api/node/graphql/service.gen.graphql index 69f0eac413..ec0a9a8bc2 100644 --- a/core/api/node/graphql/service.gen.graphql +++ b/core/api/node/graphql/service.gen.graphql @@ -298,6 +298,7 @@ type BertyEntityConversation implements Node { createdAt: GoogleProtobufTimestamp updatedAt: GoogleProtobufTimestamp readAt: GoogleProtobufTimestamp + wroteAt: GoogleProtobufTimestamp title: String! topic: String! infos: String! @@ -310,6 +311,7 @@ type BertyEntityConversationMember implements Node { createdAt: GoogleProtobufTimestamp updatedAt: GoogleProtobufTimestamp readAt: GoogleProtobufTimestamp + wroteAt: GoogleProtobufTimestamp status: Enum contact: BertyEntityContact conversationId: ID! @@ -794,27 +796,29 @@ input BertyEntityContactInput { overrideDisplayName: String! overrideDisplayStatus: String! } +input BertyEntityConversationMemberInput { + id: ID! + createdAt: GoogleProtobufTimestampInput + updatedAt: GoogleProtobufTimestampInput + readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput + status: Enum + contact: BertyEntityContactInput + conversationId: ID! + contactId: ID! +} input BertyEntityConversationInput { id: ID! createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! kind: Enum members: [BertyEntityConversationMemberInput] } -input BertyEntityConversationMemberInput { - id: ID! - createdAt: GoogleProtobufTimestampInput - updatedAt: GoogleProtobufTimestampInput - readAt: GoogleProtobufTimestampInput - status: Enum - contact: BertyEntityContactInput - conversationId: ID! - contactId: ID! -} input BertyEntityMessageInput { text: String! } @@ -882,6 +886,7 @@ type Query { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! @@ -893,6 +898,7 @@ type Query { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput status: Enum contact: BertyEntityContactInput conversationId: ID! @@ -1010,6 +1016,7 @@ type Mutation { createdAt: GoogleProtobufTimestampInput updatedAt: GoogleProtobufTimestampInput readAt: GoogleProtobufTimestampInput + wroteAt: GoogleProtobufTimestampInput title: String! topic: String! infos: String! diff --git a/core/entity/conversation.go b/core/entity/conversation.go index 2dc0661751..9bffe39905 100644 --- a/core/entity/conversation.go +++ b/core/entity/conversation.go @@ -10,14 +10,7 @@ import ( "github.com/gofrs/uuid" ) -type ConversationInteractive interface { - ConversationInformative - - // only member should interact on conversation, not conversation itself - GetInteractiveMember(contactID string) (ConversationMemberInteractive, error) -} - -type ConversationInformative interface { +type ConversationInteractor interface { IsOneToOne() bool IsGroup() bool @@ -25,15 +18,17 @@ type ConversationInformative interface { IsMember(contactID string) bool - GetMember(contactID string) (ConversationMemberInformative, error) + GetMember(contactID string) (ConversationMemberInteractor, error) - GetOwners() []ConversationMemberInformative + GetOwners() []ConversationMemberInteractor GetComputedTitle() string + + Marshal() ([]byte, error) + Unmarshal([]byte) error } -var _ ConversationInteractive = (*Conversation)(nil) -var _ ConversationInformative = (*Conversation)(nil) +var _ ConversationInteractor = (*Conversation)(nil) func NewOneToOneConversation(a *Contact, b *Contact) (*Conversation, error) { c := &Conversation{ @@ -43,18 +38,18 @@ func NewOneToOneConversation(a *Contact, b *Contact) (*Conversation, error) { } // create the first member - ma, err := NewConversationMember(a, c) + ma, err := newConversationMember(a, c) if err != nil { return nil, errorcodes.ErrConversation.Wrap(err) } - mb, err := NewConversationMember(b, c) + mb, err := newConversationMember(b, c) if err != nil { return nil, errorcodes.ErrConversation.Wrap(err) } c.Members = append(c.Members, ma, mb) - im, err := c.GetInteractiveMember(ma.GetContactID()) + im, err := c.GetMember(ma.GetContactID()) if err != nil { return nil, errorcodes.ErrConversation.Wrap(err) } @@ -92,14 +87,14 @@ func NewGroupConversation(contacts []*Contact) (*Conversation, error) { } // add myself as converastion member - mm, err := NewConversationMember(myself, c) + mm, err := newConversationMember(myself, c) if err != nil { return nil, errorcodes.ErrConversation.Wrap(err) } c.Members = append(c.Members, mm) // get interactive member - im, err := c.GetInteractiveMember(myself.ID) + im, err := c.GetMember(myself.ID) if err != nil { return nil, err } @@ -159,26 +154,14 @@ func (c *Conversation) IsGroup() bool { return true } -// GetInteractiveMember returns a member that can interact with conversation -func (c *Conversation) GetInteractiveMember(contactID string) (ConversationMemberInteractive, error) { - for _, member := range c.Members { - if member.ContactID == contactID { - copy := *member - copy.Conversation = c - return ©, nil - } - } - return nil, errorcodes.ErrConversationGetInteractiveMember.Wrap( - errorcodes.ErrConversationMembers.Wrap( - errorcodes.ErrConversationMemberContactID.New(), - ), - ) -} - -func (c *Conversation) GetMember(contactID string) (ConversationMemberInformative, error) { +// GetMember returns a member that can interact with conversation +func (c *Conversation) GetMember(contactID string) (ConversationMemberInteractor, error) { for _, member := range c.Members { if member.ContactID == contactID { - return member, nil + return &conversationMember{ + ConversationMember: member, + conversation: c, + }, nil } } return nil, errorcodes.ErrConversationGetMember.Wrap( @@ -188,11 +171,14 @@ func (c *Conversation) GetMember(contactID string) (ConversationMemberInformativ ) } -func (c *Conversation) GetOwners() []ConversationMemberInformative { - owners := []ConversationMemberInformative{} +func (c *Conversation) GetOwners() []ConversationMemberInteractor { + owners := []ConversationMemberInteractor{} for _, member := range c.Members { if member.Status == ConversationMember_Owner { - owners = append(owners, member) + owners = append(owners, &conversationMember{ + ConversationMember: member, + conversation: c, + }) } } return owners @@ -248,14 +234,13 @@ func (c Conversation) Filtered() *Conversation { Title: c.Title, Topic: c.Topic, Kind: c.Kind, + ReadAt: c.ReadAt, Members: filteredMembers, } } // Conversation Member takes actions on conversation -type ConversationMemberInteractive interface { - ConversationMemberInformative - +type ConversationMemberInteractor interface { Invite(contact *Contact) error Leave() error @@ -268,23 +253,28 @@ type ConversationMemberInteractive interface { Unblock(contactID string) error Read(time.Time) error - Write(*Message) error -} + Write(time.Time, *Message) error -type ConversationMemberInformative interface { IsOwner() bool IsActive() bool IsBlocked() bool + Marshal() ([]byte, error) + Unmarshal([]byte) error + GetContactID() string GetContact() *Contact GetConversationID() string } -var _ ConversationMemberInformative = (*ConversationMember)(nil) -var _ ConversationMemberInteractive = (*ConversationMember)(nil) +type conversationMember struct { + *ConversationMember + conversation *Conversation +} + +var _ ConversationMemberInteractor = (*conversationMember)(nil) -func NewConversationMember(contact *Contact, conversation *Conversation) (*ConversationMember, error) { +func newConversationMember(contact *Contact, conversation *Conversation) (*ConversationMember, error) { m := &ConversationMember{ ID: conversation.ID + ":" + contact.ID, Status: ConversationMember_Active, @@ -295,47 +285,44 @@ func NewConversationMember(contact *Contact, conversation *Conversation) (*Conve return m, nil } -func (m *ConversationMember) IsOwner() bool { +func (m *conversationMember) IsOwner() bool { if m.Status == ConversationMember_Owner { return true } - // only for interactive member - if m.Conversation != nil { - // if conversation is 1 to 1, member is automatically owner - if m.Conversation.Kind == Conversation_OneToOne { - return true - } + // if conversation is 1 to 1, member is automatically owner + if m.conversation.Kind == Conversation_OneToOne { + return true + } - // if there is no member in conversation, member is automatically owner - if len(m.Conversation.Members) == 0 { - return true - } + // if there is no member in conversation, member is automatically owner + if len(m.conversation.Members) == 0 { + return true + } - // if there is no owner, member is automatically owner - if len(m.Conversation.GetOwners()) == 0 { - return true - } + // if there is no owner, member is automatically owner + if len(m.conversation.GetOwners()) == 0 { + return true } return false } -func (m *ConversationMember) IsActive() bool { +func (m *conversationMember) IsActive() bool { if m.Status == ConversationMember_Active || m.IsOwner() { return true } return false } -func (m *ConversationMember) IsBlocked() bool { +func (m *conversationMember) IsBlocked() bool { if m.Status == ConversationMember_Blocked { return true } return false } -func (m *ConversationMember) Invite(contact *Contact) error { +func (m *conversationMember) Invite(contact *Contact) error { // check if member have the right to invite if !m.IsActive() { return errorcodes.ErrConversationMemberInvite.Wrap( @@ -344,40 +331,31 @@ func (m *ConversationMember) Invite(contact *Contact) error { } // check if contact is already a member - if m.Conversation.IsMember(contact.ID) { + if m.conversation.IsMember(contact.ID) { return nil } // check if we can add the member - if m.Conversation.IsFull() { + if m.conversation.IsFull() { return errorcodes.ErrConversationMemberInvite.Wrap( errorcodes.ErrConversationIsFull.New(), ) } // then add the member - cm, err := NewConversationMember(contact, m.Conversation) + cm, err := newConversationMember(contact, m.conversation) if err != nil { return errorcodes.ErrConversationMemberInvite.Wrap(err) } - m.Conversation.Members = append(m.Conversation.Members, cm) + m.conversation.Members = append(m.conversation.Members, cm) return nil } -func (m *ConversationMember) Leave() error { - // check if the member is the only owner - if m.IsOwner() && len(m.Conversation.GetOwners()) == 1 { - return errorcodes.ErrConversationMemberLeave.Wrap( - errorcodes.ErrConversationMemberStatus.Wrap( - errorcodes.ErrConversationGetOwners.New(), - ), - ) - } - +func (m *conversationMember) Leave() error { // retrieve member in list and remove it - for i, member := range m.Conversation.Members { + for i, member := range m.conversation.Members { if m.ID == member.ID { - m.Conversation.Members = append(m.Conversation.Members[:i], m.Conversation.Members[i+1:]...) + m.conversation.Members = append(m.conversation.Members[:i], m.conversation.Members[i+1:]...) return nil } } @@ -387,7 +365,7 @@ func (m *ConversationMember) Leave() error { ) } -func (m *ConversationMember) SetTitle(title string) error { +func (m *conversationMember) SetTitle(title string) error { // check if member have the right to set the title if !m.IsOwner() { return errorcodes.ErrConversationMemberSetTitle.Wrap( @@ -403,11 +381,11 @@ func (m *ConversationMember) SetTitle(title string) error { } // then set the title - m.Conversation.Title = title + m.conversation.Title = title return nil } -func (m *ConversationMember) SetTopic(topic string) error { +func (m *conversationMember) SetTopic(topic string) error { // check if member have the right to set the topic if !m.IsOwner() { return errorcodes.ErrConversationMemberSetTopic.Wrap( @@ -416,11 +394,11 @@ func (m *ConversationMember) SetTopic(topic string) error { } // then set the topic - m.Conversation.Topic = topic + m.conversation.Topic = topic return nil } -func (m *ConversationMember) SetOwner(contactID string) error { +func (m *conversationMember) SetOwner(contactID string) error { // check if member have the right to set new owner if !m.IsOwner() { return errorcodes.ErrConversationMemberSetOwner.Wrap( @@ -428,7 +406,7 @@ func (m *ConversationMember) SetOwner(contactID string) error { ) } - for _, member := range m.Conversation.Members { + for _, member := range m.conversation.Members { if member.Contact.ID == contactID { member.Status = ConversationMember_Owner return nil @@ -442,7 +420,7 @@ func (m *ConversationMember) SetOwner(contactID string) error { ) } -func (m *ConversationMember) Block(contactID string) error { +func (m *conversationMember) Block(contactID string) error { // check if member have the right to block if !m.IsOwner() { return errorcodes.ErrConversationMemberBlock.Wrap( @@ -451,7 +429,7 @@ func (m *ConversationMember) Block(contactID string) error { } // check if contact is an owner - cm, err := m.Conversation.GetInteractiveMember(contactID) + cm, err := m.conversation.GetMember(contactID) if err != nil { return errorcodes.ErrConversationMemberBlock.Wrap(err) } @@ -464,7 +442,7 @@ func (m *ConversationMember) Block(contactID string) error { } // then block the contact - for _, member := range m.Conversation.Members { + for _, member := range m.conversation.Members { if member.Contact.ID == contactID { member.Status = ConversationMember_Blocked return nil @@ -478,7 +456,7 @@ func (m *ConversationMember) Block(contactID string) error { ) } -func (m *ConversationMember) Unblock(contactID string) error { +func (m *conversationMember) Unblock(contactID string) error { // check if member have the right to unblock if !m.IsOwner() { return errorcodes.ErrConversationMemberUnblock.Wrap( @@ -486,7 +464,7 @@ func (m *ConversationMember) Unblock(contactID string) error { ) } - for _, member := range m.Conversation.Members { + for _, member := range m.conversation.Members { if member.Contact.ID == contactID { member.Status = ConversationMember_Active return nil @@ -500,7 +478,7 @@ func (m *ConversationMember) Unblock(contactID string) error { ) } -func (m *ConversationMember) Read(at time.Time) error { +func (m *conversationMember) Read(at time.Time) error { // check if member have the right to read if !m.IsActive() { return errorcodes.ErrConversationMemberRead.Wrap( @@ -508,25 +486,15 @@ func (m *ConversationMember) Read(at time.Time) error { ) } - m.ReadAt = at - for _, member := range m.Conversation.Members { - if member.Contact.ID == m.Contact.ID { - member.ReadAt = at - - // say that conversation has been read by at least one user - m.Conversation.ReadAt = at - return nil - } - } + at = at.UTC() - return errorcodes.ErrConversationMemberRead.Wrap( - errorcodes.ErrConversationMembers.Wrap( - errorcodes.ErrConversationMemberContactID.New(), - ), - ) + m.ReadAt = at + // say that conversation has been read by at least one user + m.conversation.ReadAt = at + return nil } -func (m *ConversationMember) Write(message *Message) error { +func (m *conversationMember) Write(at time.Time, message *Message) error { // check if member have the right to write if !m.IsActive() { return errorcodes.ErrConversationMemberWrite.Wrap( @@ -534,30 +502,30 @@ func (m *ConversationMember) Write(message *Message) error { ) } - now := time.Now() - m.ReadAt = now - for _, member := range m.Conversation.Members { - if member.Contact.ID == m.Contact.ID { - member.ReadAt = now - - // say that conversation has not been read by others members - m.Conversation.ReadAt = time.Time{} + at = at.UTC() - if m.Conversation.IsOneToOne() { - m.Conversation.Infos = message.Text - } else { - m.Conversation.Infos = m.Contact.DisplayName + ": " + message.Text - } + m.ReadAt = at + m.WroteAt = at + m.conversation.WroteAt = at - return nil - } + if m.conversation.IsOneToOne() { + m.conversation.Infos = message.Text + } else { + m.conversation.Infos = m.Contact.DisplayName + ": " + message.Text } - return errorcodes.ErrConversationMemberWrite.Wrap( - errorcodes.ErrConversationMembers.Wrap( - errorcodes.ErrConversationMemberContactID.New(), - ), - ) + return nil +} + +func (m conversationMember) Filtered() *ConversationMember { + member := ConversationMember{ + ID: m.ID, + Status: m.Status, + } + if m.Contact != nil { + member.Contact = m.Contact.Filtered() + } + return &member } func (m ConversationMember) Filtered() *ConversationMember { diff --git a/core/entity/conversation.pb.go b/core/entity/conversation.pb.go index 8a24341e07..2c24261ac6 100644 --- a/core/entity/conversation.pb.go +++ b/core/entity/conversation.pb.go @@ -93,11 +93,12 @@ type Conversation struct { CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` UpdatedAt time.Time `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` ReadAt time.Time `protobuf:"bytes,4,opt,name=read_at,json=readAt,proto3,stdtime" json:"read_at"` + WroteAt time.Time `protobuf:"bytes,5,opt,name=wrote_at,json=wroteAt,proto3,stdtime" json:"wrote_at"` Title string `protobuf:"bytes,20,opt,name=title,proto3" json:"title,omitempty"` Topic string `protobuf:"bytes,21,opt,name=topic,proto3" json:"topic,omitempty"` Infos string `protobuf:"bytes,22,opt,name=infos,proto3" json:"infos,omitempty"` Kind Conversation_Kind `protobuf:"varint,23,opt,name=kind,proto3,enum=berty.entity.Conversation_Kind" json:"kind,omitempty"` - Members []*ConversationMember `protobuf:"bytes,100,rep,name=members,proto3" json:"members,omitempty" gorm:"foreignkey:ConversationID;association_foreignkey:ID"` + Members []*ConversationMember `protobuf:"bytes,100,rep,name=members,proto3" json:"members,omitempty" gorm:"foreignkey:ConversationID;association_foreignkey:ID;save_associations:true"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -164,6 +165,13 @@ func (m *Conversation) GetReadAt() time.Time { return time.Time{} } +func (m *Conversation) GetWroteAt() time.Time { + if m != nil { + return m.WroteAt + } + return time.Time{} +} + func (m *Conversation) GetTitle() string { if m != nil { return m.Title @@ -204,11 +212,11 @@ type ConversationMember struct { CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` UpdatedAt time.Time `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` ReadAt time.Time `protobuf:"bytes,4,opt,name=read_at,json=readAt,proto3,stdtime" json:"read_at"` + WroteAt time.Time `protobuf:"bytes,5,opt,name=wrote_at,json=wroteAt,proto3,stdtime" json:"wrote_at"` Status ConversationMember_Status `protobuf:"varint,10,opt,name=status,proto3,enum=berty.entity.ConversationMember_Status" json:"status,omitempty"` - Contact *Contact `protobuf:"bytes,100,opt,name=contact,proto3" json:"contact,omitempty"` + Contact *Contact `protobuf:"bytes,100,opt,name=contact,proto3" json:"contact,omitempty" gorm:"association_autoupdate:false;association_create:true"` ConversationID string `protobuf:"bytes,101,opt,name=conversation_id,json=conversationId,proto3" json:"conversation_id,omitempty"` ContactID string `protobuf:"bytes,102,opt,name=contact_id,json=contactId,proto3" json:"contact_id,omitempty"` - Conversation *Conversation `protobuf:"bytes,103,opt,name=conversation,proto3" json:"conversation,omitempty" gorm:"PRELOAD:false;foreignkey:ID;association_foreignkey:ConversationID"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -275,6 +283,13 @@ func (m *ConversationMember) GetReadAt() time.Time { return time.Time{} } +func (m *ConversationMember) GetWroteAt() time.Time { + if m != nil { + return m.WroteAt + } + return time.Time{} +} + func (m *ConversationMember) GetStatus() ConversationMember_Status { if m != nil { return m.Status @@ -303,13 +318,6 @@ func (m *ConversationMember) GetContactID() string { return "" } -func (m *ConversationMember) GetConversation() *Conversation { - if m != nil { - return m.Conversation - } - return nil -} - func init() { proto.RegisterEnum("berty.entity.Conversation_Kind", Conversation_Kind_name, Conversation_Kind_value) proto.RegisterEnum("berty.entity.ConversationMember_Status", ConversationMember_Status_name, ConversationMember_Status_value) @@ -320,51 +328,51 @@ func init() { func init() { proto.RegisterFile("entity/conversation.proto", fileDescriptor_6f1357e67b20af82) } var fileDescriptor_6f1357e67b20af82 = []byte{ - // 693 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x54, 0xcd, 0x6e, 0xd3, 0x4a, - 0x18, 0xcd, 0xa4, 0xf9, 0x69, 0xa6, 0x51, 0x6f, 0x34, 0x4a, 0x7b, 0xdd, 0x5c, 0x29, 0x8e, 0xac, - 0x2b, 0xdd, 0x2c, 0xae, 0x1c, 0xa9, 0xed, 0xa6, 0xa9, 0x00, 0xc5, 0x0d, 0x82, 0x08, 0x50, 0x2a, - 0x53, 0x36, 0x6c, 0xa2, 0x89, 0x3d, 0x71, 0x47, 0x89, 0x3d, 0x66, 0x3c, 0x69, 0x15, 0xb6, 0x7d, - 0x81, 0x2e, 0xfb, 0x08, 0xac, 0x10, 0x4b, 0x1e, 0xa1, 0x4b, 0x96, 0xac, 0x42, 0x15, 0x5e, 0x00, - 0xfa, 0x04, 0xc8, 0x63, 0x9b, 0x3a, 0x54, 0x15, 0xea, 0x9a, 0x55, 0xfc, 0x7d, 0x73, 0xce, 0xf1, - 0x77, 0xe6, 0x3b, 0x31, 0xdc, 0x22, 0x9e, 0xa0, 0x62, 0xd6, 0xb2, 0x98, 0x77, 0x42, 0x78, 0x80, - 0x05, 0x65, 0x9e, 0xee, 0x73, 0x26, 0x18, 0x2a, 0x0f, 0x09, 0x17, 0x33, 0x3d, 0x02, 0xd4, 0xaa, - 0x0e, 0x73, 0x98, 0x3c, 0x68, 0x85, 0x4f, 0x11, 0xa6, 0xa6, 0x3a, 0x8c, 0x39, 0x13, 0xd2, 0x92, - 0xd5, 0x70, 0x3a, 0x6a, 0x09, 0xea, 0x92, 0x40, 0x60, 0xd7, 0x8f, 0x01, 0xff, 0xf8, 0x63, 0xa7, - 0x75, 0x82, 0x27, 0xd4, 0xc6, 0x82, 0xfc, 0x7c, 0x88, 0x0f, 0x35, 0xec, 0xd3, 0x1b, 0xaa, 0xc3, - 0xb1, 0x7f, 0xfc, 0x66, 0x92, 0xfc, 0xc6, 0x98, 0xea, 0xcd, 0x80, 0x02, 0x5b, 0xe2, 0x97, 0xae, - 0x4b, 0x82, 0x00, 0x3b, 0x89, 0x5e, 0x25, 0xee, 0x12, 0xce, 0xa3, 0x8e, 0xf6, 0x3e, 0x07, 0xcb, - 0x07, 0x29, 0x6b, 0x68, 0x17, 0x66, 0xa9, 0xad, 0x80, 0x06, 0x68, 0x96, 0x8c, 0x7f, 0xaf, 0xbe, - 0x6d, 0x81, 0x8f, 0x67, 0x35, 0xb0, 0x98, 0xab, 0xd9, 0x5e, 0xf7, 0x7a, 0xae, 0x22, 0x87, 0x71, - 0xb7, 0xad, 0xf9, 0x9c, 0xba, 0x98, 0xcf, 0x06, 0x63, 0x32, 0xd3, 0xcc, 0x2c, 0xb5, 0xd1, 0x01, - 0x84, 0x16, 0x27, 0x58, 0x10, 0x7b, 0x80, 0x85, 0x92, 0x6d, 0x80, 0xe6, 0xda, 0x76, 0x4d, 0x8f, - 0xbc, 0xeb, 0x89, 0x01, 0xfd, 0x28, 0xf1, 0x6e, 0xac, 0x5e, 0xce, 0xd5, 0xcc, 0xf9, 0x17, 0x15, - 0x98, 0xa5, 0x98, 0xd7, 0x11, 0xa1, 0xc8, 0xd4, 0xb7, 0x13, 0x91, 0x95, 0xfb, 0x88, 0xc4, 0xbc, - 0x8e, 0x40, 0x0f, 0x60, 0x91, 0x13, 0x2c, 0x15, 0x72, 0xf7, 0x50, 0x28, 0x84, 0xa4, 0x8e, 0x40, - 0x55, 0x98, 0x17, 0x54, 0x4c, 0x88, 0x52, 0x0d, 0x6f, 0xc0, 0x8c, 0x0a, 0xd9, 0x65, 0x3e, 0xb5, - 0x94, 0x8d, 0xb8, 0x1b, 0x16, 0x61, 0x97, 0x7a, 0x23, 0x16, 0x28, 0x9b, 0x51, 0x57, 0x16, 0x68, - 0x07, 0xe6, 0xc6, 0xd4, 0xb3, 0x95, 0xbf, 0x1b, 0xa0, 0xb9, 0xbe, 0xad, 0xea, 0xe9, 0x90, 0xe8, - 0xe9, 0xab, 0xd6, 0x9f, 0x51, 0xcf, 0x36, 0x25, 0x18, 0xbd, 0x85, 0x45, 0x97, 0xb8, 0x43, 0xc2, - 0x03, 0xc5, 0x6e, 0xac, 0x34, 0xd7, 0xb6, 0x1b, 0x77, 0xf3, 0x5e, 0x48, 0xa0, 0xf1, 0xf0, 0x7a, - 0xae, 0xb6, 0xa3, 0x75, 0x8c, 0x18, 0x27, 0xd4, 0xf1, 0xc6, 0x64, 0xd6, 0x4e, 0x03, 0x7b, 0xdd, - 0x7d, 0x1c, 0x04, 0xcc, 0xa2, 0xb2, 0x1a, 0xa4, 0x50, 0xbd, 0xae, 0x66, 0x26, 0x2f, 0xd4, 0xfe, - 0x87, 0xb9, 0x70, 0x12, 0xb4, 0x06, 0x8b, 0xaf, 0xbc, 0xb1, 0xc7, 0x4e, 0xbd, 0x4a, 0x06, 0x95, - 0xe1, 0x6a, 0xdf, 0x23, 0x47, 0xac, 0xef, 0x91, 0x0a, 0x40, 0x25, 0x98, 0x7f, 0xc2, 0xd9, 0xd4, - 0xaf, 0x64, 0xb5, 0xcf, 0x79, 0x88, 0x6e, 0x4f, 0xf3, 0xa7, 0xc7, 0xe6, 0x11, 0x2c, 0x04, 0x02, - 0x8b, 0x69, 0xa0, 0x40, 0xb9, 0xf6, 0xff, 0x7e, 0xb7, 0x3e, 0xfd, 0xa5, 0x84, 0x9b, 0x31, 0x0d, - 0xed, 0xc1, 0x62, 0xfc, 0x07, 0x56, 0x6c, 0xf9, 0xfe, 0x8d, 0x5b, 0x0a, 0xe1, 0xa1, 0xb1, 0xfa, - 0xee, 0xac, 0x06, 0x3e, 0x9c, 0xd5, 0x32, 0x66, 0x82, 0x47, 0x07, 0xf0, 0xaf, 0xf4, 0xc7, 0x69, - 0x40, 0x6d, 0x85, 0xc8, 0x3d, 0xd4, 0x52, 0x7b, 0x58, 0x5f, 0x0e, 0x86, 0xb9, 0x9e, 0xa6, 0xf4, - 0x6c, 0xb4, 0x0b, 0x61, 0xac, 0x17, 0xf2, 0x47, 0x92, 0xbf, 0x91, 0xe2, 0x97, 0xe2, 0x01, 0x7a, - 0x5d, 0xb3, 0x14, 0x03, 0x7b, 0x36, 0x3a, 0x07, 0xb0, 0x9c, 0x16, 0x52, 0x9c, 0xf8, 0xee, 0xee, - 0x74, 0x6f, 0x1c, 0x5e, 0x7c, 0xdf, 0x02, 0xd7, 0x73, 0xf5, 0x69, 0x14, 0x89, 0x43, 0xf3, 0xf1, - 0xf3, 0x7e, 0xa7, 0xdb, 0x1e, 0xe1, 0x49, 0x40, 0xf6, 0x97, 0x22, 0x7a, 0x57, 0x78, 0x97, 0x9d, - 0x68, 0xe6, 0xd2, 0x04, 0xda, 0x1e, 0x2c, 0x44, 0x57, 0xbb, 0x9c, 0xe7, 0x12, 0xcc, 0xf7, 0x4f, - 0x3d, 0xc2, 0x2b, 0x00, 0x41, 0x58, 0xe8, 0x58, 0x82, 0x9e, 0x90, 0x4a, 0x36, 0xc4, 0x18, 0x13, - 0x66, 0x8d, 0x89, 0x5d, 0x59, 0x31, 0x9a, 0x97, 0x8b, 0x3a, 0xf8, 0xb4, 0xa8, 0x83, 0xab, 0x45, - 0x1d, 0x5c, 0x7c, 0xad, 0x67, 0x5e, 0x6f, 0x46, 0x3e, 0x04, 0xb1, 0x8e, 0x5b, 0x16, 0xe3, 0xa4, - 0x15, 0x39, 0x1a, 0x16, 0x64, 0x28, 0x76, 0x7e, 0x04, 0x00, 0x00, 0xff, 0xff, 0x06, 0x17, 0x0a, - 0x8e, 0x1d, 0x06, 0x00, 0x00, + // 696 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x54, 0xcf, 0x6a, 0x13, 0x41, + 0x18, 0xcf, 0xa4, 0x4d, 0xd2, 0x4c, 0x4b, 0x0d, 0x43, 0x5a, 0xb7, 0x11, 0xb2, 0x61, 0x11, 0xcc, + 0x41, 0x36, 0xd0, 0xf6, 0x62, 0x8a, 0x94, 0xa4, 0x81, 0x12, 0x44, 0x8a, 0x6b, 0x7b, 0xf1, 0x12, + 0x26, 0xbb, 0x5f, 0xb6, 0x43, 0x92, 0x9d, 0x75, 0x76, 0x92, 0x9a, 0x73, 0x0f, 0x5e, 0xf5, 0xe6, + 0x23, 0x78, 0xf4, 0xe8, 0x0b, 0x08, 0x3d, 0xfa, 0x04, 0xb1, 0xc4, 0x17, 0x90, 0x3e, 0x81, 0xec, + 0xec, 0xae, 0xdd, 0x5a, 0x44, 0x8a, 0x57, 0x4f, 0xd9, 0xef, 0xfb, 0x7e, 0xbf, 0x5f, 0xbe, 0x3f, + 0x3f, 0x06, 0x6f, 0x81, 0x27, 0x99, 0x9c, 0x35, 0x6c, 0xee, 0x4d, 0x41, 0x04, 0x54, 0x32, 0xee, + 0x99, 0xbe, 0xe0, 0x92, 0x93, 0xb5, 0x3e, 0x08, 0x39, 0x33, 0x23, 0x40, 0xa5, 0xec, 0x72, 0x97, + 0xab, 0x42, 0x23, 0xfc, 0x8a, 0x30, 0x15, 0xdd, 0xe5, 0xdc, 0x1d, 0x41, 0x43, 0x45, 0xfd, 0xc9, + 0xa0, 0x21, 0xd9, 0x18, 0x02, 0x49, 0xc7, 0x7e, 0x0c, 0x78, 0xe0, 0x0f, 0xdd, 0xc6, 0x94, 0x8e, + 0x98, 0x43, 0x25, 0xfc, 0xfa, 0x88, 0x8b, 0x06, 0xf5, 0xd9, 0x35, 0xd5, 0x15, 0xd4, 0x3f, 0x7d, + 0x3d, 0x4a, 0x7e, 0x63, 0x4c, 0xf9, 0xba, 0x41, 0x49, 0x6d, 0xf9, 0x5b, 0x76, 0x0c, 0x41, 0x40, + 0xdd, 0x44, 0xaf, 0x14, 0x67, 0x41, 0x88, 0x28, 0x63, 0xbc, 0xcd, 0xe1, 0xb5, 0x83, 0xd4, 0x68, + 0x64, 0x17, 0x67, 0x99, 0xa3, 0xa1, 0x1a, 0xaa, 0x17, 0xdb, 0x0f, 0x2f, 0x7f, 0x6c, 0xa1, 0xcf, + 0xe7, 0x15, 0xb4, 0x98, 0xeb, 0xd9, 0x6e, 0xe7, 0x6a, 0xae, 0x13, 0x97, 0x8b, 0x71, 0xd3, 0xf0, + 0x05, 0x1b, 0x53, 0x31, 0xeb, 0x0d, 0x61, 0x66, 0x58, 0x59, 0xe6, 0x90, 0x03, 0x8c, 0x6d, 0x01, + 0x54, 0x82, 0xd3, 0xa3, 0x52, 0xcb, 0xd6, 0x50, 0x7d, 0x75, 0xbb, 0x62, 0x46, 0xb3, 0x9b, 0xc9, + 0x00, 0xe6, 0x71, 0x32, 0x7b, 0x7b, 0xe5, 0x62, 0xae, 0x67, 0xde, 0x7d, 0xd3, 0x91, 0x55, 0x8c, + 0x79, 0x2d, 0x19, 0x8a, 0x4c, 0x7c, 0x27, 0x11, 0x59, 0xba, 0x8b, 0x48, 0xcc, 0x6b, 0x49, 0xf2, + 0x14, 0x17, 0x04, 0x50, 0xa5, 0xb0, 0x7c, 0x07, 0x85, 0x7c, 0x48, 0x6a, 0x49, 0xb2, 0x8f, 0x57, + 0xce, 0x04, 0x97, 0x10, 0xf2, 0x73, 0x77, 0xe0, 0x17, 0x14, 0xab, 0x25, 0x49, 0x19, 0xe7, 0x24, + 0x93, 0x23, 0xd0, 0xca, 0xe1, 0x0a, 0xad, 0x28, 0x50, 0x59, 0xee, 0x33, 0x5b, 0xdb, 0x88, 0xb3, + 0x61, 0x10, 0x66, 0x99, 0x37, 0xe0, 0x81, 0xb6, 0x19, 0x65, 0x55, 0x40, 0x76, 0xf0, 0xf2, 0x90, + 0x79, 0x8e, 0x76, 0xbf, 0x86, 0xea, 0xeb, 0xdb, 0xba, 0x99, 0x76, 0x99, 0x99, 0xbe, 0x95, 0xf9, + 0x8c, 0x79, 0x8e, 0xa5, 0xc0, 0xe4, 0x3d, 0xc2, 0x85, 0x31, 0x8c, 0xfb, 0x20, 0x02, 0xcd, 0xa9, + 0x2d, 0xd5, 0x57, 0xb7, 0x6b, 0x7f, 0x26, 0x3e, 0x57, 0xc0, 0xf6, 0xc9, 0xd5, 0x5c, 0x7f, 0x11, + 0x1d, 0x74, 0xc0, 0x05, 0x30, 0xd7, 0x1b, 0xc2, 0xac, 0x99, 0x06, 0x76, 0x3b, 0x7b, 0x34, 0x08, + 0xb8, 0xcd, 0x54, 0xd4, 0x4b, 0xa1, 0xba, 0x9d, 0xbd, 0x80, 0x4e, 0xa1, 0x97, 0x2a, 0x07, 0x4d, + 0x29, 0x26, 0x60, 0x58, 0x49, 0x1f, 0xc6, 0x63, 0xbc, 0x1c, 0x76, 0x48, 0x56, 0x71, 0xe1, 0xc4, + 0x1b, 0x7a, 0xfc, 0xcc, 0x2b, 0x65, 0xc8, 0x1a, 0x5e, 0x39, 0xf2, 0xe0, 0x98, 0x1f, 0x79, 0x50, + 0x42, 0xa4, 0x88, 0x73, 0x87, 0x82, 0x4f, 0xfc, 0x52, 0xd6, 0xf8, 0x92, 0xc3, 0xe4, 0x76, 0x93, + 0xff, 0xfd, 0xf8, 0x8f, 0x7e, 0xdc, 0xc7, 0xf9, 0x40, 0x52, 0x39, 0x09, 0x34, 0xac, 0xfc, 0xf4, + 0xe8, 0x6f, 0xb6, 0x30, 0x5f, 0x2a, 0xb8, 0x15, 0xd3, 0xc8, 0x1b, 0x5c, 0x88, 0x9f, 0x16, 0xcd, + 0x51, 0x0d, 0x6c, 0xdc, 0x52, 0x08, 0x8b, 0xed, 0xc3, 0x8f, 0xe7, 0x15, 0xf4, 0xe9, 0xbc, 0x92, + 0xb9, 0x9a, 0xeb, 0x7b, 0xd1, 0x59, 0xd2, 0xde, 0xa1, 0x13, 0xc9, 0xa3, 0xad, 0x34, 0x07, 0x74, + 0x14, 0xc0, 0x0d, 0x63, 0x45, 0x3b, 0x4f, 0xfc, 0x13, 0xff, 0x1d, 0x39, 0xc0, 0xf7, 0xd2, 0xaf, + 0x6e, 0x8f, 0x39, 0x1a, 0x28, 0x1f, 0x54, 0x52, 0x3e, 0x58, 0xbf, 0xe9, 0x57, 0x6b, 0x3d, 0x4d, + 0xe9, 0x3a, 0x64, 0x17, 0xe3, 0x58, 0x2f, 0xe4, 0x0f, 0x14, 0x7f, 0x23, 0xc5, 0x2f, 0xc6, 0xfd, + 0x77, 0x3b, 0x56, 0x31, 0x06, 0x76, 0x1d, 0xe3, 0x09, 0xce, 0x47, 0x6b, 0xb8, 0x69, 0xde, 0x22, + 0xce, 0x1d, 0x9d, 0x79, 0x20, 0x4a, 0x88, 0x60, 0x9c, 0x6f, 0xd9, 0x92, 0x4d, 0xa1, 0x94, 0x0d, + 0x31, 0xed, 0x11, 0xb7, 0x87, 0xe0, 0x94, 0x96, 0xda, 0xf5, 0x8b, 0x45, 0x15, 0x7d, 0x5d, 0x54, + 0xd1, 0xe5, 0xa2, 0x8a, 0x3e, 0x7c, 0xaf, 0x66, 0x5e, 0x6d, 0x46, 0xfb, 0x92, 0x60, 0x9f, 0x36, + 0x6c, 0x2e, 0xa0, 0x11, 0x6d, 0xae, 0x9f, 0x57, 0x17, 0xdc, 0xf9, 0x19, 0x00, 0x00, 0xff, 0xff, + 0x0d, 0x6b, 0x49, 0xa4, 0x63, 0x06, 0x00, 0x00, } func (m *Conversation) Marshal() (dAtA []byte, err error) { @@ -412,6 +420,14 @@ func (m *Conversation) MarshalTo(dAtA []byte) (int, error) { return 0, err } i += n3 + dAtA[i] = 0x2a + i++ + i = encodeVarintConversation(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.WroteAt))) + n4, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.WroteAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n4 if len(m.Title) > 0 { dAtA[i] = 0xa2 i++ @@ -487,27 +503,35 @@ func (m *ConversationMember) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintConversation(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt))) - n4, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) + n5, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) if err != nil { return 0, err } - i += n4 + i += n5 dAtA[i] = 0x1a i++ i = encodeVarintConversation(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt))) - n5, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i:]) + n6, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i:]) if err != nil { return 0, err } - i += n5 + i += n6 dAtA[i] = 0x22 i++ i = encodeVarintConversation(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.ReadAt))) - n6, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ReadAt, dAtA[i:]) + n7, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ReadAt, dAtA[i:]) if err != nil { return 0, err } - i += n6 + i += n7 + dAtA[i] = 0x2a + i++ + i = encodeVarintConversation(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.WroteAt))) + n8, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.WroteAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n8 if m.Status != 0 { dAtA[i] = 0x50 i++ @@ -519,11 +543,11 @@ func (m *ConversationMember) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x6 i++ i = encodeVarintConversation(dAtA, i, uint64(m.Contact.Size())) - n7, err := m.Contact.MarshalTo(dAtA[i:]) + n9, err := m.Contact.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n7 + i += n9 } if len(m.ConversationID) > 0 { dAtA[i] = 0xaa @@ -541,18 +565,6 @@ func (m *ConversationMember) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintConversation(dAtA, i, uint64(len(m.ContactID))) i += copy(dAtA[i:], m.ContactID) } - if m.Conversation != nil { - dAtA[i] = 0xba - i++ - dAtA[i] = 0x6 - i++ - i = encodeVarintConversation(dAtA, i, uint64(m.Conversation.Size())) - n8, err := m.Conversation.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n8 - } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) } @@ -584,6 +596,8 @@ func (m *Conversation) Size() (n int) { n += 1 + l + sovConversation(uint64(l)) l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ReadAt) n += 1 + l + sovConversation(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.WroteAt) + n += 1 + l + sovConversation(uint64(l)) l = len(m.Title) if l > 0 { n += 2 + l + sovConversation(uint64(l)) @@ -627,6 +641,8 @@ func (m *ConversationMember) Size() (n int) { n += 1 + l + sovConversation(uint64(l)) l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ReadAt) n += 1 + l + sovConversation(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.WroteAt) + n += 1 + l + sovConversation(uint64(l)) if m.Status != 0 { n += 1 + sovConversation(uint64(m.Status)) } @@ -642,10 +658,6 @@ func (m *ConversationMember) Size() (n int) { if l > 0 { n += 2 + l + sovConversation(uint64(l)) } - if m.Conversation != nil { - l = m.Conversation.Size() - n += 2 + l + sovConversation(uint64(l)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -813,6 +825,36 @@ func (m *Conversation) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WroteAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConversation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConversation + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.WroteAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 20: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) @@ -1120,6 +1162,36 @@ func (m *ConversationMember) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WroteAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConversation + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConversation + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.WroteAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) @@ -1230,39 +1302,6 @@ func (m *ConversationMember) Unmarshal(dAtA []byte) error { } m.ContactID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 103: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Conversation", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowConversation - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthConversation - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Conversation == nil { - m.Conversation = &Conversation{} - } - if err := m.Conversation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipConversation(dAtA[iNdEx:]) diff --git a/core/entity/conversation.proto b/core/entity/conversation.proto index fa5b35cb02..bfba4bd4a0 100644 --- a/core/entity/conversation.proto +++ b/core/entity/conversation.proto @@ -22,6 +22,7 @@ message Conversation { google.protobuf.Timestamp created_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; google.protobuf.Timestamp updated_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; google.protobuf.Timestamp read_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + google.protobuf.Timestamp wrote_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; // // metadata @@ -38,7 +39,7 @@ message Conversation { // repeated ConversationMember members = 100 [ - (gogoproto.moretags) = "gorm:\"foreignkey:ConversationID;association_foreignkey:ID\"" + (gogoproto.moretags) = "gorm:\"foreignkey:ConversationID;association_foreignkey:ID;save_associations:true\"" ]; // HasMany to ConversationMember.Conversation enum Kind { @@ -57,6 +58,7 @@ message ConversationMember { google.protobuf.Timestamp created_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; google.protobuf.Timestamp updated_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; google.protobuf.Timestamp read_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + google.protobuf.Timestamp wrote_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; enum Status { Unknown = 0; @@ -78,6 +80,7 @@ message ConversationMember { // Contact contact = 100 [ + (gogoproto.moretags) = "gorm:\"association_autoupdate:false;association_create:true\"", (validate.required) = true, (validate.skip) = false ]; @@ -93,9 +96,4 @@ message ConversationMember { (validate.min_len) = 1, (gql.graphql_id) = true ]; - - Conversation conversation = 103 [ - (gogoproto.moretags) = "gorm:\"PRELOAD:false;foreignkey:ID;association_foreignkey:ConversationID\"", - (gql.graphql_unexported) = true - ]; } diff --git a/core/entity/conversation.validate.gen.go b/core/entity/conversation.validate.gen.go index 475ed8059c..5f9707120f 100644 --- a/core/entity/conversation.validate.gen.go +++ b/core/entity/conversation.validate.gen.go @@ -46,6 +46,14 @@ func (m *Conversation) Validate() error { } } + // handling field: WroteAt - name:"wrote_at" number:5 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".google.protobuf.Timestamp" json_name:"wroteAt" options:<65001:0 65010:1 > (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) + + if v, ok := interface{}(m.GetWroteAt()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return errors.Wrap(err, "embedded message verification failed: WroteAt") + } + } + // handling field: Title - name:"title" number:20 label:LABEL_OPTIONAL type:TYPE_STRING json_name:"title" (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) // handling field: Topic - name:"topic" number:21 label:LABEL_OPTIONAL type:TYPE_STRING json_name:"topic" (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) @@ -54,7 +62,7 @@ func (m *Conversation) Validate() error { // handling field: Kind - name:"kind" number:23 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".berty.entity.Conversation.Kind" json_name:"kind" (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) - // handling field: Members - name:"members" number:100 label:LABEL_REPEATED type:TYPE_MESSAGE type_name:".berty.entity.ConversationMember" json_name:"members" options:<65006:"gorm:\"foreignkey:ConversationID;association_foreignkey:ID\"" > (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) + // handling field: Members - name:"members" number:100 label:LABEL_REPEATED type:TYPE_MESSAGE type_name:".berty.entity.ConversationMember" json_name:"members" options:<65006:"gorm:\"foreignkey:ConversationID;association_foreignkey:ID;save_associations:true\"" > (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) if v, ok := interface{}(m.GetMembers()).(interface{ Validate() error }); ok { if err := v.Validate(); err != nil { @@ -95,9 +103,17 @@ func (m *ConversationMember) Validate() error { } } + // handling field: WroteAt - name:"wrote_at" number:5 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".google.protobuf.Timestamp" json_name:"wroteAt" options:<65001:0 65010:1 > (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) + + if v, ok := interface{}(m.GetWroteAt()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return errors.Wrap(err, "embedded message verification failed: WroteAt") + } + } + // handling field: Status - name:"status" number:10 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".berty.entity.ConversationMember.Status" json_name:"status" (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) - // handling field: Contact - name:"contact" number:100 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".berty.entity.Contact" json_name:"contact" options:<[]:true []:false > (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=true, min_items=0, max_items=0) + // handling field: Contact - name:"contact" number:100 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".berty.entity.Contact" json_name:"contact" options:<[]:true []:false 65006:"gorm:\"association_autoupdate:false;association_create:true\"" > (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=true, min_items=0, max_items=0) if m.GetContact() == nil { return errors.New("Contact is required") } @@ -111,14 +127,5 @@ func (m *ConversationMember) Validate() error { // handling field: ConversationID - name:"conversation_id" number:101 label:LABEL_OPTIONAL type:TYPE_STRING json_name:"conversationId" options:<53004:1 (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) // handling field: ContactID - name:"contact_id" number:102 label:LABEL_OPTIONAL type:TYPE_STRING json_name:"contactId" options:<53004:1 (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) - - // handling field: Conversation - name:"conversation" number:103 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".berty.entity.Conversation" json_name:"conversation" options:<53011:1 65006:"gorm:\"PRELOAD:false;foreignkey:ID;association_foreignkey:ConversationID\"" > (is_contact_key=false, defined_only=false, min_len=0, max_len=0, skip=false, required=false, min_items=0, max_items=0) - - if v, ok := interface{}(m.GetConversation()).(interface{ Validate() error }); ok { - if err := v.Validate(); err != nil { - return errors.Wrap(err, "embedded message verification failed: Conversation") - } - } - return nil } diff --git a/core/entity/conversation_test.go b/core/entity/conversation_test.go index 72e3d120c5..add750279a 100644 --- a/core/entity/conversation_test.go +++ b/core/entity/conversation_test.go @@ -21,24 +21,22 @@ func Test(t *testing.T) { bart = &Contact{ID: "bart", DisplayName: "bart", Status: Contact_IsRequested} lisa = &Contact{ID: "lisa", DisplayName: "lisa", Status: Contact_RequestedMe} - c *Conversation + c ConversationInteractor - active ConversationMemberInteractive - - mm ConversationMemberInformative - hm ConversationMemberInformative - bm ConversationMemberInformative - lm ConversationMemberInformative + mm ConversationMemberInteractor + hm ConversationMemberInteractor + bm ConversationMemberInteractor + lm ConversationMemberInteractor ) Convey("create 1to1 conversation between myself and homer", t, FailureHalts, func() { c, err = NewOneToOneConversation(myself, homer) So(err, ShouldBeNil) - mm, err = c.GetInteractiveMember(myself.ID) + mm, err = c.GetMember(myself.ID) So(err, ShouldBeNil) - hm, err = c.GetInteractiveMember(homer.ID) + hm, err = c.GetMember(homer.ID) So(err, ShouldBeNil) Convey("myself and homer are owners", FailureHalts, func() { @@ -47,25 +45,16 @@ func Test(t *testing.T) { }) Convey("myself try to block homer", FailureHalts, func() { - active, err = c.GetInteractiveMember(mm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Block(homer.ID) + err = mm.Block(homer.ID) So(err, ShouldNotBeNil) }) Convey("homer write a message", FailureHalts, func() { - active, err = c.GetInteractiveMember(hm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Write(&Message{Text: "D'oh!"}) + err = hm.Write(time.Now(), &Message{Text: "D'oh!"}) So(err, ShouldBeNil) Convey("myself read the message", FailureHalts, func() { - active, err := c.GetInteractiveMember(mm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Read(time.Now()) + err = mm.Read(time.Now()) So(err, ShouldBeNil) }) }) @@ -107,84 +96,55 @@ func Test(t *testing.T) { }) Convey("bart invite homer", FailureHalts, func() { - active, err = c.GetInteractiveMember(bm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Invite(homer) + err = bm.Invite(homer) So(err, ShouldBeNil) hm, err = c.GetMember(homer.ID) So(err, ShouldBeNil) Convey("homer write message", FailureHalts, func() { - active, err = c.GetInteractiveMember(hm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Write(&Message{Text: "D'oh!"}) + err = hm.Write(time.Now(), &Message{Text: "D'oh!"}) So(err, ShouldBeNil) }) Convey("bart write message", FailureHalts, func() { - active, err = c.GetInteractiveMember(bm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Write(&Message{Text: "¡ Ay, caramba!"}) + err = bm.Write(time.Now(), &Message{Text: "¡ Ay, caramba!"}) So(err, ShouldBeNil) }) Convey("myself block homer", FailureHalts, func() { - active, err = c.GetInteractiveMember(mm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Block(homer.ID) + err = mm.Block(homer.ID) So(err, ShouldBeNil) Convey("homer try to write a message", FailureHalts, func() { - active, err = c.GetInteractiveMember(hm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Write(&Message{Text: "D'oh!"}) + err = hm.Write(time.Now(), &Message{Text: "D'oh!"}) So(err, ShouldNotBeNil) }) Convey("bart try to unblock homer", FailureHalts, func() { - active, err = c.GetInteractiveMember(bm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Unblock(homer.ID) + err = bm.Unblock(homer.ID) So(err, ShouldNotBeNil) }) Convey("bart try to block myself", FailureHalts, func() { - active, err = c.GetInteractiveMember(bm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Block(myself.ID) + err = bm.Block(myself.ID) So(err, ShouldNotBeNil) }) Convey("bart try to set lisa as owner", FailureHalts, func() { - active, err = c.GetInteractiveMember(bm.GetContactID()) - So(err, ShouldBeNil) - - err = active.SetOwner(lisa.ID) + err = bm.SetOwner(lisa.ID) So(err, ShouldNotBeNil) }) Convey("myself set lisa as owner", FailureHalts, func() { - active, err = c.GetInteractiveMember(mm.GetContactID()) - So(err, ShouldBeNil) + err = mm.SetOwner(lisa.ID) - err = active.SetOwner(lisa.ID) So(err, ShouldBeNil) So(lm.IsOwner(), ShouldBeTrue) Convey("lisa unblock homer", FailureHalts, func() { - active, err = c.GetInteractiveMember(lm.GetContactID()) - So(err, ShouldBeNil) - - err = active.Unblock(homer.ID) + err = lm.Unblock(homer.ID) So(err, ShouldBeNil) }) }) diff --git a/core/node/event_handlers.go b/core/node/event_handlers.go index 2c9afa98dc..ecb9d1f25f 100644 --- a/core/node/event_handlers.go +++ b/core/node/event_handlers.go @@ -141,25 +141,31 @@ func (n *Node) handleConversationUpdate(ctx context.Context, input *entity.Event sql := n.sql(ctx) - update := attrs.Conversation + // find conversation + conversation := &entity.Conversation{ID: attrs.Conversation.ID} + if err = sql.Find(&conversation).Error; err != nil { + return err + } - cm := &entity.ConversationMember{ - ContactID: input.SourceContactID, - ConversationID: attrs.Conversation.ID, + // get interactive member + cm, err := conversation.GetMember(input.SourceContactID) + if err != nil { + return errorcodes.ErrNodeHandleConversationUpdate.Wrap(err) } + if err := sql.Preload("Conversation").First(cm).Error; err != nil { return bsql.GenericError(err) } - if err := cm.SetTitle(update.Title); err != nil { + if err := cm.SetTitle(attrs.Conversation.Title); err != nil { return errorcodes.ErrNodeHandleConversationUpdate.Wrap(err) } - if err := cm.SetTopic(update.Title); err != nil { + if err := cm.SetTopic(attrs.Conversation.Title); err != nil { return errorcodes.ErrNodeHandleConversationUpdate.Wrap(err) } - if err := bsql.ConversationSave(sql, cm.Conversation); err != nil { + if err := bsql.ConversationSave(sql, conversation); err != nil { return errorcodes.ErrNodeHandleConversationUpdate.Wrap(err) } @@ -228,14 +234,14 @@ func (n *Node) handleConversationNewMessage(ctx context.Context, input *entity.E } // get interactive member - im, err := conversation.GetInteractiveMember(input.SourceContactID) + im, err := conversation.GetMember(input.SourceContactID) if err != nil { n.LogBackgroundWarn(ctx, errors.New("handleConversationNewMessage: Member not found")) return err } // say that member have write to conversation - if err = im.Write(attrs.Message); err != nil { + if err = im.Write(input.CreatedAt, attrs.Message); err != nil { return err } @@ -290,7 +296,7 @@ func (n *Node) handleConversationRead(ctx context.Context, input *entity.Event) } // get interactive member - im, err := conversation.GetInteractiveMember(input.SourceContactID) + im, err := conversation.GetMember(input.SourceContactID) if err != nil { return err } diff --git a/core/node/nodeapi.go b/core/node/nodeapi.go index 0c50b76826..61c8ceffec 100644 --- a/core/node/nodeapi.go +++ b/core/node/nodeapi.go @@ -134,10 +134,10 @@ func (n *Node) EventSeen(ctx context.Context, input *entity.Event) (*entity.Even return nil, errorcodes.ErrDbUpdate.Wrap(err) } - // check if event is from another contact - if event.Direction != entity.Event_Incoming { - return event, nil - } + // // check if event is from another contact + // if event.Direction != entity.Event_Incoming { + // return event, nil + // } seenAt := time.Now().UTC() event.SeenAt = &seenAt @@ -147,14 +147,6 @@ func (n *Node) EventSeen(ctx context.Context, input *entity.Event) (*entity.Even return nil, errors.Wrap(err, "cannot set event as seen") } - // mark conversation as read - if event.TargetType == entity.Event_ToSpecificConversation { - _, err := n.ConversationRead(ctx, &entity.Conversation{ID: event.ToConversationID()}) - if err != nil { - return nil, err - } - } - return event, nil } @@ -258,7 +250,7 @@ func (n *Node) ContactRequest(ctx context.Context, req *node.ContactRequestInput } else if contact.Status == entity.Contact_Myself { return nil, errorcodes.ErrContactReqMyself.New() - } else { + } else if contact.Status != entity.Contact_Unknown { return nil, errorcodes.ErrContactReqExisting.New() } @@ -365,14 +357,9 @@ func (n *Node) ContactRemove(ctx context.Context, contact *entity.Contact) (*ent return nil, errorcodes.ErrDbDelete.Wrap(err) } - // remove 1-1 conversation - // don't return error if not found - conversation, err := bsql.ConversationOneToOne(sql, n.config.Myself.ID, contact.ID) - switch { - case err == nil: // conversation exists, delete it - n.ConversationRemove(ctx, &entity.Conversation{ID: conversation.ID}) - case gorm.IsRecordNotFoundError(errors.Cause(err)): // conversation is not found, do nothing - case err != nil: // another error is triggered, returning it + // remove one to one conversation + _, err = n.conversationRemove(ctx, &entity.Conversation{ID: entity.GetOneToOneID(n.config.Myself, contact)}) + if err != nil { return nil, err } diff --git a/core/node/nodeapi_conversation.go b/core/node/nodeapi_conversation.go index 13e5c05e45..c2b06bbd61 100644 --- a/core/node/nodeapi_conversation.go +++ b/core/node/nodeapi_conversation.go @@ -117,7 +117,7 @@ func (n *Node) ConversationInvite(ctx context.Context, input *node.ConversationM } // find interactive member (current node user) - cm, err := c.GetInteractiveMember(n.UserID()) + cm, err := c.GetMember(n.UserID()) if err != nil { return nil, err } @@ -228,13 +228,13 @@ func (n *Node) ConversationAddMessage(ctx context.Context, input *node.Conversat } // get interactive member (current user) - im, err := conversation.GetInteractiveMember(n.UserID()) + im, err := conversation.GetMember(n.UserID()) if err != nil { return nil, err } // member write new message - if err = im.Write(input.Message); err != nil { + if err = im.Write(time.Now().UTC(), input.Message); err != nil { return nil, err } @@ -301,7 +301,7 @@ func (n *Node) ConversationUpdate(ctx context.Context, input *entity.Conversatio } // get interactive member (current user) - im, err := conversation.GetInteractiveMember(n.UserID()) + im, err := conversation.GetMember(n.UserID()) if err != nil { return nil, err } @@ -339,7 +339,7 @@ func (n *Node) ConversationRead(ctx context.Context, input *entity.Conversation) } // get interactive member (current user) - im, err := conversation.GetInteractiveMember(n.UserID()) + im, err := conversation.GetMember(n.UserID()) if err != nil { return nil, err } @@ -370,7 +370,7 @@ func (n *Node) ConversationRead(ctx context.Context, input *entity.Conversation) return conversation, n.EnqueueOutgoingEvent(ctx, n.NewEvent(ctx). SetToConversation(conversation). - SetConversationReadAttrs(&entity.ConversationReadAttrs{Conversation: conversation})) + SetConversationReadAttrs(&entity.ConversationReadAttrs{Conversation: conversation.Filtered()})) } func (n *Node) ConversationLastEvent(ctx context.Context, input *entity.Conversation) (*entity.Event, error) { @@ -389,24 +389,37 @@ func (n *Node) ConversationLastEvent(ctx context.Context, input *entity.Conversa } func (n *Node) ConversationRemove(ctx context.Context, input *entity.Conversation) (*entity.Conversation, error) { + defer n.handleMutex(ctx)() + + return n.conversationRemove(ctx, input) +} + +func (n *Node) conversationRemove(ctx context.Context, input *entity.Conversation) (*entity.Conversation, error) { var err error + sql := n.sql(ctx) + // get conversation - if err = n.sql(ctx).First(input).Error; err != nil { - return nil, bsql.GenericError(err) + conversation := &entity.Conversation{ID: input.ID} + if err = sql.Model(conversation).Where(conversation).First(conversation).Error; err != nil { + return nil, err } - // remove conversation - sql := n.sql(ctx) - if err = sql. - Where(&entity.ConversationMember{ConversationID: input.ID}). - Delete(&entity.ConversationMember{}).Error; err != nil { - return nil, errorcodes.ErrDbDelete.Wrap(err) + // remove conversation before all + if err = sql.Delete(conversation).Error; err != nil { + return nil, err } - if err = sql.Delete(input).Error; err != nil { - return nil, errorcodes.ErrDbDelete.Wrap(err) + // get interactive member (current user) + im, err := conversation.GetMember(n.UserID()) + if err != nil { + return nil, err } - return input, nil + // leave conversation + if err = im.Leave(); err != nil { + return nil, err + } + + return conversation, nil } diff --git a/core/node/sql.go b/core/node/sql.go index ba71b732ef..51bcfc5137 100644 --- a/core/node/sql.go +++ b/core/node/sql.go @@ -123,12 +123,6 @@ func (n *Node) createCommitLog(scope *gorm.Scope, operation string, reflectValue case *entity.Config: log.Entity = &node.CommitLog_Entity{Config: data} case *entity.Event: - if operation != "delete" { - data, err = sql.EventByID(scope.DB(), data.ID) - if err != nil { - return nil - } - } log.Entity = &node.CommitLog_Entity{Event: data} case *entity.DevicePushConfig: log.Entity = &node.CommitLog_Entity{DevicePushConfig: data} diff --git a/core/pkg/errorcodes/errors.pb.go b/core/pkg/errorcodes/errors.pb.go index 21fa16e907..218c8bf5cb 100644 --- a/core/pkg/errorcodes/errors.pb.go +++ b/core/pkg/errorcodes/errors.pb.go @@ -118,7 +118,6 @@ const ( ErrConversationIsMember Code = 13204 ErrConversationGetMember Code = 13205 ErrConversationGetOwners Code = 13206 - ErrConversationGetInteractiveMember Code = 13207 ErrConversationMember Code = 13500 ErrConversationMemberID Code = 13501 ErrConversationMemberStatus Code = 13502 @@ -266,7 +265,6 @@ var Code_name = map[int32]string{ 13204: "ErrConversationIsMember", 13205: "ErrConversationGetMember", 13206: "ErrConversationGetOwners", - 13207: "ErrConversationGetInteractiveMember", 13500: "ErrConversationMember", 13501: "ErrConversationMemberID", 13502: "ErrConversationMemberStatus", @@ -414,7 +412,6 @@ var Code_value = map[string]int32{ "ErrConversationIsMember": 13204, "ErrConversationGetMember": 13205, "ErrConversationGetOwners": 13206, - "ErrConversationGetInteractiveMember": 13207, "ErrConversationMember": 13500, "ErrConversationMemberID": 13501, "ErrConversationMemberStatus": 13502, @@ -581,129 +578,128 @@ func init() { func init() { proto.RegisterFile("pkg/errorcodes/errors.proto", fileDescriptor_f04d618bf87de955) } var fileDescriptor_f04d618bf87de955 = []byte{ - // 1955 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x58, 0x6b, 0x8c, 0x5b, 0x47, - 0xf5, 0xcf, 0xdd, 0x24, 0xbb, 0xc9, 0xe4, 0xb1, 0x27, 0x93, 0x64, 0xd7, 0xdd, 0x24, 0xbb, 0xdb, - 0xf6, 0xff, 0x27, 0x51, 0x05, 0x1b, 0x14, 0xa4, 0x08, 0x21, 0x41, 0xba, 0xf6, 0xb5, 0x8d, 0x95, - 0xae, 0xe3, 0xae, 0xb3, 0x20, 0x21, 0x45, 0x68, 0x7c, 0xef, 0xd9, 0xeb, 0xd1, 0x5e, 0xcf, 0x35, - 0x73, 0xe7, 0xba, 0x31, 0x52, 0x25, 0xa8, 0x0c, 0xa4, 0xa5, 0x45, 0x4d, 0x5f, 0x14, 0x68, 0xcb, - 0x1b, 0xf1, 0x28, 0xef, 0x02, 0x2d, 0x34, 0xa5, 0xf0, 0xa9, 0x3c, 0x95, 0x96, 0x2f, 0x3c, 0xa5, - 0x92, 0x7e, 0x69, 0x4b, 0x29, 0x69, 0x01, 0x89, 0xc7, 0x17, 0x34, 0xf7, 0x8e, 0xed, 0xeb, 0xb5, - 0x77, 0x3f, 0xad, 0xe7, 0xf7, 0x3b, 0x73, 0xce, 0x6f, 0xce, 0x9c, 0x39, 0x33, 0x77, 0xc9, 0xa1, - 0xe6, 0x9a, 0x77, 0x1c, 0xa5, 0x0c, 0xa4, 0x13, 0xb8, 0x18, 0x26, 0x3f, 0xc3, 0x85, 0xa6, 0x0c, - 0x54, 0x40, 0xa1, 0x86, 0x52, 0xb5, 0x17, 0xfa, 0xf4, 0xcc, 0x9b, 0x3c, 0xae, 0xea, 0x51, 0x6d, - 0xc1, 0x09, 0x1a, 0xc7, 0xbd, 0xc0, 0x0b, 0x8e, 0xc7, 0x86, 0xb5, 0x68, 0x35, 0x1e, 0xc5, 0x83, - 0xf8, 0x57, 0xe2, 0xe0, 0xba, 0x57, 0xc7, 0xc8, 0x8e, 0x25, 0x54, 0xcc, 0x65, 0x8a, 0xd1, 0x1b, - 0xc8, 0x36, 0xed, 0x24, 0x63, 0xcd, 0x5b, 0xc7, 0xf6, 0x9e, 0x98, 0x5a, 0x58, 0xef, 0x7c, 0x21, - 0x17, 0xb8, 0xb8, 0x1c, 0xdb, 0xd0, 0xb7, 0x93, 0xbd, 0x78, 0x5e, 0xa1, 0x70, 0xd1, 0x7d, 0x6f, - 0x4c, 0x66, 0xc6, 0xe6, 0xb7, 0x6e, 0x32, 0x6b, 0x4f, 0xd7, 0x5a, 0x8f, 0x42, 0x5a, 0x21, 0xbb, - 0x9b, 0x3e, 0x73, 0xb0, 0x1e, 0xf8, 0x2e, 0xca, 0x30, 0xb3, 0x75, 0x7e, 0xeb, 0xb1, 0x5d, 0x27, - 0xde, 0x38, 0x3c, 0xb9, 0x2b, 0x6e, 0xa1, 0x92, 0x32, 0xcf, 0x0b, 0x25, 0xdb, 0xcb, 0x03, 0x1e, - 0xe8, 0x09, 0x32, 0xde, 0x64, 0x12, 0x85, 0xca, 0x6c, 0x9b, 0xb7, 0x8e, 0xed, 0x3a, 0x31, 0xb3, - 0xb1, 0xaf, 0x65, 0x63, 0x49, 0x33, 0x64, 0xa2, 0x81, 0x61, 0xc8, 0x3c, 0xcc, 0x6c, 0x9f, 0xb7, - 0x8e, 0xed, 0x5c, 0xee, 0x0e, 0xe9, 0x14, 0x19, 0x77, 0x98, 0xef, 0xa3, 0xcc, 0x8c, 0xc7, 0x84, - 0x19, 0xcd, 0x9c, 0x22, 0xfb, 0x86, 0x84, 0x50, 0x20, 0x5b, 0xd7, 0xb0, 0x1d, 0xa7, 0x6d, 0xe7, - 0xb2, 0xfe, 0x49, 0x0f, 0x90, 0xed, 0x2d, 0xe6, 0x47, 0x98, 0x19, 0x8b, 0xb1, 0x64, 0xf0, 0xb6, - 0xb1, 0xb7, 0x5a, 0x37, 0x3c, 0x7a, 0x84, 0x6c, 0xd3, 0x29, 0xa0, 0x40, 0x76, 0xe7, 0xa5, 0x5c, - 0x11, 0x2e, 0xae, 0x72, 0x81, 0x2e, 0x6c, 0xa1, 0xe3, 0x64, 0xec, 0xcc, 0x69, 0xb0, 0xe8, 0x2e, - 0x32, 0x91, 0x97, 0x32, 0xcb, 0x42, 0x84, 0x31, 0x7a, 0x80, 0x40, 0x6c, 0xc6, 0x1b, 0x4d, 0x1f, - 0x1b, 0x28, 0x14, 0xba, 0xb0, 0x95, 0x1e, 0x26, 0x99, 0xbc, 0x94, 0xcb, 0x4c, 0xb8, 0x41, 0xa3, - 0x88, 0x02, 0x25, 0x53, 0x81, 0x2c, 0x30, 0xee, 0xa3, 0x0b, 0xdb, 0xe8, 0x0c, 0x99, 0xd2, 0x73, - 0x56, 0x4a, 0xf6, 0x7a, 0x6e, 0xbb, 0xf1, 0x57, 0x45, 0xc9, 0x99, 0xcf, 0xdf, 0xcf, 0x14, 0x0f, - 0x04, 0x8c, 0xd3, 0x29, 0x42, 0xf3, 0x52, 0xda, 0x18, 0x0e, 0xe0, 0x13, 0x74, 0x37, 0xd9, 0x91, - 0x97, 0xb2, 0xc2, 0x04, 0x77, 0x60, 0x07, 0x05, 0xb2, 0x2b, 0x2f, 0x65, 0x5e, 0xb4, 0xd0, 0x0f, - 0x9a, 0x08, 0x2f, 0x4e, 0xd0, 0x6b, 0xc8, 0x81, 0x14, 0xb2, 0x22, 0x94, 0x8c, 0x42, 0xad, 0xf0, - 0xa5, 0x09, 0x7a, 0x24, 0x96, 0xd8, 0xa5, 0xca, 0x81, 0x8d, 0x2d, 0xee, 0x60, 0x21, 0x88, 0x84, - 0x0b, 0x2f, 0x4f, 0xd0, 0xbd, 0x64, 0x67, 0x4c, 0x2b, 0xae, 0xda, 0x70, 0x79, 0x92, 0xee, 0x27, - 0x7b, 0x7b, 0xe3, 0x92, 0x68, 0x46, 0x0a, 0x9e, 0x9d, 0xa4, 0x94, 0xec, 0xe9, 0x81, 0x36, 0x53, - 0x0c, 0x9e, 0x9b, 0xa4, 0x07, 0xc8, 0x64, 0x0f, 0xcb, 0x9f, 0xe7, 0xa1, 0x0a, 0xe1, 0x37, 0x93, - 0x74, 0x17, 0x19, 0xcf, 0x4b, 0x59, 0x46, 0x05, 0x8f, 0x4f, 0xd3, 0x6b, 0xc9, 0xe1, 0x64, 0xb0, - 0x28, 0x02, 0x55, 0x47, 0x99, 0xf3, 0x39, 0x0a, 0x95, 0x0b, 0x84, 0x40, 0x47, 0xab, 0x7b, 0x62, - 0x9a, 0xee, 0x8b, 0xb3, 0x5f, 0x46, 0x55, 0x55, 0x12, 0x59, 0x03, 0x7e, 0x38, 0x6d, 0x56, 0x57, - 0x46, 0x75, 0x73, 0x84, 0x11, 0xc2, 0x8f, 0xa6, 0xe9, 0x24, 0x21, 0x09, 0x62, 0x73, 0xe6, 0xc3, - 0x93, 0x29, 0xa0, 0xc2, 0x85, 0x07, 0x97, 0xa6, 0xcd, 0x2a, 0x34, 0x70, 0xa2, 0x02, 0x0f, 0x65, - 0xe8, 0x14, 0xd9, 0xd7, 0x1b, 0x97, 0x5c, 0x4c, 0x56, 0xf7, 0x70, 0x86, 0x4e, 0xc7, 0xf9, 0x4d, - 0xf0, 0x4a, 0x54, 0xf3, 0xb9, 0x73, 0x1a, 0xdb, 0xf0, 0x48, 0x86, 0xee, 0x89, 0x13, 0x9c, 0x6f, - 0xa1, 0x50, 0xf0, 0x99, 0xb9, 0x6e, 0x16, 0xf4, 0xb0, 0xaa, 0x4f, 0x8b, 0x84, 0xcf, 0xce, 0x19, - 0xad, 0x31, 0x18, 0x27, 0xe1, 0x73, 0x73, 0x26, 0x31, 0xef, 0x62, 0x3e, 0x77, 0x93, 0xad, 0xba, - 0x70, 0xd4, 0xc4, 0xe8, 0x63, 0x49, 0x16, 0x6f, 0x3f, 0x4a, 0x33, 0x64, 0xff, 0x00, 0xb1, 0xd4, - 0x0e, 0xd1, 0x5f, 0x85, 0x3b, 0x8e, 0x1a, 0x37, 0x15, 0xe6, 0x71, 0x91, 0xb8, 0xb9, 0xba, 0x40, - 0x09, 0xd9, 0xae, 0x4b, 0xa1, 0x06, 0xbf, 0x3d, 0x69, 0x52, 0x62, 0xd7, 0x72, 0x12, 0x99, 0x42, - 0xf8, 0x5d, 0x1f, 0x59, 0x69, 0xba, 0x1a, 0xf9, 0x7d, 0x1f, 0xb1, 0xd1, 0x47, 0x85, 0xf0, 0x87, - 0x93, 0x26, 0x09, 0x76, 0xad, 0x1c, 0xa8, 0x3a, 0x17, 0x5e, 0xb2, 0xe5, 0x7f, 0x3c, 0x69, 0x04, - 0xda, 0xb5, 0x92, 0x50, 0x28, 0x05, 0xf3, 0xf3, 0xfa, 0x64, 0xc2, 0x9f, 0x4e, 0x9a, 0x2c, 0xe6, - 0x64, 0xbb, 0xa9, 0x02, 0x78, 0xfa, 0x1d, 0x66, 0xc1, 0xc9, 0xb8, 0xca, 0x3d, 0xf8, 0xc2, 0x29, - 0x53, 0x68, 0x3d, 0x68, 0x89, 0x87, 0x0d, 0xa6, 0x9c, 0x3a, 0x7c, 0xf1, 0x94, 0x71, 0xdb, 0xa3, - 0x0a, 0x81, 0x6c, 0x30, 0x05, 0x5f, 0x3a, 0x45, 0x0f, 0xc6, 0xa5, 0x9e, 0x10, 0x79, 0xe1, 0xe8, - 0xbf, 0x70, 0xe1, 0xc6, 0x01, 0xd8, 0xc6, 0x04, 0x7e, 0xe5, 0xc6, 0x81, 0xa0, 0x7a, 0x73, 0x2e, - 0x2f, 0x9a, 0x52, 0xeb, 0x41, 0x45, 0x14, 0xf0, 0xec, 0xe2, 0x40, 0xbc, 0xd3, 0xd8, 0xb6, 0x51, - 0x37, 0x17, 0x78, 0x6e, 0xd1, 0xac, 0xbb, 0x27, 0xc4, 0xa9, 0x33, 0x2e, 0xe0, 0xb1, 0xac, 0xc9, - 0xf2, 0xa2, 0xe3, 0x2c, 0x31, 0xc1, 0x3c, 0x94, 0xf0, 0xe5, 0x02, 0x9d, 0x21, 0x07, 0x07, 0xb0, - 0x92, 0xe0, 0xaa, 0xac, 0xfd, 0x7c, 0xa5, 0x60, 0xc2, 0xf6, 0x39, 0xbb, 0x06, 0x5f, 0x2d, 0x98, - 0x5d, 0x4c, 0xa3, 0x7a, 0x0e, 0x7c, 0x6d, 0x14, 0x63, 0xcb, 0xa0, 0x09, 0x8f, 0x16, 0x8c, 0xd4, - 0x34, 0xb3, 0xc4, 0x3d, 0xf8, 0xfa, 0x28, 0xc2, 0xe6, 0x12, 0xbe, 0x51, 0xa0, 0x87, 0xe2, 0xd6, - 0xd1, 0x27, 0xca, 0x81, 0x3a, 0xd3, 0x44, 0xdd, 0x9f, 0xbe, 0x59, 0x30, 0x99, 0xeb, 0x93, 0xb9, - 0x55, 0x0f, 0xee, 0x2c, 0x9a, 0xbd, 0x19, 0x80, 0xcb, 0xac, 0x81, 0x70, 0x57, 0x91, 0xce, 0x91, - 0x99, 0xf5, 0x54, 0x85, 0x85, 0x61, 0xb3, 0x2e, 0x75, 0x77, 0xfb, 0x58, 0x71, 0x48, 0xbb, 0x9e, - 0x8b, 0x0a, 0xee, 0x2e, 0xd2, 0xc3, 0x64, 0x7a, 0x3d, 0x53, 0x94, 0x4d, 0xa7, 0x2a, 0x5b, 0x70, - 0xb1, 0x38, 0x24, 0x25, 0x1b, 0x28, 0xb8, 0x3a, 0x2c, 0x25, 0x1b, 0xa8, 0x38, 0x4b, 0xaf, 0x15, - 0x87, 0x56, 0x96, 0x0d, 0x54, 0xfe, 0x3c, 0xd7, 0xed, 0xe0, 0xf5, 0xa2, 0xd9, 0xa2, 0x5c, 0x20, - 0x14, 0x73, 0xd4, 0x32, 0xbe, 0x0f, 0xee, 0x2e, 0x77, 0xeb, 0xa4, 0x87, 0xe9, 0xa2, 0xb8, 0x58, - 0x36, 0xba, 0x06, 0xe0, 0x25, 0x1e, 0x86, 0xba, 0x21, 0xdc, 0x53, 0x36, 0xfb, 0xda, 0x67, 0xe3, - 0x0e, 0xa5, 0xb9, 0x7b, 0xcb, 0x66, 0xad, 0x7d, 0xce, 0x9c, 0xc3, 0xfb, 0xd6, 0x85, 0xba, 0x09, - 0x85, 0xa7, 0xea, 0x70, 0x7f, 0xb9, 0x5b, 0x92, 0x09, 0x5c, 0xb2, 0xe1, 0x81, 0xb2, 0xe9, 0x73, - 0x3a, 0xf1, 0xff, 0x5a, 0xe9, 0xaa, 0x5e, 0xf5, 0xba, 0x02, 0xfe, 0xbd, 0xd2, 0x9d, 0xb3, 0xea, - 0x19, 0xef, 0xff, 0x49, 0x99, 0x25, 0x2d, 0x38, 0x84, 0xff, 0xae, 0x98, 0x46, 0x93, 0xf4, 0x98, - 0x45, 0x9f, 0xb3, 0x10, 0x9e, 0x3f, 0x67, 0x0a, 0x38, 0x05, 0xea, 0x8a, 0xff, 0xf3, 0x39, 0xb3, - 0x8b, 0x29, 0xbc, 0x1c, 0xe4, 0x98, 0x70, 0x75, 0x3b, 0xc1, 0x10, 0xae, 0x8c, 0x30, 0x48, 0xda, - 0x83, 0xb9, 0x74, 0x5e, 0x38, 0xd7, 0x3d, 0x49, 0x81, 0x68, 0xa1, 0x0c, 0x93, 0x56, 0xf3, 0x0c, - 0x76, 0x0f, 0x4c, 0x0a, 0x2d, 0xd9, 0xf0, 0x33, 0xec, 0x1e, 0xf6, 0x14, 0x7e, 0x96, 0x2b, 0x1f, - 0xe1, 0xe7, 0x23, 0xa9, 0xa0, 0xc9, 0x1d, 0xf8, 0xc5, 0x28, 0xaa, 0x24, 0x56, 0x83, 0x10, 0x7e, - 0x89, 0xfd, 0xcc, 0xf7, 0xa8, 0xd3, 0x5c, 0xb8, 0xf0, 0x2b, 0x34, 0x55, 0x91, 0x66, 0x96, 0xb0, - 0x51, 0x43, 0x19, 0xc2, 0xaf, 0x91, 0xfe, 0x1f, 0x99, 0x1b, 0x49, 0x96, 0xc2, 0x33, 0x02, 0xcf, - 0x06, 0x67, 0x04, 0xc2, 0x45, 0x6f, 0x84, 0x8b, 0x52, 0x58, 0x94, 0x41, 0xd4, 0x84, 0x7b, 0xbc, - 0x7e, 0x3d, 0xa4, 0xc8, 0x42, 0xe4, 0xfb, 0x70, 0xaf, 0xd7, 0xaf, 0xa4, 0x14, 0x97, 0x04, 0x80, - 0xfb, 0x3c, 0x73, 0x7f, 0xa6, 0xd9, 0x22, 0x2a, 0x43, 0xdf, 0xbf, 0x01, 0x7d, 0xe6, 0x16, 0xa1, - 0xa5, 0x3f, 0xe0, 0xd1, 0x63, 0xe4, 0xfa, 0x61, 0x3a, 0x6e, 0xbc, 0xcc, 0x51, 0xbc, 0x85, 0xc6, - 0xd1, 0xc7, 0x47, 0x29, 0x34, 0xdc, 0x93, 0x7c, 0x84, 0x42, 0x93, 0x00, 0x1b, 0x2e, 0x71, 0x3a, - 0x4f, 0x0e, 0x8d, 0x64, 0xab, 0x8a, 0xa9, 0x28, 0x84, 0xa7, 0xb8, 0xb9, 0x88, 0x87, 0x2d, 0x4c, - 0x49, 0xc3, 0x8f, 0x39, 0xbd, 0x9e, 0xcc, 0x6e, 0x66, 0x52, 0xb2, 0xe1, 0x69, 0x4e, 0xdf, 0x40, - 0xae, 0xdd, 0xc8, 0xa8, 0x5f, 0x50, 0x3f, 0xe1, 0x23, 0x56, 0x3d, 0x6c, 0x57, 0xb2, 0xe1, 0xa7, - 0x1b, 0x6b, 0x2f, 0x89, 0x16, 0x57, 0x08, 0x1f, 0x5a, 0x33, 0x35, 0x3d, 0x6c, 0x71, 0x13, 0xb2, - 0x16, 0xc2, 0x87, 0xd7, 0xe8, 0x75, 0xe4, 0xc8, 0xe8, 0xe5, 0xa3, 0x4a, 0xca, 0xf5, 0x23, 0x9b, - 0xdb, 0xc4, 0x75, 0x7b, 0x61, 0x53, 0x9b, 0x78, 0x3f, 0xe1, 0xf6, 0x8d, 0xc5, 0x64, 0xfd, 0xc0, - 0x59, 0x83, 0x3b, 0xd6, 0x36, 0xcc, 0xf4, 0x8a, 0xa8, 0xc5, 0x26, 0x1f, 0x5d, 0xa3, 0xb3, 0xe4, - 0x9a, 0x91, 0x26, 0xcb, 0xc8, 0x5c, 0xb8, 0x73, 0xe3, 0x18, 0xef, 0x96, 0x3a, 0x23, 0x77, 0xad, - 0xd1, 0xdd, 0xf1, 0xbb, 0xb4, 0x12, 0x85, 0x75, 0xf8, 0x56, 0xc3, 0x94, 0xbd, 0x1e, 0xad, 0x88, - 0x35, 0x11, 0xdc, 0x22, 0x2a, 0x32, 0x68, 0x71, 0xfd, 0x64, 0xf9, 0x76, 0xc3, 0x9c, 0x77, 0x4d, - 0xf6, 0xd0, 0xef, 0x34, 0x4c, 0xcd, 0x6a, 0xb4, 0x24, 0x5a, 0xfa, 0x39, 0x92, 0x3c, 0x91, 0x56, - 0x39, 0x4a, 0xf8, 0x6e, 0xc3, 0x08, 0x48, 0xd1, 0x55, 0x94, 0xad, 0x78, 0xf3, 0x56, 0xb9, 0x07, - 0x8f, 0xa5, 0x43, 0x9a, 0x86, 0x97, 0x8d, 0x84, 0xeb, 0x63, 0xc9, 0x85, 0xef, 0x35, 0xcc, 0xf2, - 0x52, 0x7a, 0x6c, 0xd4, 0x9d, 0x37, 0xa9, 0x8d, 0xef, 0xa7, 0xbd, 0x67, 0x65, 0xc0, 0x5c, 0x87, - 0x85, 0x2a, 0x15, 0xfe, 0x07, 0x0d, 0x73, 0x27, 0xa6, 0xc2, 0x9f, 0x6d, 0x37, 0x11, 0x1e, 0x6f, - 0x98, 0xee, 0x3c, 0x30, 0x13, 0x9e, 0xe8, 0xae, 0xb1, 0x1c, 0x68, 0x0f, 0x4e, 0x12, 0xe6, 0xc1, - 0xc8, 0xc8, 0x48, 0xa3, 0x4b, 0x4c, 0x86, 0x75, 0xe6, 0x17, 0x72, 0x4b, 0xf0, 0x89, 0xc8, 0xc8, - 0x18, 0xc1, 0x2f, 0x56, 0xca, 0x55, 0xf8, 0xe4, 0x26, 0x06, 0x4b, 0x37, 0x9f, 0x3d, 0x0b, 0x9f, - 0x8a, 0x4c, 0xe9, 0xa6, 0x0d, 0x56, 0x44, 0xa3, 0x1f, 0xe3, 0xa1, 0xa8, 0xfb, 0xfe, 0x1d, 0x65, - 0x11, 0x47, 0x79, 0x78, 0x53, 0x93, 0x38, 0xce, 0x23, 0x91, 0xc9, 0x76, 0xda, 0x64, 0x19, 0x1d, - 0xe4, 0x2d, 0x84, 0x4f, 0x47, 0xa6, 0x16, 0xe2, 0x17, 0xcb, 0x07, 0x6e, 0x35, 0xfd, 0x55, 0x8f, - 0x74, 0x96, 0xe2, 0x29, 0xd5, 0xa8, 0x06, 0x1f, 0xbc, 0x95, 0xfe, 0x7f, 0xdc, 0x42, 0x35, 0xf3, - 0x4e, 0xa6, 0xf7, 0x2a, 0x5d, 0x5e, 0xe6, 0x0d, 0x79, 0xe9, 0x36, 0x6b, 0x53, 0x33, 0x73, 0x24, - 0x9f, 0xba, 0xcd, 0xa2, 0x7b, 0xe3, 0xd7, 0x72, 0xae, 0x1e, 0x89, 0x35, 0x78, 0xb1, 0x63, 0xd1, - 0xa9, 0xe4, 0xde, 0xd4, 0xe3, 0x2c, 0x73, 0x4b, 0xc2, 0xc5, 0xf3, 0xf0, 0x52, 0xc7, 0xa2, 0x07, - 0x93, 0xeb, 0xc6, 0xe0, 0xf1, 0xa3, 0xf9, 0xe5, 0x8e, 0x45, 0x33, 0xc9, 0xcb, 0xcd, 0xc0, 0x55, - 0x9f, 0x3b, 0x58, 0xb2, 0xe1, 0x2f, 0x1d, 0x8b, 0x1e, 0x4e, 0x7a, 0x78, 0x9a, 0x31, 0xd7, 0xf0, - 0x2b, 0x1d, 0x8b, 0xce, 0x26, 0xbd, 0x56, 0xb3, 0x31, 0x55, 0x0e, 0x54, 0x2e, 0xd0, 0xdf, 0x63, - 0x0a, 0xe1, 0xaf, 0x43, 0x32, 0x62, 0x13, 0x78, 0xb5, 0x63, 0xd1, 0xb9, 0xe4, 0xc4, 0x69, 0xdc, - 0x66, 0xd8, 0x08, 0x44, 0x55, 0x05, 0x92, 0x79, 0x58, 0x61, 0xaa, 0x0e, 0x7f, 0xeb, 0x58, 0x74, - 0x3e, 0x39, 0x72, 0x7d, 0x03, 0xfd, 0xd6, 0xd2, 0x72, 0x6b, 0xfa, 0x79, 0x74, 0xb5, 0x63, 0xd1, - 0xfd, 0xc9, 0xdd, 0xdd, 0x0b, 0x0d, 0xaf, 0x0d, 0x2e, 0x2f, 0xfe, 0x98, 0x08, 0xeb, 0xf0, 0x7a, - 0xc7, 0xa2, 0x34, 0xb9, 0xfa, 0x63, 0x5b, 0xdd, 0xa3, 0xfe, 0xde, 0xb1, 0xe8, 0xa1, 0xa4, 0xbb, - 0x6b, 0xac, 0xc0, 0x45, 0xa2, 0x2d, 0xdb, 0x2e, 0xd9, 0xf0, 0x8f, 0x61, 0x72, 0xd1, 0xf7, 0x63, - 0x3e, 0x84, 0x7f, 0x76, 0xac, 0x99, 0x6d, 0x17, 0x3e, 0x3f, 0xbb, 0x25, 0xfb, 0xe6, 0x67, 0xae, - 0xcc, 0x5a, 0x97, 0xaf, 0xcc, 0x5a, 0xcf, 0x5f, 0x99, 0xb5, 0x1e, 0x7c, 0x61, 0x76, 0xcb, 0x7b, - 0x66, 0x93, 0xcf, 0x6a, 0x85, 0x4e, 0xfd, 0xb8, 0x13, 0x48, 0x3c, 0x3e, 0xf8, 0xff, 0x89, 0xda, - 0x78, 0xfc, 0x8f, 0x85, 0xb7, 0xfc, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x00, 0x49, 0x59, 0x2e, 0xb8, + // 1939 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x58, 0x6b, 0x6c, 0x5c, 0x47, + 0x15, 0xee, 0x75, 0x12, 0x3b, 0x99, 0x3c, 0x7c, 0x32, 0x49, 0xec, 0xad, 0x93, 0xd8, 0x6e, 0x0b, + 0x24, 0xaa, 0xc0, 0x41, 0x41, 0x8a, 0x10, 0x12, 0xa4, 0xde, 0xbd, 0xbb, 0xcb, 0x2a, 0xf5, 0x66, + 0xeb, 0x8d, 0x41, 0x42, 0x8a, 0xd0, 0xec, 0xbd, 0xc7, 0xbb, 0x23, 0xdf, 0x9d, 0x59, 0xe6, 0xce, + 0xdd, 0xc6, 0x48, 0x95, 0x20, 0x5a, 0x20, 0x2d, 0x2d, 0x6a, 0xfa, 0x52, 0x81, 0xb6, 0xbc, 0x11, + 0xef, 0x67, 0x81, 0x16, 0x9a, 0x52, 0xf8, 0x55, 0x9e, 0x4a, 0xcb, 0x1f, 0x9e, 0x52, 0x49, 0xff, + 0xb4, 0xa5, 0x94, 0xb4, 0x80, 0xc4, 0xe3, 0x0f, 0x9a, 0x7b, 0x67, 0x77, 0xef, 0xda, 0x6b, 0xf3, + 0xcb, 0x3b, 0xdf, 0xf7, 0xcd, 0x39, 0x67, 0xce, 0x9c, 0x39, 0x33, 0xd7, 0xe4, 0x60, 0x6b, 0xa5, + 0x7e, 0x0c, 0x95, 0x92, 0xca, 0x93, 0x3e, 0x86, 0xc9, 0xcf, 0x70, 0xae, 0xa5, 0xa4, 0x96, 0x14, + 0x6a, 0xa8, 0xf4, 0xea, 0x5c, 0x9f, 0x9e, 0x7a, 0x53, 0x9d, 0xeb, 0x46, 0x54, 0x9b, 0xf3, 0x64, + 0xf3, 0x58, 0x5d, 0xd6, 0xe5, 0xb1, 0x58, 0x58, 0x8b, 0x96, 0xe3, 0x51, 0x3c, 0x88, 0x7f, 0x25, + 0x06, 0xae, 0x7f, 0x65, 0x84, 0x6c, 0x5f, 0x40, 0xcd, 0x7c, 0xa6, 0x19, 0xbd, 0x91, 0x6c, 0x35, + 0x46, 0x32, 0xce, 0xac, 0x73, 0x74, 0xcf, 0xf1, 0x89, 0xb9, 0xb5, 0xc6, 0xe7, 0x72, 0xd2, 0xc7, + 0xc5, 0x58, 0x43, 0xdf, 0x4e, 0xf6, 0xe0, 0x39, 0x8d, 0xc2, 0x47, 0xff, 0xbd, 0x31, 0x99, 0x19, + 0x99, 0xdd, 0xb2, 0xc9, 0xac, 0xdd, 0x5d, 0xb5, 0x19, 0x85, 0xb4, 0x42, 0x76, 0xb5, 0x02, 0xe6, + 0x61, 0x43, 0x06, 0x3e, 0xaa, 0x30, 0xb3, 0x65, 0x76, 0xcb, 0xd1, 0x9d, 0xc7, 0xdf, 0xb8, 0x7e, + 0x72, 0x37, 0xb8, 0xb9, 0x4a, 0x4a, 0x9e, 0x17, 0x5a, 0xad, 0x2e, 0x0e, 0x58, 0xa0, 0xc7, 0xc9, + 0x68, 0x8b, 0x29, 0x14, 0x3a, 0xb3, 0x75, 0xd6, 0x39, 0xba, 0xf3, 0xf8, 0xd4, 0xc6, 0xb6, 0x16, + 0xad, 0x92, 0x66, 0xc8, 0x58, 0x13, 0xc3, 0x90, 0xd5, 0x31, 0xb3, 0x6d, 0xd6, 0x39, 0xba, 0x63, + 0xb1, 0x3b, 0xa4, 0x13, 0x64, 0xd4, 0x63, 0x41, 0x80, 0x2a, 0x33, 0x1a, 0x13, 0x76, 0x34, 0x75, + 0x92, 0xec, 0x5d, 0x17, 0x08, 0x05, 0xb2, 0x65, 0x05, 0x57, 0xe3, 0xb4, 0xed, 0x58, 0x34, 0x3f, + 0xe9, 0x7e, 0xb2, 0xad, 0xcd, 0x82, 0x08, 0x33, 0x23, 0x31, 0x96, 0x0c, 0xde, 0x36, 0xf2, 0x56, + 0xe7, 0xc6, 0xf3, 0x87, 0xc9, 0x56, 0x93, 0x02, 0x0a, 0x64, 0x57, 0x5e, 0xa9, 0x25, 0xe1, 0xe3, + 0x32, 0x17, 0xe8, 0xc3, 0x35, 0x74, 0x94, 0x8c, 0x9c, 0x3e, 0x05, 0x0e, 0xdd, 0x49, 0xc6, 0xf2, + 0x4a, 0x65, 0x59, 0x88, 0x30, 0x42, 0xf7, 0x13, 0x88, 0x65, 0xbc, 0xd9, 0x0a, 0xb0, 0x89, 0x42, + 0xa3, 0x0f, 0x5b, 0xe8, 0x21, 0x92, 0xc9, 0x2b, 0xb5, 0xc8, 0x84, 0x2f, 0x9b, 0x45, 0x14, 0xa8, + 0x98, 0x96, 0xaa, 0xc0, 0x78, 0x80, 0x3e, 0x6c, 0xa5, 0x53, 0x64, 0xc2, 0xcc, 0x59, 0x2a, 0xb9, + 0x6b, 0xb9, 0x6d, 0xd6, 0x5e, 0x15, 0x15, 0x67, 0x01, 0x7f, 0x3f, 0xd3, 0x5c, 0x0a, 0x18, 0xa5, + 0x13, 0x84, 0xe6, 0x95, 0x72, 0x31, 0x1c, 0xc0, 0xc7, 0xe8, 0x2e, 0xb2, 0x3d, 0xaf, 0x54, 0x85, + 0x09, 0xee, 0xc1, 0x76, 0x0a, 0x64, 0x67, 0x5e, 0xa9, 0xbc, 0x68, 0x63, 0x20, 0x5b, 0x08, 0x2f, + 0x8c, 0xd1, 0x6b, 0xc9, 0xfe, 0x14, 0xb2, 0x24, 0xb4, 0x8a, 0x42, 0x13, 0xe1, 0x8b, 0x63, 0xf4, + 0x70, 0x1c, 0x62, 0x97, 0x2a, 0x4b, 0x17, 0xdb, 0xdc, 0xc3, 0x82, 0x8c, 0x84, 0x0f, 0x2f, 0x8d, + 0xd1, 0x3d, 0x64, 0x47, 0x4c, 0x6b, 0xae, 0x57, 0xe1, 0xf2, 0x38, 0xdd, 0x47, 0xf6, 0xf4, 0xc6, + 0x25, 0xd1, 0x8a, 0x34, 0x3c, 0x33, 0x4e, 0x29, 0xd9, 0xdd, 0x03, 0x5d, 0xa6, 0x19, 0x3c, 0x3b, + 0x4e, 0xf7, 0x93, 0xf1, 0x1e, 0x96, 0x3f, 0xc7, 0x43, 0x1d, 0xc2, 0x6f, 0xc6, 0xe9, 0x4e, 0x32, + 0x9a, 0x57, 0xaa, 0x8c, 0x1a, 0x1e, 0x9b, 0xa4, 0xd7, 0x91, 0x43, 0xc9, 0x60, 0x5e, 0x48, 0xdd, + 0x40, 0x95, 0x0b, 0x38, 0x0a, 0x9d, 0x93, 0x42, 0xa0, 0x67, 0xa2, 0x7b, 0x7c, 0x92, 0xee, 0x8d, + 0xb3, 0x5f, 0x46, 0x5d, 0xd5, 0x0a, 0x59, 0x13, 0x7e, 0x38, 0x69, 0x57, 0x57, 0x46, 0x7d, 0x4b, + 0x84, 0x11, 0xc2, 0x8f, 0x26, 0xe9, 0x38, 0x21, 0x09, 0xe2, 0x72, 0x16, 0xc0, 0x13, 0x29, 0xa0, + 0xc2, 0x45, 0x1d, 0x2e, 0x4d, 0xda, 0x55, 0x18, 0xe0, 0x78, 0x05, 0x1e, 0xca, 0xd0, 0x09, 0xb2, + 0xb7, 0x37, 0x2e, 0xf9, 0x98, 0xac, 0xee, 0xe1, 0x0c, 0x9d, 0x8c, 0xf3, 0x9b, 0xe0, 0x95, 0xa8, + 0x16, 0x70, 0xef, 0x14, 0xae, 0xc2, 0x23, 0x19, 0xba, 0x3b, 0x4e, 0x70, 0xbe, 0x8d, 0x42, 0xc3, + 0xa7, 0x67, 0xba, 0x59, 0x30, 0xc3, 0xaa, 0x39, 0x2d, 0x0a, 0x3e, 0x33, 0x63, 0x63, 0x8d, 0xc1, + 0x38, 0x09, 0x9f, 0x9d, 0xb1, 0x89, 0x79, 0x17, 0x0b, 0xb8, 0x9f, 0x6c, 0xd5, 0x85, 0x23, 0xd6, + 0x47, 0x1f, 0x4b, 0xb2, 0x78, 0xfb, 0x11, 0x9a, 0x21, 0xfb, 0x06, 0x88, 0x85, 0xd5, 0x10, 0x83, + 0x65, 0xb8, 0xe3, 0x88, 0x35, 0x53, 0x61, 0x75, 0x2e, 0x12, 0x33, 0x57, 0xe7, 0x28, 0x21, 0xdb, + 0x4c, 0x29, 0xd4, 0xe0, 0xb7, 0x27, 0x6c, 0x4a, 0xdc, 0x5a, 0x4e, 0x21, 0xd3, 0x08, 0xbf, 0xeb, + 0x23, 0x4b, 0x2d, 0xdf, 0x20, 0xbf, 0xef, 0x23, 0x2e, 0x06, 0xa8, 0x11, 0xfe, 0x70, 0xc2, 0x26, + 0xc1, 0xad, 0x95, 0xa5, 0x6e, 0x70, 0x51, 0x4f, 0xb6, 0xfc, 0x8f, 0x27, 0x6c, 0x80, 0x6e, 0xad, + 0x24, 0x34, 0x2a, 0xc1, 0x82, 0xbc, 0x39, 0x99, 0xf0, 0xa7, 0x13, 0x36, 0x8b, 0x39, 0xb5, 0xda, + 0xd2, 0x12, 0x9e, 0x7a, 0x87, 0x5d, 0x70, 0x32, 0xae, 0xf2, 0x3a, 0x7c, 0xfe, 0xa4, 0x2d, 0xb4, + 0x1e, 0xb4, 0xc0, 0xc3, 0x26, 0xd3, 0x5e, 0x03, 0xbe, 0x70, 0xd2, 0x9a, 0xed, 0x51, 0x05, 0xa9, + 0x9a, 0x4c, 0xc3, 0x17, 0x4f, 0xd2, 0x03, 0x71, 0xa9, 0x27, 0x44, 0x5e, 0x78, 0xe6, 0x2f, 0x5c, + 0xb8, 0x69, 0x00, 0x76, 0x31, 0x81, 0x5f, 0xbe, 0x69, 0xc0, 0xa9, 0xd9, 0x9c, 0xcb, 0xf3, 0xb6, + 0xd4, 0x7a, 0x50, 0x11, 0x05, 0x3c, 0x33, 0x3f, 0xe0, 0xef, 0x14, 0xae, 0xba, 0x68, 0x9a, 0x0b, + 0x3c, 0x3b, 0x6f, 0xd7, 0xdd, 0x0b, 0xc4, 0x6b, 0x30, 0x2e, 0xe0, 0xd1, 0xac, 0xcd, 0xf2, 0xbc, + 0xe7, 0x2d, 0x30, 0xc1, 0xea, 0xa8, 0xe0, 0x4b, 0x05, 0x3a, 0x45, 0x0e, 0x0c, 0x60, 0x25, 0xc1, + 0x75, 0xd9, 0xd8, 0xf9, 0x72, 0xc1, 0xba, 0xed, 0x73, 0x6e, 0x0d, 0xbe, 0x52, 0xb0, 0xbb, 0x98, + 0x46, 0xcd, 0x1c, 0xf8, 0xea, 0x30, 0xc6, 0x55, 0xb2, 0x05, 0x5f, 0x2b, 0xd8, 0x50, 0xd3, 0xcc, + 0x02, 0xaf, 0xc3, 0xd7, 0x87, 0x11, 0x2e, 0x57, 0xf0, 0x8d, 0x02, 0x3d, 0x18, 0xb7, 0x8e, 0x3e, + 0x51, 0x96, 0xfa, 0x74, 0x0b, 0x4d, 0x7f, 0xfa, 0x66, 0xc1, 0x66, 0xae, 0x4f, 0xe6, 0x96, 0xeb, + 0x70, 0x67, 0xd1, 0xee, 0xcd, 0x00, 0x5c, 0x66, 0x4d, 0x84, 0xbb, 0x8a, 0x74, 0x86, 0x4c, 0xad, + 0xa5, 0x2a, 0x2c, 0x0c, 0x5b, 0x0d, 0x65, 0xba, 0xdb, 0xc7, 0x8a, 0xeb, 0x62, 0x37, 0x73, 0x51, + 0xc3, 0xdd, 0x45, 0x7a, 0x88, 0x4c, 0xae, 0x65, 0x8a, 0xaa, 0xe5, 0x55, 0x55, 0x1b, 0x2e, 0x16, + 0xd7, 0x85, 0x92, 0x95, 0x1a, 0xae, 0xae, 0x0f, 0x25, 0x2b, 0x75, 0x9c, 0xa5, 0x57, 0x8b, 0xeb, + 0x56, 0x96, 0x95, 0x3a, 0x7f, 0x8e, 0x9b, 0x76, 0xf0, 0x5a, 0xd1, 0x6e, 0x51, 0x4e, 0x0a, 0xcd, + 0x3c, 0xbd, 0x88, 0xef, 0x83, 0xbb, 0xcb, 0xdd, 0x3a, 0xe9, 0x61, 0xa6, 0x28, 0x2e, 0x96, 0x6d, + 0x5c, 0x03, 0xf0, 0x02, 0x0f, 0x43, 0xd3, 0x10, 0xee, 0x29, 0xdb, 0x7d, 0xed, 0xb3, 0x71, 0x87, + 0x32, 0xdc, 0xbd, 0x65, 0xbb, 0xd6, 0x3e, 0x67, 0xcf, 0xe1, 0x7d, 0x6b, 0x5c, 0xdd, 0x8c, 0xa2, + 0xae, 0x1b, 0x70, 0x7f, 0xb9, 0x5b, 0x92, 0x09, 0x5c, 0x72, 0xe1, 0x81, 0xb2, 0xed, 0x73, 0x26, + 0xf1, 0xff, 0x5a, 0xea, 0x46, 0xbd, 0x5c, 0xef, 0x06, 0xf0, 0xef, 0xa5, 0xee, 0x9c, 0xe5, 0xba, + 0xb5, 0xfe, 0x9f, 0x94, 0x2c, 0x69, 0xc1, 0x21, 0xfc, 0x77, 0xc9, 0x36, 0x9a, 0xa4, 0xc7, 0xcc, + 0x07, 0x9c, 0x85, 0xf0, 0xdc, 0x59, 0x5b, 0xc0, 0x29, 0xd0, 0x54, 0xfc, 0x9f, 0xcf, 0xda, 0x5d, + 0x4c, 0xe1, 0x65, 0x99, 0x63, 0xc2, 0x37, 0xed, 0x04, 0x43, 0xb8, 0x32, 0x44, 0x90, 0xb4, 0x07, + 0x7b, 0xe9, 0x3c, 0x7f, 0xb6, 0x7b, 0x92, 0xa4, 0x68, 0xa3, 0x0a, 0x93, 0x56, 0xf3, 0x34, 0x76, + 0x0f, 0x4c, 0x0a, 0x2d, 0xb9, 0xf0, 0x33, 0xec, 0x1e, 0xf6, 0x14, 0x7e, 0x86, 0xeb, 0x00, 0xe1, + 0xe7, 0x43, 0x29, 0xd9, 0xe2, 0x1e, 0xfc, 0x62, 0x18, 0x55, 0x12, 0xcb, 0x32, 0x84, 0x5f, 0x62, + 0x3f, 0xf3, 0x3d, 0xea, 0x14, 0x17, 0x3e, 0xfc, 0x0a, 0x6d, 0x55, 0xa4, 0x99, 0x05, 0x6c, 0xd6, + 0x50, 0x85, 0xf0, 0x6b, 0xa4, 0xaf, 0x23, 0x33, 0x43, 0xc9, 0x52, 0x78, 0x5a, 0xe0, 0x19, 0x79, + 0x5a, 0x20, 0x5c, 0xac, 0x0f, 0x31, 0x51, 0x0a, 0x8b, 0x4a, 0x46, 0x2d, 0xb8, 0xa7, 0xde, 0xaf, + 0x87, 0x14, 0x59, 0x88, 0x82, 0x00, 0xee, 0xad, 0xf7, 0x2b, 0x29, 0xc5, 0x25, 0x0e, 0xe0, 0xbe, + 0xba, 0xbd, 0x3f, 0xd3, 0x6c, 0x11, 0xb5, 0xa5, 0xef, 0xdf, 0x80, 0x3e, 0x7d, 0xab, 0x30, 0xa1, + 0x3f, 0x30, 0xcc, 0xaf, 0x9d, 0xfa, 0x04, 0x1f, 0xe2, 0xd7, 0x2e, 0xcb, 0x85, 0x4b, 0x9c, 0xce, + 0x92, 0x83, 0x43, 0xd9, 0xaa, 0x66, 0x3a, 0x0a, 0xe1, 0x49, 0x6e, 0xaf, 0xd7, 0xf5, 0x0a, 0x5b, + 0xa8, 0xf0, 0x63, 0x4e, 0x6f, 0x20, 0xd3, 0x9b, 0x49, 0x4a, 0x2e, 0x3c, 0xc5, 0xe9, 0x1b, 0xc8, + 0x75, 0x1b, 0x89, 0xfa, 0x65, 0xf2, 0x13, 0x4e, 0x8f, 0x92, 0x1b, 0xfe, 0xaf, 0xae, 0xe4, 0xc2, + 0x4f, 0x37, 0x8e, 0xbd, 0x24, 0xda, 0x5c, 0x23, 0x7c, 0x68, 0xc5, 0x56, 0xea, 0x7a, 0xc5, 0xcd, + 0xc8, 0xda, 0x08, 0x1f, 0x5e, 0xa1, 0xd7, 0x93, 0xc3, 0xc3, 0x97, 0x8f, 0x3a, 0x29, 0xc2, 0x8f, + 0x6c, 0xae, 0x89, 0xab, 0xf1, 0xc2, 0xa6, 0x9a, 0x78, 0x97, 0xe0, 0xf6, 0x8d, 0x83, 0xc9, 0x06, + 0xd2, 0x5b, 0x81, 0x3b, 0x56, 0x36, 0xcc, 0xf4, 0x92, 0xa8, 0xc5, 0x92, 0x8f, 0xae, 0xd0, 0x69, + 0x72, 0xed, 0x50, 0xc9, 0x22, 0x32, 0x1f, 0xee, 0xdc, 0xd8, 0xc7, 0xbb, 0x95, 0xc9, 0xc8, 0x5d, + 0x2b, 0x74, 0x57, 0xfc, 0xda, 0xac, 0x44, 0x61, 0x03, 0xbe, 0xd5, 0xb4, 0xc5, 0x6c, 0x46, 0x4b, + 0x62, 0x45, 0xc8, 0x5b, 0x45, 0x45, 0xc9, 0x36, 0x37, 0x0f, 0x91, 0x6f, 0x37, 0xed, 0x29, 0x36, + 0x64, 0x0f, 0xfd, 0x4e, 0xd3, 0x56, 0xa2, 0x41, 0x4b, 0xa2, 0x6d, 0x1e, 0x19, 0xc9, 0xc3, 0x67, + 0x99, 0xa3, 0x82, 0xef, 0x36, 0x6d, 0x00, 0x29, 0xba, 0x8a, 0xaa, 0x1d, 0x6f, 0xde, 0x32, 0xaf, + 0xc3, 0xa3, 0x69, 0x97, 0xb6, 0x8d, 0x65, 0x23, 0xe1, 0x07, 0x58, 0xf2, 0xe1, 0x7b, 0x4d, 0xbb, + 0xbc, 0x54, 0x3c, 0x2e, 0x9a, 0x7e, 0x9a, 0xd4, 0xc6, 0xf7, 0xd3, 0xd6, 0xb3, 0x4a, 0x32, 0xdf, + 0x63, 0xa1, 0x4e, 0xb9, 0xff, 0x41, 0xd3, 0xde, 0x74, 0x29, 0xf7, 0x67, 0x56, 0x5b, 0x08, 0x8f, + 0x35, 0x6d, 0xcf, 0x1d, 0x98, 0x09, 0x8f, 0x77, 0xd7, 0x58, 0x96, 0xc6, 0x82, 0x97, 0xb8, 0x79, + 0x30, 0xb2, 0x61, 0xa4, 0xd1, 0x05, 0xa6, 0xc2, 0x06, 0x0b, 0x0a, 0xb9, 0x05, 0xf8, 0x78, 0x64, + 0xc3, 0x18, 0xc2, 0xcf, 0x57, 0xca, 0x55, 0xf8, 0xc4, 0x26, 0x82, 0x85, 0x5b, 0xce, 0x9c, 0x81, + 0x4f, 0x46, 0xb6, 0x74, 0xd3, 0x82, 0x25, 0xd1, 0xec, 0xfb, 0x78, 0x28, 0xea, 0xbe, 0x6a, 0x87, + 0x29, 0x62, 0x2f, 0x0f, 0x6f, 0x2a, 0x89, 0xfd, 0x3c, 0x12, 0xd9, 0x6c, 0xa7, 0x25, 0x8b, 0xe8, + 0x21, 0x6f, 0x23, 0x7c, 0x2a, 0xb2, 0xb5, 0x10, 0xbf, 0x43, 0x3e, 0x70, 0x9b, 0xed, 0x9a, 0x66, + 0x64, 0xb2, 0x14, 0x4f, 0xa9, 0x46, 0x35, 0xf8, 0xe0, 0x6d, 0xf4, 0xf5, 0x71, 0x63, 0x34, 0xcc, + 0x3b, 0x99, 0xd9, 0xab, 0x74, 0x79, 0xd9, 0x97, 0xe1, 0xa5, 0xf3, 0xce, 0xa6, 0x32, 0x7b, 0x24, + 0x9f, 0x3c, 0xef, 0xd0, 0x3d, 0xf1, 0x1b, 0x38, 0xd7, 0x88, 0xc4, 0x0a, 0xbc, 0xd0, 0x71, 0xe8, + 0x44, 0x72, 0x1b, 0x9a, 0x71, 0x96, 0xf9, 0x25, 0xe1, 0xe3, 0x39, 0x78, 0xb1, 0xe3, 0xd0, 0x03, + 0xc9, 0x25, 0x62, 0xf1, 0xf8, 0x29, 0xfc, 0x52, 0xc7, 0xa1, 0x99, 0xe4, 0x3d, 0x66, 0xe1, 0x6a, + 0xc0, 0x3d, 0x2c, 0xb9, 0xf0, 0x97, 0x8e, 0x43, 0x0f, 0x25, 0x9d, 0x39, 0xcd, 0xd8, 0xcb, 0xf5, + 0xe5, 0x8e, 0x43, 0xa7, 0x93, 0x0e, 0x6a, 0xd8, 0x98, 0x2a, 0x4b, 0x9d, 0x93, 0xe6, 0x2b, 0x4b, + 0x23, 0xfc, 0x75, 0x5d, 0x18, 0xb1, 0x04, 0x5e, 0xe9, 0x38, 0x74, 0x26, 0x39, 0x71, 0x06, 0x77, + 0x19, 0x36, 0xa5, 0xa8, 0x6a, 0xa9, 0x58, 0x1d, 0x2b, 0x4c, 0x37, 0xe0, 0x6f, 0x1d, 0x87, 0xce, + 0x26, 0x47, 0xae, 0x2f, 0x30, 0x2f, 0x28, 0x13, 0x6e, 0xcd, 0x3c, 0x7a, 0xae, 0x76, 0x1c, 0xba, + 0x2f, 0xb9, 0x91, 0x7b, 0xae, 0xe1, 0xd5, 0xc1, 0xe5, 0xc5, 0x9f, 0x08, 0x61, 0x03, 0x5e, 0xeb, + 0x38, 0x94, 0x26, 0x17, 0x7a, 0xac, 0x35, 0x3d, 0xea, 0xef, 0x1d, 0x87, 0x1e, 0x4c, 0xba, 0xbb, + 0xc1, 0x0a, 0x5c, 0x24, 0xb1, 0x65, 0x57, 0x4b, 0x2e, 0xfc, 0x63, 0x3d, 0x39, 0x1f, 0x04, 0x31, + 0x1f, 0xc2, 0x3f, 0x3b, 0xce, 0xd4, 0xd6, 0x0b, 0x9f, 0x9b, 0xbe, 0x26, 0xfb, 0xe6, 0xa7, 0xaf, + 0x4c, 0x3b, 0x97, 0xaf, 0x4c, 0x3b, 0xcf, 0x5d, 0x99, 0x76, 0x1e, 0x7c, 0x7e, 0xfa, 0x9a, 0xf7, + 0x4c, 0x27, 0x1f, 0xcb, 0x1a, 0xbd, 0xc6, 0x31, 0x4f, 0x2a, 0x3c, 0x36, 0xf8, 0x5f, 0x87, 0xda, + 0x68, 0xfc, 0xef, 0x82, 0xb7, 0xfc, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xba, 0x95, 0xf6, 0x6d, 0x8e, 0x10, 0x00, 0x00, } diff --git a/core/pkg/errorcodes/errors.proto b/core/pkg/errorcodes/errors.proto index 67110bbf5a..421079be96 100644 --- a/core/pkg/errorcodes/errors.proto +++ b/core/pkg/errorcodes/errors.proto @@ -120,7 +120,6 @@ enum Code { ErrConversationIsMember = 13204; ErrConversationGetMember = 13205; ErrConversationGetOwners = 13206; - ErrConversationGetInteractiveMember = 13207; ErrConversationMember = 13500; ErrConversationMemberID = 13501; diff --git a/core/sql/helpers.go b/core/sql/helpers.go index 4d8002a59b..686752ee85 100644 --- a/core/sql/helpers.go +++ b/core/sql/helpers.go @@ -158,19 +158,25 @@ func ConversationSave(db *gorm.DB, c *entity.Conversation) error { var err error for _, member := range c.Members { - err := db.Find(&entity.Contact{ID: member.Contact.ID}).Error - if err != nil { - if !errorcodes.ErrDbNothingFound.Is(GenericError(err)) { - return err - } - if err := db.Save(member.Contact).Error; err != nil { - return err + if member.Contact != nil { + err := db.Find(&entity.Contact{ID: member.Contact.ID}).Error + if err != nil { + if !errorcodes.ErrDbNothingFound.Is(GenericError(err)) { + db.Delete(c) + return err + } + member.Contact.Status = entity.Contact_Unknown + if err := db.Save(member.Contact).Error; err != nil { + db.Delete(c) + return err + } } } if err = db.Save(member).Error; err != nil { + db.Delete(c) return err } } - return err + return nil } diff --git a/core/sql/migrations/v0006conversationLogic/migration.go b/core/sql/migrations/v0006conversationLogic/migration.go index 4f2a754f61..08440b7604 100644 --- a/core/sql/migrations/v0006conversationLogic/migration.go +++ b/core/sql/migrations/v0006conversationLogic/migration.go @@ -44,7 +44,7 @@ func GetMigration() *gormigrate.Migration { return nil }, Rollback: func(tx *gorm.DB) error { - if err := tx.Table("conversation").DropColumn("kind").Error; err != nil { + if err := tx.Table("conversation").DropColumn("kind").DropColumn("wrote_at").Error; err != nil { return err } return nil @@ -120,11 +120,12 @@ type Conversation struct { CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` UpdatedAt time.Time `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` ReadAt time.Time `protobuf:"bytes,4,opt,name=read_at,json=readAt,proto3,stdtime" json:"read_at"` + WroteAt time.Time `protobuf:"bytes,5,opt,name=wrote_at,json=wroteAt,proto3,stdtime" json:"wrote_at"` Title string `protobuf:"bytes,20,opt,name=title,proto3" json:"title,omitempty"` Topic string `protobuf:"bytes,21,opt,name=topic,proto3" json:"topic,omitempty"` Infos string `protobuf:"bytes,22,opt,name=infos,proto3" json:"infos,omitempty"` Kind Conversation_Kind `protobuf:"varint,23,opt,name=kind,proto3,enum=berty.entity.Conversation_Kind" json:"kind,omitempty"` - Members []*ConversationMember `protobuf:"bytes,100,rep,name=members,proto3" json:"members,omitempty" gorm:"foreignkey:ConversationID;association_foreignkey:ID"` + Members []*ConversationMember `protobuf:"bytes,100,rep,name=members,proto3" json:"members,omitempty" gorm:"foreignkey:ConversationID;association_foreignkey:ID;save_associations:true"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -135,11 +136,11 @@ type ConversationMember struct { CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` UpdatedAt time.Time `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` ReadAt time.Time `protobuf:"bytes,4,opt,name=read_at,json=readAt,proto3,stdtime" json:"read_at"` + WroteAt time.Time `protobuf:"bytes,5,opt,name=wrote_at,json=wroteAt,proto3,stdtime" json:"wrote_at"` Status ConversationMember_Status `protobuf:"varint,10,opt,name=status,proto3,enum=berty.entity.ConversationMember_Status" json:"status,omitempty"` - Contact *Contact `protobuf:"bytes,100,opt,name=contact,proto3" json:"contact,omitempty"` + Contact *Contact `protobuf:"bytes,100,opt,name=contact,proto3" json:"contact,omitempty" gorm:"association_autoupdate:false;association_create:true"` ConversationID string `protobuf:"bytes,101,opt,name=conversation_id,json=conversationId,proto3" json:"conversation_id,omitempty"` ContactID string `protobuf:"bytes,102,opt,name=contact_id,json=contactId,proto3" json:"contact_id,omitempty"` - Conversation *Conversation `protobuf:"bytes,103,opt,name=conversation,proto3" json:"conversation,omitempty" gorm:"PRELOAD:false;foreignkey:ID;association_foreignkey:ConversationID"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"`