824 lines
30 KiB
Go
824 lines
30 KiB
Go
|
|
// 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.ScaleIntProportionally(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")
|
|
}
|
|
|
|
messageCreationTimeString, exists := messageMap["CreationTime"]
|
|
if (exists == false){
|
|
return errors.New("Malformed message map: Missing CreationTime.")
|
|
}
|
|
|
|
currentMessageCreationTimeInt64, err := helpers.ConvertStringToInt64(messageCreationTimeString)
|
|
if (err != nil) {
|
|
return errors.New("Malformed message map: Contains invalid CreationTime: " + messageCreationTimeString)
|
|
}
|
|
|
|
existingMostRecentMessageCreationTime, exists := conversationsMap[messageTheirIdentityHash]
|
|
if (exists == false || currentMessageCreationTimeInt64 > existingMostRecentMessageCreationTime){
|
|
|
|
conversationsMap[messageTheirIdentityHash] = currentMessageCreationTimeInt64
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
mostRecentMessageCreationTimeString := helpers.ConvertInt64ToString(conversationMostRecentMessageTime)
|
|
|
|
newConversationMap := map[string]string{
|
|
"MyIdentityHash": myIdentityHashString,
|
|
"TheirIdentityHash": theirIdentityHashString,
|
|
"NetworkType": networkTypeString,
|
|
"MostRecentMessageTime": mostRecentMessageCreationTimeString,
|
|
}
|
|
|
|
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.ScaleIntProportionally(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
|
|
}
|
|
|
|
|
|
|