2024-04-11 15:51:56 +02:00
// myChatConversations provides functions to generate and retrieve a user's chat conversations
// These conversations can be filtered and sorted based on the recipient's qualities
// Examples: Sort recipients by age, filter recipients who do not fulfill our desires
package myChatConversations
import "seekia/internal/appMemory"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/messaging/myChatFilters"
import "seekia/internal/messaging/myChatMessages"
import "seekia/internal/messaging/myReadStatus"
import "seekia/internal/myBlockedUsers"
import "seekia/internal/myDatastores/myMapList"
import "seekia/internal/myIdentity"
import "seekia/internal/mySettings"
import "seekia/internal/profiles/viewableProfiles"
import "slices"
import "sync"
import "errors"
// This mutex will be locked whenever we update chat conversations
var updatingMyChatConversationsMutex sync . Mutex
var myMateChatConversationsMapListDatastore * myMapList . MyMapList
var myModeratorChatConversationsMapListDatastore * myMapList . MyMapList
// This function must be called whenever an app user signs in
func InitializeMyChatConversationsDatastores ( ) error {
updatingMyChatConversationsMutex . Lock ( )
defer updatingMyChatConversationsMutex . Unlock ( )
newMyMateChatConversationsMapListDatastore , err := myMapList . CreateNewMapList ( "MyMateChatConversations" )
if ( err != nil ) { return err }
newMyModeratorChatConversationsMapListDatastore , err := myMapList . CreateNewMapList ( "MyModeratorChatConversations" )
if ( err != nil ) { return err }
myMateChatConversationsMapListDatastore = newMyMateChatConversationsMapListDatastore
myModeratorChatConversationsMapListDatastore = newMyModeratorChatConversationsMapListDatastore
return nil
}
func getMyConversationsMapListDatastore ( identityType string ) ( * myMapList . MyMapList , error ) {
if ( identityType == "Mate" ) {
return myMateChatConversationsMapListDatastore , nil
}
if ( identityType == "Moderator" ) {
return myModeratorChatConversationsMapListDatastore , nil
}
return nil , errors . New ( "getMyConversationsMapListDatastore called with invalid identity type: " + identityType )
}
// This function checks if conversations are generated and sorted
func GetMyChatConversationsReadyStatus ( identityType string , networkType byte ) ( bool , error ) {
isValid := helpers . VerifyNetworkType ( networkType )
if ( isValid == false ) {
networkTypeString := helpers . ConvertByteToString ( networkType )
return false , errors . New ( "GetMyChatConversationsReadyStatus called with invalid networkType: " + networkTypeString )
}
generatedStatus , err := getMyChatConversationsGeneratedStatus ( identityType )
if ( err != nil ) { return false , err }
if ( generatedStatus == false ) {
return false , nil
}
exists , sortedStatus , err := mySettings . GetSetting ( identityType + "ChatConversationsSortedStatus" )
if ( err != nil ) { return false , err }
if ( exists == false || sortedStatus != "Yes" ) {
return false , nil
}
exists , conversationsNetworkTypeString , err := mySettings . GetSetting ( identityType + "ChatConversationsNetworkType" )
if ( err != nil ) { return false , err }
if ( exists == false ) {
// This should not happen, because ...ChatConversationsNetworkType is set whenever conversations are generated
return false , errors . New ( "mySettings is missing " + identityType + "ChatConversationsNetworkType when " + identityType + "ChatConversationsGeneratedStatus exists." )
}
conversationsNetworkType , err := helpers . ConvertNetworkTypeStringToByte ( conversationsNetworkTypeString )
if ( err != nil ) {
return false , errors . New ( "MySettings contains invalid " + identityType + "ChatConversationsNetworkType: " + conversationsNetworkTypeString )
}
if ( conversationsNetworkType != networkType ) {
// Conversations must have been generated for a different networkType.
// This should not happen, because we should only call this function using our current appNetworkType
// Whenever we change network types, we reset the ChatConversationsGeneratedStatus to No.
//TODO: Log this.
err := mySettings . SetSetting ( identityType + "ChatConversationsGeneratedStatus" , "No" )
if ( err != nil ) { return false , err }
return false , nil
}
return true , nil
}
// Our conversations will need a refresh any time a new message sent to the user's inboxes is downloaded
func CheckIfMyChatConversationsNeedRefresh ( identityType string ) ( bool , error ) {
exists , needsRefresh , err := mySettings . GetSetting ( identityType + "ChatConversationsNeedRefreshYesNo" )
if ( err != nil ) { return true , err }
if ( exists == true && needsRefresh == "No" ) {
return false , nil
}
return true , nil
}
func getMyChatConversationsGeneratedStatus ( identityType string ) ( bool , error ) {
chatMessagesReady , err := myChatMessages . GetMyChatMessagesReadyStatus ( identityType )
if ( err != nil ) { return false , err }
if ( chatMessagesReady == false ) {
return false , nil
}
exists , readyStatus , err := mySettings . GetSetting ( identityType + "ChatConversationsGeneratedStatus" )
if ( err != nil ) { return false , err }
if ( exists == false || readyStatus != "Yes" ) {
return false , nil
}
return true , nil
}
// This function returns the ready (generated and sorted) chat conversations map list
//Outputs:
// -bool: Chat conversations are ready
// -[]map[string]string
// -error
func GetMyChatConversationsMapList ( identityType string , networkType byte ) ( bool , [ ] map [ string ] string , error ) {
if ( identityType != "Mate" && identityType != "Moderator" ) {
return false , nil , errors . New ( "GetMyChatConversationsMapList called with invalid identityType: " + identityType )
}
isValid := helpers . VerifyNetworkType ( networkType )
if ( isValid == false ) {
networkTypeString := helpers . ConvertByteToString ( networkType )
return false , nil , errors . New ( "GetMyChatConversationsMapList called with invalid networkType: " + networkTypeString )
}
conversationsReady , err := GetMyChatConversationsReadyStatus ( identityType , networkType )
if ( err != nil ) { return false , nil , err }
if ( conversationsReady == false ) {
return false , nil , nil
}
myReadyConversationsMapListDatastore , err := getMyConversationsMapListDatastore ( identityType )
if ( err != nil ) { return false , nil , err }
myReadyChatConversationsMapList , err := myReadyConversationsMapListDatastore . GetMapList ( )
if ( err != nil ) { return false , nil , err }
return true , myReadyChatConversationsMapList , nil
}
//Outputs:
// -bool: Conversations ready
// -string: Number of conversations
// -error
func GetNumberOfConversations ( identityType string , networkType byte ) ( bool , int , error ) {
isValid := helpers . VerifyNetworkType ( networkType )
if ( isValid == false ) {
networkTypeString := helpers . ConvertByteToString ( networkType )
return false , 0 , errors . New ( "GetNumberOfConversations called with invalid networkType: " + networkTypeString )
}
conversationsReady , conversationsMap , err := GetMyChatConversationsMapList ( identityType , networkType )
if ( err != nil ) { return false , 0 , err }
if ( conversationsReady == false ) {
return false , 0 , nil
}
numberOfConversations := len ( conversationsMap )
return true , numberOfConversations , nil
}
func GetConversationsSortByAttribute ( identityType string ) ( string , error ) {
exists , currentAttribute , err := mySettings . GetSetting ( identityType + "ChatConversations_SortByAttribute" )
if ( err != nil ) { return "" , err }
if ( exists == false ) {
return "MatchScore" , nil
}
return currentAttribute , nil
}
func GetConversationsSortDirection ( identityType string ) ( string , error ) {
exists , sortDirection , err := mySettings . GetSetting ( identityType + "ChatConversations_SortDirection" )
if ( err != nil ) { return "" , err }
if ( exists == false ) {
return "Descending" , nil
}
if ( sortDirection != "Ascending" && sortDirection != "Descending" ) {
return "" , errors . New ( "MySettings malformed: Invalid ChatConversations_SortDirection: " + sortDirection )
}
return sortDirection , nil
}
//Outputs:
// -bool: Conversations ready
// -string: Number of unread conversations
// -error
func GetNumberOfUnreadConversations ( identityType string , networkType byte ) ( bool , int , error ) {
isValid := helpers . VerifyNetworkType ( networkType )
if ( isValid == false ) {
networkTypeString := helpers . ConvertByteToString ( networkType )
return false , 0 , errors . New ( "GetNumberOfUnreadConversations called with invalid networkType: " + networkTypeString )
}
conversationsReady , conversationsMapList , err := GetMyChatConversationsMapList ( identityType , networkType )
if ( err != nil ) { return false , 0 , err }
if ( conversationsReady == false ) {
return false , 0 , nil
}
numberOfUnreadConversations := 0
for _ , conversationMap := range conversationsMapList {
myIdentityHashString , exists := conversationMap [ "MyIdentityHash" ]
if ( exists == false ) {
return false , 0 , errors . New ( "Invalid conversation map: Item missing MyIdentityHash" )
}
theirIdentityHashString , exists := conversationMap [ "TheirIdentityHash" ]
if ( exists == false ) {
return false , 0 , errors . New ( "Invalid conversation map: Item missing TheirIdentityHash" )
}
myIdentityHash , myIdentityType , err := identity . ReadIdentityHashString ( myIdentityHashString )
if ( err != nil ) {
return false , 0 , errors . New ( "Invalid conversation map: Item contains invalid MyIdentityHash: " + myIdentityHashString )
}
theirIdentityHash , theirIdentityType , err := identity . ReadIdentityHashString ( theirIdentityHashString )
if ( err != nil ) {
return false , 0 , errors . New ( "Invalid conversation map: Item contains invalid TheirIdentityHash: " + theirIdentityHashString )
}
if ( myIdentityType != theirIdentityType ) {
return false , 0 , errors . New ( "Invalid conversation map: Item contains mismatched My and Their identityTypes." )
}
if ( myIdentityType != identityType ) {
return false , 0 , errors . New ( "GetMyChatConversationsMapList returning conversation map for different identityType participants." )
}
readUnreadStatus , err := myReadStatus . GetConversationReadUnreadStatus ( myIdentityHash , theirIdentityHash , networkType )
if ( err != nil ) { return false , 0 , err }
if ( readUnreadStatus == "Unread" ) {
numberOfUnreadConversations += 1
}
}
return true , numberOfUnreadConversations , nil
}
//Outputs:
// -bool: Build encountered error
// -string: Error encountered
// -bool: Build is stopped (will be stopped if user went to different page)
// -bool: Conversations are ready
// -float64: Percentage Progress (0 - 1)
// -error
func GetChatConversationsBuildStatus ( identityType string , networkType byte ) ( bool , string , bool , bool , float64 , error ) {
isValid := helpers . VerifyNetworkType ( networkType )
if ( isValid == false ) {
networkTypeString := helpers . ConvertByteToString ( networkType )
return false , "" , false , false , 0 , errors . New ( "GetChatConversationsBuildStatus called with invalid networkType: " + networkTypeString )
}
exists , encounteredError := appMemory . GetMemoryEntry ( identityType + "ChatConversationsBuildEncounteredError" )
if ( exists == false ) {
// No build exists. A build has not been started since Seekia was started
return false , "" , false , false , 0 , nil
}
if ( encounteredError == "Yes" ) {
exists , errorEncountered := appMemory . GetMemoryEntry ( identityType + "ChatConversationsBuildError" )
if ( exists == false ) {
return false , "" , false , false , 0 , errors . New ( "Chat conversations build error encountered error is yes, but no error exists." )
}
return true , errorEncountered , false , false , 0 , nil
}
isStopped := CheckIfBuildMyConversationsIsStopped ( )
if ( isStopped == true ) {
return false , "" , true , false , 0 , nil
}
conversationsReadyBool , err := GetMyChatConversationsReadyStatus ( identityType , networkType )
if ( err != nil ) { return false , "" , false , false , 0 , err }
if ( conversationsReadyBool == true ) {
return false , "" , false , true , 1 , nil
}
exists , currentPercentageString := appMemory . GetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" )
if ( exists == false ) {
// No build exists. A build has not been started since Seekia was started
return false , "" , false , false , 0 , nil
}
currentPercentageFloat , err := helpers . ConvertStringToFloat64 ( currentPercentageString )
if ( err != nil ) {
return false , "" , false , false , 0 , errors . New ( "ChatConversationsReadyProgressStatus is invalid: Not a float: " + currentPercentageString )
}
return false , "" , false , false , currentPercentageFloat , nil
}
func CheckIfBuildMyConversationsIsStopped ( ) bool {
exists , buildStoppedStatus := appMemory . GetMemoryEntry ( "StopBuildMyConversationsYesNo" )
if ( exists == false || buildStoppedStatus != "No" ) {
return true
}
return false
}
// This function will cancel the current build (if one is running)
// It will then start updating our chat messages and conversations list
func StartUpdatingMyConversations ( identityType string , networkType byte ) error {
if ( identityType != "Mate" && identityType != "Moderator" ) {
return errors . New ( "StartUpdatingMyConversations called with invalid identity type: " + identityType )
}
isValid := helpers . VerifyNetworkType ( networkType )
if ( isValid == false ) {
networkTypeString := helpers . ConvertByteToString ( networkType )
return errors . New ( "StartUpdatingMyConversations called with invalid networkType: " + networkTypeString )
}
networkTypeString := helpers . ConvertByteToString ( networkType )
appMemory . SetMemoryEntry ( "StopBuildMyConversationsYesNo" , "Yes" )
// We wait for any existing build to stop
updatingMyChatConversationsMutex . Lock ( )
appMemory . SetMemoryEntry ( identityType + "ChatConversationsBuildEncounteredError" , "No" )
appMemory . SetMemoryEntry ( identityType + "ChatConversationsBuildError" , "" )
appMemory . SetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" , "0" )
appMemory . SetMemoryEntry ( "StopBuildMyConversationsYesNo" , "No" )
updateMyChatConversations := func ( ) error {
myIdentityExists , myIdentityHash , err := myIdentity . GetMyIdentityHash ( identityType )
if ( err != nil ) { return err }
if ( myIdentityExists == false ) {
// Our identity does not exist.
// We first update our myChatMessagesMapList ready status by running the below function
updateProgressFunction := func ( _ int ) error {
return nil
}
_ , err := myChatMessages . GetUpdatedMyChatMessagesMapList ( identityType , networkType , updateProgressFunction )
if ( err != nil ) { return err }
// Now we overwrite our conversation datastores with empty map lists.
// Using the DeleteMapList function achieves this.
myChatConversationsMapListDatastore , err := getMyConversationsMapListDatastore ( identityType )
if ( err != nil ) { return err }
err = myChatConversationsMapListDatastore . DeleteMapList ( )
if ( err != nil ) { return err }
err = mySettings . SetSetting ( identityType + "ChatConversationsGeneratedStatus" , "Yes" )
if ( err != nil ) { return err }
err = mySettings . SetSetting ( identityType + "ChatConversationsNetworkType" , networkTypeString )
if ( err != nil ) { return err }
err = mySettings . SetSetting ( identityType + "ChatConversationsSortedStatus" , "Yes" )
if ( err != nil ) { return err }
appMemory . SetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" , "1" )
err = mySettings . SetSetting ( identityType + "ChatConversationsNeedRefreshYesNo" , "No" )
if ( err != nil ) { return err }
return nil
}
isStopped := CheckIfBuildMyConversationsIsStopped ( )
if ( isStopped == true ) {
// User has moved to a different page. Stop generating.
return nil
}
getConversationsNeedToBeGeneratedStatus := func ( ) ( bool , error ) {
messagesReadyStatus , err := myChatMessages . GetMyChatMessagesReadyStatus ( identityType )
if ( err != nil ) { return false , err }
if ( messagesReadyStatus == false ) {
return true , nil
}
conversationsGenerated , err := getMyChatConversationsGeneratedStatus ( identityType )
if ( err != nil ) { return false , err }
if ( conversationsGenerated == false ) {
return true , nil
}
exists , chatConversationsNetworkTypeString , err := mySettings . GetSetting ( identityType + "ChatConversationsNetworkType" )
if ( err != nil ) { return false , err }
if ( exists == false ) {
// This should not happen, because MatchesNetworkType is set to Yes whenever matches are generated
return false , errors . New ( "...ChatConversationsNetworkType missing when ...ChatConversationsGeneratedStatus exists." )
}
chatConversationsNetworkType , err := helpers . ConvertNetworkTypeStringToByte ( chatConversationsNetworkTypeString )
if ( err != nil ) {
return false , errors . New ( "mySettings contains invalid ...ChatConversationsNetworkType: " + chatConversationsNetworkTypeString )
}
if ( chatConversationsNetworkType != networkType ) {
// This should not happen, because ...ChatConversationsGeneratedStatus should be set to No whenever app network
// type is changed, and StartUpdatingMyConversations should only be called with the current app network type.
return true , nil
}
return false , nil
}
conversationsNeedToBeGenerated , err := getConversationsNeedToBeGeneratedStatus ( )
if ( err != nil ) { return err }
if ( conversationsNeedToBeGenerated == true ) {
// We generate conversations
err := mySettings . SetSetting ( identityType + "ChatConversationsGeneratedStatus" , "No" )
if ( err != nil ) { return err }
err = mySettings . SetSetting ( identityType + "ChatConversationsSortedStatus" , "No" )
if ( err != nil ) { return err }
updatePercentageProgressFunction := func ( input int ) error {
// Input is a value between 0-100
// We reduce it down to a value between 0-50
newProgressInt , err := helpers . ScaleNumberProportionally ( true , input , 0 , 100 , 0 , 50 )
if ( err != nil ) { return err }
newPercentageProgressFloat := float64 ( newProgressInt ) / 100
newPercentageProgressString := helpers . ConvertFloat64ToString ( newPercentageProgressFloat )
appMemory . SetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" , newPercentageProgressString )
return nil
}
myChatMessagesMapList , err := myChatMessages . GetUpdatedMyChatMessagesMapList ( identityType , networkType , updatePercentageProgressFunction )
if ( err != nil ) { return err }
appMemory . SetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" , ".50" )
// Conversations map stores data as TheirIdentityHash -> MostRecentMessageTime
conversationsMap := make ( map [ [ 16 ] byte ] int64 )
for _ , messageMap := range myChatMessagesMapList {
isStopped := CheckIfBuildMyConversationsIsStopped ( )
if ( isStopped == true ) {
// User has moved to a different page. Stop generating.
return nil
}
messageMyIdentityHashString , exists := messageMap [ "MyIdentityHash" ]
if ( exists == false ) {
return errors . New ( "Malformed message map: missing MyIdentityHash." )
}
messageMyIdentityHash , messageMyIdentityHashIdentityType , err := identity . ReadIdentityHashString ( messageMyIdentityHashString )
if ( err != nil ) {
return errors . New ( "Malformed message map: contains invalid MyIdentityHash: " + messageMyIdentityHashString )
}
if ( messageMyIdentityHashIdentityType != identityType ) {
return errors . New ( "GetUpdatedMyChatMessagesMapList returning message with different identity type." )
}
if ( messageMyIdentityHash != myIdentityHash ) {
// This should not happen, because our old identity's messages should have been deleted.
return errors . New ( "GetUpdatedMyChatMessagesMapList returning map list containing message from a different identityHash than our current one for provided identityType." )
}
messageTheirIdentityHashString , exists := messageMap [ "TheirIdentityHash" ]
if ( exists == false ) {
return errors . New ( "Malformed message map: Missing TheirIdentityHash." )
}
messageTheirIdentityHash , theirIdentityType , err := identity . ReadIdentityHashString ( messageTheirIdentityHashString )
if ( err != nil ) {
return errors . New ( "Malformed message map: Contains invalid TheirIdentityHash: " + messageTheirIdentityHashString )
}
if ( theirIdentityType != identityType ) {
return errors . New ( "myChatMessagesMapList contains message map between different identity types." )
}
messageNetworkType , exists := messageMap [ "NetworkType" ]
if ( exists == false ) {
return errors . New ( "myChatMessagesMapList returning messageMap missing NetworkType." )
}
if ( messageNetworkType != networkTypeString ) {
return errors . New ( "myChatMessagesMapList returning messageMap with different NetworkType" )
}
2024-06-11 06:59:06 +02:00
messageCreationTimeString , exists := messageMap [ "CreationTime" ]
2024-04-11 15:51:56 +02:00
if ( exists == false ) {
2024-06-11 06:59:06 +02:00
return errors . New ( "Malformed message map: Missing CreationTime." )
2024-04-11 15:51:56 +02:00
}
2024-06-11 06:59:06 +02:00
currentMessageCreationTimeInt64 , err := helpers . ConvertStringToInt64 ( messageCreationTimeString )
2024-04-11 15:51:56 +02:00
if ( err != nil ) {
2024-06-11 06:59:06 +02:00
return errors . New ( "Malformed message map: Contains invalid CreationTime: " + messageCreationTimeString )
2024-04-11 15:51:56 +02:00
}
2024-06-11 06:59:06 +02:00
existingMostRecentMessageCreationTime , exists := conversationsMap [ messageTheirIdentityHash ]
if ( exists == false || currentMessageCreationTimeInt64 > existingMostRecentMessageCreationTime ) {
2024-04-11 15:51:56 +02:00
2024-06-11 06:59:06 +02:00
conversationsMap [ messageTheirIdentityHash ] = currentMessageCreationTimeInt64
2024-04-11 15:51:56 +02:00
}
}
appMemory . SetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" , ".60" )
myIdentityHashString , _ , err := identity . EncodeIdentityHashBytesToString ( myIdentityHash )
if ( err != nil ) {
myIdentityHashHex := encoding . EncodeBytesToHexString ( myIdentityHash [ : ] )
return errors . New ( "GetMyIdentityHash returning invalid myIdentityHash: " + myIdentityHashHex )
}
newConversationsMapList := make ( [ ] map [ string ] string , 0 )
for theirIdentityHash , conversationMostRecentMessageTime := range conversationsMap {
isStopped := CheckIfBuildMyConversationsIsStopped ( )
if ( isStopped == true ) {
// User has moved to a different page. Stop generating.
return nil
}
theyAreBlocked , _ , _ , _ , err := myBlockedUsers . CheckIfUserIsBlocked ( theirIdentityHash )
if ( err != nil ) { return err }
if ( theyAreBlocked == true ) {
continue
}
userPassesChatFilters , err := myChatFilters . CheckIfUserPassesAllMyChatFilters ( theirIdentityHash , networkType )
if ( err != nil ) { return err }
if ( userPassesChatFilters == false ) {
continue
}
theirIdentityHashString , _ , err := identity . EncodeIdentityHashBytesToString ( theirIdentityHash )
if ( err != nil ) {
theirIdentityHashHex := encoding . EncodeBytesToHexString ( theirIdentityHash [ : ] )
return errors . New ( "conversationsMap contains invalid theirIdentityHash: " + theirIdentityHashHex )
}
2024-06-11 06:59:06 +02:00
mostRecentMessageCreationTimeString := helpers . ConvertInt64ToString ( conversationMostRecentMessageTime )
2024-04-11 15:51:56 +02:00
newConversationMap := map [ string ] string {
"MyIdentityHash" : myIdentityHashString ,
"TheirIdentityHash" : theirIdentityHashString ,
"NetworkType" : networkTypeString ,
2024-06-11 06:59:06 +02:00
"MostRecentMessageTime" : mostRecentMessageCreationTimeString ,
2024-04-11 15:51:56 +02:00
}
newConversationsMapList = append ( newConversationsMapList , newConversationMap )
}
myChatConversationsMapListDatastore , err := getMyConversationsMapListDatastore ( identityType )
if ( err != nil ) { return err }
err = myChatConversationsMapListDatastore . OverwriteMapList ( newConversationsMapList )
if ( err != nil ) { return err }
err = mySettings . SetSetting ( identityType + "ChatConversationsGeneratedStatus" , "Yes" )
if ( err != nil ) { return err }
err = mySettings . SetSetting ( identityType + "ChatConversationsNetworkType" , networkTypeString )
if ( err != nil ) { return err }
}
isStopped = CheckIfBuildMyConversationsIsStopped ( )
if ( isStopped == true ) {
// User has moved to a different page. Stop generating.
return nil
}
appMemory . SetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" , ".70" )
conversationsReady , err := GetMyChatConversationsReadyStatus ( identityType , networkType )
if ( err != nil ) { return err }
if ( conversationsReady == false ) {
// Now we sort conversations.
currentSortByAttribute , err := GetConversationsSortByAttribute ( identityType )
if ( err != nil ) { return err }
currentSortDirection , err := GetConversationsSortDirection ( identityType )
if ( err != nil ) { return err }
myChatConversationsMapListDatastore , err := getMyConversationsMapListDatastore ( identityType )
if ( err != nil ) { return err }
currentConversationsMapList , err := myChatConversationsMapListDatastore . GetMapList ( )
if ( err != nil ) { return err }
// We use this map to make sure there are no duplicate recipients
// This should never happen, unless the user's stored map list was edited or there is a bug
recipientsMap := make ( map [ [ 16 ] byte ] struct { } )
// Map structure: Their Identity Hash -> Sort By Attribute Value
recipientAttributeValuesMap := make ( map [ string ] float64 )
getAllowUnknownViewableStatusBool := func ( ) bool {
if ( identityType == "Mate" ) {
return false
}
return true
}
allowUnknownViewableStatusBool := getAllowUnknownViewableStatusBool ( )
maximumIndex := len ( currentConversationsMapList ) - 1
for index , conversationMap := range currentConversationsMapList {
conversationMyIdentityHashString , exists := conversationMap [ "MyIdentityHash" ]
if ( exists == false ) {
return errors . New ( "Malformed conversation map during sort: item missing MyIdentityHash." )
}
conversationMyIdentityHash , conversationMyIdentityHashIdentityType , err := identity . ReadIdentityHashString ( conversationMyIdentityHashString )
if ( err != nil ) {
return errors . New ( "Malformed conversation map during sort: contains invalid MyIdentityHash: " + conversationMyIdentityHashString )
}
if ( conversationMyIdentityHashIdentityType != identityType ) {
return errors . New ( "Malformed conversation map during sort: contains conversation with different identity type." )
}
if ( conversationMyIdentityHash != myIdentityHash ) {
// This should not happen, because our conversations should be regenerated whenever we change our identity
return errors . New ( "Malformed conversation map during sort: Contains different identity hash." )
}
theirIdentityHashString , exists := conversationMap [ "TheirIdentityHash" ]
if ( exists == false ) {
return errors . New ( "ChatConversations map list item missing TheirIdentityHash" )
}
theirIdentityHash , theirIdentityType , err := identity . ReadIdentityHashString ( theirIdentityHashString )
if ( err != nil ) {
return errors . New ( "ChatConversations map list item contains invalid theirIdentityHash: " + theirIdentityHashString )
}
if ( theirIdentityType != identityType ) {
return errors . New ( "ChatConversations map list item contains invalid theirIdentityHash: Different identityType: " + theirIdentityType )
}
_ , exists = recipientsMap [ theirIdentityHash ]
if ( exists == true ) {
return errors . New ( "ChatConversations map list is malformed: Contains two conversation maps with the same recipient" )
}
recipientsMap [ theirIdentityHash ] = struct { } { }
profileExists , _ , attributeExists , attributeValue , err := viewableProfiles . GetAnyAttributeFromNewestViewableUserProfile ( theirIdentityHash , networkType , currentSortByAttribute , true , allowUnknownViewableStatusBool , true )
if ( err != nil ) { return err }
if ( profileExists == true && attributeExists == true ) {
attributeValueFloat , err := helpers . ConvertStringToFloat64 ( attributeValue )
if ( err != nil ) {
return errors . New ( "User attribute cannot be converted to float during conversation build: " + attributeValue )
}
recipientAttributeValuesMap [ theirIdentityHashString ] = attributeValueFloat
}
isStopped := CheckIfBuildMyConversationsIsStopped ( )
if ( isStopped == true ) {
// User has moved to a different page. Stop generating.
return nil
}
newScaledPercentageInt , err := helpers . ScaleNumberProportionally ( true , index , 0 , maximumIndex , 70 , 85 )
if ( err != nil ) { return err }
newProgressFloat := float64 ( newScaledPercentageInt ) / 100
newProgressString := helpers . ConvertFloat64ToString ( newProgressFloat )
appMemory . SetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" , newProgressString )
}
compareConversationMapsFunction := func ( conversationMapA map [ string ] string , conversationMapB map [ string ] string ) int {
identityHashA , exists := conversationMapA [ "TheirIdentityHash" ]
if ( exists == false ) {
panic ( "Malformed conversations map list: Item missing TheirIdentityHash" )
}
identityHashB , exists := conversationMapB [ "TheirIdentityHash" ]
if ( exists == false ) {
panic ( "Malformed conversations map list: Item missing TheirIdentityHash" )
}
if ( identityHashA == identityHashB ) {
panic ( "Malformed conversations map list: Two conversations contain the same TheirIdentityHash." )
}
attributeValueA , attributeValueAExists := recipientAttributeValuesMap [ identityHashA ]
attributeValueB , attributeValueBExists := recipientAttributeValuesMap [ identityHashB ]
if ( attributeValueAExists == false && attributeValueBExists == false ) {
// We don't know the attribute value for either recipient
// We sort recipients in unicode order
if ( identityHashA < identityHashB ) {
return - 1
}
return 1
} else if ( attributeValueAExists == true && attributeValueBExists == false ) {
// We sort unknown attribute recipient conversations to the back of the list
return - 1
} else if ( attributeValueAExists == false && attributeValueBExists == true ) {
return 1
}
// Both recipient attribute values exist
if ( attributeValueA == attributeValueB ) {
// If values are equal, we want the result to be the same with each refresh
// We sort the identity hashses in unicode order
if ( identityHashA < identityHashB ) {
return - 1
}
return 1
}
if ( attributeValueA < attributeValueB ) {
if ( currentSortDirection == "Ascending" ) {
return - 1
}
return 1
}
if ( currentSortDirection == "Ascending" ) {
return 1
}
return - 1
}
slices . SortFunc ( currentConversationsMapList , compareConversationMapsFunction )
err = myChatConversationsMapListDatastore . OverwriteMapList ( currentConversationsMapList )
if ( err != nil ) { return err }
err = mySettings . SetSetting ( identityType + "ChatConversationsSortedStatus" , "Yes" )
if ( err != nil ) { return err }
}
appMemory . SetMemoryEntry ( identityType + "ChatConversationsReadyProgressStatus" , "1" )
err = mySettings . SetSetting ( identityType + "ChatConversationsNeedRefreshYesNo" , "No" )
if ( err != nil ) { return err }
return nil
}
updateFunction := func ( ) {
err := updateMyChatConversations ( )
if ( err != nil ) {
appMemory . SetMemoryEntry ( identityType + "ChatConversationsBuildEncounteredError" , "Yes" )
appMemory . SetMemoryEntry ( identityType + "ChatConversationsBuildError" , err . Error ( ) )
}
updatingMyChatConversationsMutex . Unlock ( )
}
go updateFunction ( )
return nil
}