seekia/gui/contactsGui.go

1090 lines
38 KiB
Go
Raw Normal View History

package gui
// contactsGui.go implements pages to view and manage a user's contacts
import "fyne.io/fyne/v2"
import "fyne.io/fyne/v2/container"
import "fyne.io/fyne/v2/widget"
import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/dialog"
import "fyne.io/fyne/v2/data/binding"
import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/canvas"
import "seekia/internal/appMemory"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/imagery"
import "seekia/internal/myContacts"
import "seekia/internal/myIdentity"
import "seekia/internal/mySettings"
import "seekia/internal/network/appNetworkType/getAppNetworkType"
import "seekia/internal/profiles/viewableProfiles"
import "image"
import "strings"
import "slices"
import "errors"
func setMyContactsPage(window fyne.Window, identityType string, previousPage func()){
appMemory.SetMemoryEntry("CurrentViewedPage", "MyContacts")
if (identityType != "Mate" && identityType != "Moderator" && identityType != "Host"){
setErrorEncounteredPage(window, errors.New("setMyContactsPage called with invalid identityType: " + identityType), previousPage)
return
}
currentPage := func(){ setMyContactsPage(window, identityType, previousPage) }
title := getPageTitleCentered("My " + identityType + " Contacts")
backButton := getBackButtonCentered(previousPage)
addContactButton := getWidgetCentered(widget.NewButtonWithIcon("Add Contact", theme.ContentAddIcon(), func(){
setAddContactPage(window, currentPage, currentPage)
}))
createACategoryButton := widget.NewButtonWithIcon("Create A Category", theme.ContentAddIcon(), func(){
setCreateAContactCategoryPage(window, identityType, currentPage, currentPage)
})
deleteACategoryButton := widget.NewButtonWithIcon("Delete A Category", theme.DeleteIcon(), func(){
setDeleteACategoryPage(window, identityType, currentPage, currentPage)
})
buttonsRow := container.NewHBox(layout.NewSpacer(), addContactButton, createACategoryButton, deleteACategoryButton, layout.NewSpacer())
//TODO: Add change identity type ability?
myContactsMapList, err := myContacts.GetMyContactsMapList(identityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (len(myContactsMapList) == 0){
noContactsExistLabel := getBoldLabelCentered("No " + identityType + " contacts exist.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), addContactButton, widget.NewSeparator(), noContactsExistLabel)
setPageContent(page, window)
return
}
allContactCategoriesList, err := myContacts.GetAllMyContactCategories(identityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
helpers.SortStringListToUnicodeOrder(allContactCategoriesList)
allCategoryOptionsList := []string{"All Contacts"}
allCategoryOptionsList = append(allCategoryOptionsList, allContactCategoriesList...)
allCategoryOptionsList = append(allCategoryOptionsList, "No Category")
getCurrentCategory := func()(string, error){
exists, currentCategory, err := mySettings.GetSetting("My" + identityType + "ContactsPageViewedCategory")
if (err != nil) { return "", err }
if (exists == false){
return "All Contacts", nil
}
isACategory := slices.Contains(allCategoryOptionsList, currentCategory)
if (isACategory == false){
// Category must have been deleted
return "All Contacts", nil
}
return currentCategory, nil
}
currentCategory, err := getCurrentCategory()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
categoryLabel := getBoldLabelCentered("Category:")
categorySelector := widget.NewSelect(allCategoryOptionsList, func(newCategory string){
err := mySettings.SetSetting("My" + identityType + "ContactsPageViewedCategory", newCategory)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
})
categorySelector.Selected = currentCategory
selectCategoryRow := container.NewHBox(layout.NewSpacer(), categoryLabel, categorySelector, layout.NewSpacer())
getContactsContainer := func()(*fyne.Container, error){
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
if (err != nil) { return nil, err }
anyContactsFound := false
contactsContainer := container.NewVBox()
for _, contactMap := range myContactsMapList{
checkIfContactBelongsToCurrentCategory := func()(bool, error){
if (currentCategory == "All Contacts"){
return true, nil
}
contactCategoriesBase64ListString, exists := contactMap["Categories"]
if (exists == false){
if (currentCategory == "No Category"){
return true, nil
}
return false, nil
}
contactCategoriesBase64List := strings.Split(contactCategoriesBase64ListString, "+")
for _, categoryNameBase64 := range contactCategoriesBase64List{
categoryNameString, err := encoding.DecodeBase64StringToUnicodeString(categoryNameBase64)
if (err != nil) {
return false, errors.New("Malformed MyContacts map list: Category name not Base64: " + categoryNameBase64)
}
if (categoryNameString == currentCategory){
return true, nil
}
}
return false, nil
}
contactBelongsToCurrentCategory, err := checkIfContactBelongsToCurrentCategory()
if (err != nil) { return nil, err }
if (contactBelongsToCurrentCategory == false){
continue
}
anyContactsFound = true
contactIdentityHashString, exists := contactMap["IdentityHash"]
if (exists == false) {
return nil, errors.New("Contact map malformed: Missing IdentityHash")
}
contactIdentityHash, _, err := identity.ReadIdentityHashString(contactIdentityHashString)
if (err != nil){
return nil, errors.New("Contact map malformed: contains invalid IdentityHash: " + contactIdentityHashString)
}
contactName, exists := contactMap["Name"]
if (exists == false) {
return nil, errors.New("Contact map malformed: Missing Name")
}
getContactAvatarOrImage := func()(image.Image, error){
getAllowUnknownViewableStatusBool := func()bool{
if (identityType == "Mate"){
return false
}
return true
}
allowUnknownViewableStatusBool := getAllowUnknownViewableStatusBool()
profileFound, _, retrieveAnyUserProfileAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(contactIdentityHash, appNetworkType, true, allowUnknownViewableStatusBool, true)
if (err != nil) { return nil, err }
if (profileFound == false){
emojiImageObject, err := getEmojiImageObject(2929)
if (err != nil) { return nil, err }
return emojiImageObject, nil
}
attributeExists, _, photosAttributeValue, err := retrieveAnyUserProfileAttributeFunction("Photos")
if (err != nil) { return nil, err }
if (attributeExists == true){
base64PhotosList := strings.Split(photosAttributeValue, "+")
firstPhotoBase64 := base64PhotosList[0]
userImageObject, err := imagery.ConvertWebpBase64StringToImageObject(firstPhotoBase64)
if (err != nil) {
return nil, errors.New("Database corrupt: Contains profile with invalid photos attribute.")
}
return userImageObject, nil
}
getContactEmojiIdentifier := func()(int, error){
attributeExists, _, avatarAttributeValue, err := retrieveAnyUserProfileAttributeFunction("Avatar")
if (err != nil) { return 0, err }
if (attributeExists == false){
return 2929, nil
}
userEmojiIdentifier, err := helpers.ConvertStringToInt(avatarAttributeValue)
if (err != nil) {
return 0, errors.New("Database corrupt: Contains profile with invalid Avatar attribute: " + avatarAttributeValue)
}
return userEmojiIdentifier, nil
}
contactEmojiIdentifier, err := getContactEmojiIdentifier()
if (err != nil) { return nil, err }
emojiImageObject, err := getEmojiImageObject(contactEmojiIdentifier)
if (err != nil) { return nil, err }
return emojiImageObject, nil
}
contactImageObject, err := getContactAvatarOrImage()
if (err != nil) { return nil, err }
contactFyneImage := canvas.NewImageFromImage(contactImageObject)
contactFyneImage.FillMode = canvas.ImageFillContain
imageSize := getCustomFyneSize(10)
contactFyneImage.SetMinSize(imageSize)
imageBoxed := getFyneImageBoxed(contactFyneImage)
trimmedIdentityHash, _, err := helpers.TrimAndFlattenString(contactIdentityHashString, 20)
if (err != nil) { return nil, err }
contactIdentityHashLabel := widget.NewLabel(trimmedIdentityHash)
contactNameLabel := getBoldLabel(contactName)
contactNameIdentityHashColumn := getContainerBoxed(container.NewVBox(contactNameLabel, contactIdentityHashLabel))
viewProfileButton := widget.NewButtonWithIcon("Profile", theme.VisibilityIcon(), func(){
setViewPeerProfilePageFromIdentityHash(window, contactIdentityHash, currentPage)
})
chatButton := widget.NewButtonWithIcon("Chat", theme.MailComposeIcon(), func(){
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(identityType)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (myIdentityExists == true && contactIdentityHash == myIdentityHash){
dialogTitle := translate("Identity Hash Is Self.")
dialogMessageA := getLabelCentered("This contact is your own identity.")
dialogMessageB := getLabelCentered("You cannot chat with yourself.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
setViewAConversationPage(window, contactIdentityHash, true, currentPage)
})
manageButton := widget.NewButtonWithIcon("Manage", theme.DocumentCreateIcon(), func(){
setManageContactPage(window, contactIdentityHash, currentPage)
})
row := container.NewHBox(imageBoxed, contactNameIdentityHashColumn, viewProfileButton, chatButton, manageButton)
rowCentered := getContainerCentered(row)
contactRow := getContainerBoxed(rowCentered)
contactsContainer.Add(contactRow)
}
if (anyContactsFound == false){
description1 := getBoldLabelCentered("No contacts belong to this category.")
categoryNameLabel := getLabelCentered("Category Name:")
currentCategoryLabel := getBoldLabelCentered(currentCategory)
categoryNameRow := container.NewHBox(layout.NewSpacer(), categoryNameLabel, currentCategoryLabel, layout.NewSpacer())
noContactsExistContainer := container.NewVBox(description1, categoryNameRow)
return noContactsExistContainer, nil
}
return contactsContainer, nil
}
contactsContainer, err := getContactsContainer()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
page := container.NewVBox(title, backButton, widget.NewSeparator(), buttonsRow, widget.NewSeparator(), selectCategoryRow, widget.NewSeparator(), contactsContainer)
setPageContent(page, window)
}
func setAddContactPage(window fyne.Window, previousPage func(), nextPage func()){
currentPage := func(){setAddContactPage(window, previousPage, nextPage)}
title := getPageTitleCentered("Add Contact")
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Enter the identity hash of your new contact below.")
identityHashEntry := widget.NewEntry()
identityHashEntry.SetPlaceHolder("Enter Identity Hash.")
identityHashEntryBoxed := getWidgetBoxed(identityHashEntry)
identityHashEntryWithDescriptionGrid := getContainerCentered(container.NewGridWithColumns(1, description, identityHashEntryBoxed))
nextButton := getWidgetCentered(widget.NewButtonWithIcon("Next", theme.NavigateNextIcon(), func(){
userIdentityHashString := identityHashEntry.Text
userIdentityHash, userIdentityType, err := identity.ReadIdentityHashString(userIdentityHashString)
if (err != nil){
dialogTitle := translate("Invalid Identity Hash")
dialogMessageA := getLabelCentered("The identity hash you entered is invalid.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(userIdentityType)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
if (myIdentityExists == true && myIdentityHash == userIdentityHash){
dialogTitle := translate("Identity Hash Is Self")
dialogMessageA := getLabelCentered("The identity hash you entered is your own.")
dialogMessageB := getLabelCentered("You cannot add yourself as a contact.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
setAddContactFromIdentityHashPage(window, userIdentityHash, currentPage, nextPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), identityHashEntryWithDescriptionGrid, nextButton)
setPageContent(page, window)
}
func setAddContactFromIdentityHashPage(window fyne.Window, userIdentityHash [16]byte, previousPage func(), nextPage func()){
userIdentityType, err := identity.GetIdentityTypeFromIdentityHash(userIdentityHash)
if (err != nil) {
userIdentityHashHex := encoding.EncodeBytesToHexString(userIdentityHash[:])
setErrorEncounteredPage(window, errors.New("setAddContactFromIdentityHashPage called with invalid identity hash: " + userIdentityHashHex), previousPage)
return
}
currentPage := func(){setAddContactFromIdentityHashPage(window, userIdentityHash, previousPage, nextPage)}
title := getPageTitleCentered("Add Contact")
backButton := getBackButtonCentered(previousPage)
isMyContact, err := myContacts.CheckIfUserIsMyContact(userIdentityHash)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (isMyContact == true){
description1 := getBoldLabelCentered("Cannot add contact.")
description2 := getLabelCentered("User is already a contact.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2)
setPageContent(page, window)
return
}
enterNameLabel := getBoldLabelCentered("Enter contact name:")
nameEntry := widget.NewEntry()
nameEntry.SetPlaceHolder("Enter contact name...")
nameEntryBoxed := getWidgetBoxed(nameEntry)
wideText := " "
nameEntryWidenerA := widget.NewLabel(wideText)
nameEntryWidenerB := widget.NewLabel(wideText)
enterNameEntryWidened := getContainerCentered(container.NewGridWithColumns(3, nameEntryWidenerA, nameEntryBoxed, nameEntryWidenerB))
enterDescriptionLabel := getBoldLabelCentered("Enter contact description:")
descriptionEntry := widget.NewMultiLineEntry()
descriptionEntry.Wrapping = 3
descriptionEntry.SetPlaceHolder("Enter description....")
descriptionEntryBoxed := getWidgetBoxed(descriptionEntry)
descriptionEntryWidenerA := widget.NewLabel(wideText)
descriptionEntryWidenerB := widget.NewLabel(wideText)
enterDescriptionEntryWidened := getContainerCentered(container.NewGridWithColumns(3, descriptionEntryWidenerA, descriptionEntryBoxed, descriptionEntryWidenerB))
page := container.NewVBox(title, backButton, widget.NewSeparator(), enterNameLabel, enterNameEntryWidened, widget.NewSeparator(), enterDescriptionLabel, enterDescriptionEntryWidened, widget.NewSeparator())
allContactCategories, err := myContacts.GetAllMyContactCategories(userIdentityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
contactCategoriesListBinding := binding.NewStringList()
if (len(allContactCategories) != 0){
selectCategoriesLabel := getBoldLabelCentered("Select categories for the contact:")
getNumberOfGridColumns := func()int{
numberOfCategories := len(allContactCategories)
if (numberOfCategories <= 2){
return numberOfCategories
}
return 3
}
numberOfGridColumns := getNumberOfGridColumns()
categoriesGrid := container.NewGridWithColumns(numberOfGridColumns)
for index, categoryName := range allContactCategories{
categoryNameTrimmed, _, err := helpers.TrimAndFlattenString(categoryName, 7)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
onCheckedFunction := func(isChecked bool){
currentContactCategoriesList, err := contactCategoriesListBinding.Get()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (isChecked == true){
newContactCategoriesList := helpers.AddItemToStringListAndAvoidDuplicate(currentContactCategoriesList, categoryName)
contactCategoriesListBinding.Set(newContactCategoriesList)
} else {
2024-08-11 14:31:40 +02:00
newContactCategoriesList, _ := helpers.DeleteAllMatchingItemsFromList(currentContactCategoriesList, categoryName)
contactCategoriesListBinding.Set(newContactCategoriesList)
}
}
categoryCheck := widget.NewCheck(categoryNameTrimmed, onCheckedFunction)
categoryCheckBoxed := getWidgetBoxed(categoryCheck)
categoriesGrid.Add(categoryCheckBoxed)
if (index > 15){
break
}
}
categoriesGridBoxed := getContainerCentered(getContainerBoxed(categoriesGrid))
page.Add(selectCategoriesLabel)
page.Add(categoriesGridBoxed)
}
addContactButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Add Contact"), theme.ConfirmIcon(), func(){
newContactName := nameEntry.Text
if (newContactName == ""){
dialogTitle := translate("Name Is Empty")
dialogMessageA := getLabelCentered(translate("You have not entered a contact name."))
dialogMessageB := getLabelCentered(translate("Enter a name and retry."))
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
newContactDescription := descriptionEntry.Text
contactCategoriesList, err := contactCategoriesListBinding.Get()
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
alreadyExists, err := myContacts.AddContact(userIdentityHash, newContactName, contactCategoriesList, newContactDescription)
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
if (alreadyExists == true){
setErrorEncounteredPage(window, errors.New("Contact already exists after checking already."), currentPage)
return
}
nextPage()
}))
page.Add(addContactButton)
setPageContent(page, window)
}
func setManageContactPage(window fyne.Window, contactIdentityHash [16]byte, previousPage func()){
currentPage := func(){setManageContactPage(window, contactIdentityHash, previousPage)}
title := getPageTitleCentered("Manage Contact")
backButton := getBackButtonCentered(previousPage)
contactExists, contactName, contactAddedTime, contactCategoriesList, contactDescription, err := myContacts.GetMyContactDetails(contactIdentityHash)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (contactExists == false){
setErrorEncounteredPage(window, errors.New("setManageContactPage called with missing contact."), previousPage)
return
}
contactIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(contactIdentityHash)
if (err != nil){
contactIdentityHashHex := encoding.EncodeBytesToHexString(contactIdentityHash[:])
setErrorEncounteredPage(window, errors.New("GetMyContactDetails not verifying identity hash: " + contactIdentityHashHex), previousPage)
return
}
identityHashLabel := widget.NewLabel("Identity Hash:")
identityHashText := getBoldLabel(contactIdentityHashString)
viewIdentityHashButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewIdentityHashPage(window, contactIdentityHash, currentPage)
})
contactIdentityHashRow := container.NewHBox(layout.NewSpacer(), identityHashLabel, identityHashText, viewIdentityHashButton, layout.NewSpacer())
nameLabel := widget.NewLabel("Name:")
contactNameLabel := getBoldLabel(contactName)
contactNameRow := container.NewHBox(layout.NewSpacer(), nameLabel, contactNameLabel, layout.NewSpacer())
contactCategoriesLabel := getLabelCentered("Categories:")
getContactCategoriesListString := func()(string, error){
if (len(contactCategoriesList) == 0){
return "None", nil
}
contactCategoriesListString := strings.Join(contactCategoriesList, ", ")
contactCategoriesListString, _, err := helpers.TrimAndFlattenString(contactCategoriesListString, 20)
if (err != nil) { return "", err }
return contactCategoriesListString, nil
}
contactCategoriesListString, err := getContactCategoriesListString()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
contactCategoriesListLabel := getBoldLabel(contactCategoriesListString)
contactCategoriesRow := container.NewHBox(layout.NewSpacer(), contactCategoriesLabel, contactCategoriesListLabel, layout.NewSpacer())
addedAgoString, err := helpers.ConvertUnixTimeToTimeAgoTranslated(contactAddedTime, false)
addedTimeAgoLabel := getItalicLabelCentered("Contact added " + addedAgoString + ".")
getContactDescriptionRow := func()(*fyne.Container, error){
descriptionLabel := widget.NewLabel("Description:")
if (contactDescription == ""){
noneLabel := getBoldLabel("None")
descriptionRow := container.NewHBox(layout.NewSpacer(), descriptionLabel, noneLabel, layout.NewSpacer())
return descriptionRow, nil
}
trimmedDescription, changesOccurred, err := helpers.TrimAndFlattenString(contactDescription, 20)
if (err != nil) { return nil, err }
if (changesOccurred == false){
contactDescriptionLabel := getBoldLabel(contactDescription)
descriptionRow := container.NewHBox(layout.NewSpacer(), descriptionLabel, contactDescriptionLabel, layout.NewSpacer())
return descriptionRow, nil
}
contactDescriptionTextLabel := getBoldLabel(trimmedDescription)
viewFullDescriptionButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewTextPage(window, "Viewing Contact Description", contactDescription, false, currentPage)
})
contactDescriptionRow := container.NewHBox(layout.NewSpacer(), descriptionLabel, contactDescriptionTextLabel, viewFullDescriptionButton, layout.NewSpacer())
return contactDescriptionRow, nil
}
contactDescriptionRow, err := getContactDescriptionRow()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
renameButton := widget.NewButtonWithIcon("Edit Name", theme.DocumentCreateIcon(), func(){
setRenameContactPage(window, contactIdentityHash, currentPage, currentPage)
})
editCategoriesButton := widget.NewButtonWithIcon("Edit Categories", theme.ListIcon(), func(){
setEditContactCategoriesPage(window, contactIdentityHash, currentPage, currentPage)
})
editDescriptionButton := widget.NewButtonWithIcon("Edit Description", theme.DocumentCreateIcon(), func(){
setEditContactDescriptionPage(window, contactIdentityHash, currentPage, currentPage)
})
deleteButton := widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
setConfirmDeleteContactPage(window, contactIdentityHash, currentPage, previousPage)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, renameButton, editCategoriesButton, editDescriptionButton, deleteButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), contactIdentityHashRow, contactNameRow, contactCategoriesRow, contactDescriptionRow, addedTimeAgoLabel, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
func setRenameContactPage(window fyne.Window, contactIdentityHash [16]byte, previousPage func(), nextPage func()){
title := getPageTitleCentered("Rename Contact")
backButton := getBackButtonCentered(previousPage)
contactExists, contactName, _, currentContactCategoriesList, contactDescription, err := myContacts.GetMyContactDetails(contactIdentityHash)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (contactExists == false){
setErrorEncounteredPage(window, errors.New("Trying to rename contact that does not exist."), previousPage)
return
}
currentNameLabelA := getLabelCentered("Current Contact Name:")
currentNameLabelB := getBoldLabelCentered(contactName)
enterNameLabel := getLabelCentered("Enter new name:")
enterNameEntry := widget.NewEntry()
enterNameEntry.SetPlaceHolder("Enter new name.")
enterNameEntryBoxed := getWidgetBoxed(enterNameEntry)
changeNameButton := getWidgetCentered(widget.NewButtonWithIcon("Change Name", theme.ConfirmIcon(), func(){
newName := enterNameEntry.Text
if (newName == ""){
dialogTitle := translate("Name Is Empty")
dialogMessageA := getLabelCentered("You must enter a new name.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
err := myContacts.EditContact(contactIdentityHash, newName, currentContactCategoriesList, contactDescription)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
nextPage()
}))
widener := widget.NewLabel(" ")
enterNameEntryWithButton := getContainerCentered(container.NewGridWithColumns(1, enterNameEntryBoxed, changeNameButton, widener))
page := container.NewVBox(title, backButton, widget.NewSeparator(), currentNameLabelA, currentNameLabelB, widget.NewSeparator(), enterNameLabel, enterNameEntryWithButton)
setPageContent(page, window)
}
func setEditContactCategoriesPage(window fyne.Window, contactIdentityHash [16]byte, previousPage func(), nextPage func()){
currentPage := func(){setEditContactCategoriesPage(window, contactIdentityHash, previousPage, nextPage)}
contactIdentityType, err := identity.GetIdentityTypeFromIdentityHash(contactIdentityHash)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
contactExists, contactName, _, currentContactCategoriesList, contactDescription, err := myContacts.GetMyContactDetails(contactIdentityHash)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (contactExists == false){
setErrorEncounteredPage(window, errors.New("Trying to rename contact that does not exist."), previousPage)
return
}
allMyContactCategoriesList, err := myContacts.GetAllMyContactCategories(contactIdentityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
title := getPageTitleCentered("Edit Contact Categories")
backButton := getBackButtonCentered(previousPage)
createACategoryButton := getWidgetCentered(widget.NewButtonWithIcon("Create A Category", theme.ContentAddIcon(), func(){
setCreateAContactCategoryPage(window, contactIdentityType, currentPage, currentPage)
}))
if (len(allMyContactCategoriesList) == 0){
noCategoriesExistLabel := getBoldLabelCentered("No categories exist.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), noCategoriesExistLabel, createACategoryButton)
setPageContent(page, window)
return
}
description1 := getLabelCentered("Select the categories the contact should be a member of.")
getNumberOfColumns := func()int{
if (len(allMyContactCategoriesList) == 1){
return 1
}
return 2
}
numberOfColumns := getNumberOfColumns()
categoryChecksGrid := container.NewGridWithColumns(numberOfColumns)
// We sort the categories so they show up in the same order every time.
helpers.SortStringListToUnicodeOrder(allMyContactCategoriesList)
for _, categoryName := range allMyContactCategoriesList{
handleCheckFunction := func(response bool){
getNewCategoriesList := func()[]string{
if (response == true){
newCategoriesList := append(currentContactCategoriesList, categoryName)
return newCategoriesList
}
2024-08-11 14:31:40 +02:00
newCategoriesList, _ := helpers.DeleteAllMatchingItemsFromList(currentContactCategoriesList, categoryName)
return newCategoriesList
}
newCategoriesList := getNewCategoriesList()
err := myContacts.EditContact(contactIdentityHash, contactName, newCategoriesList, contactDescription)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
currentPage()
}
categoryCheck := widget.NewCheck(categoryName, handleCheckFunction)
contactIsInCategory := slices.Contains(currentContactCategoriesList, categoryName)
if (contactIsInCategory == true){
categoryCheck.Checked = true
}
categoryCheckBoxed := getWidgetBoxed(categoryCheck)
categoryChecksGrid.Add(categoryCheckBoxed)
}
categoryChecksGridCentered := getContainerCentered(categoryChecksGrid)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, widget.NewSeparator(), createACategoryButton, widget.NewSeparator(), categoryChecksGridCentered)
setPageContent(page, window)
}
func setEditContactDescriptionPage(window fyne.Window, contactIdentityHash [16]byte, previousPage func(), nextPage func()){
currentPage := func(){setEditContactDescriptionPage(window, contactIdentityHash, previousPage, nextPage)}
title := getPageTitleCentered("Edit Contact Description")
backButton := getBackButtonCentered(previousPage)
contactExists, contactName, _, contactCategoriesList, currentContactDescription, err := myContacts.GetMyContactDetails(contactIdentityHash)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (contactExists == false){
setErrorEncounteredPage(window, errors.New("Trying to rename contact that does not exist."), previousPage)
return
}
editDescriptionDescription := getLabelCentered("Edit your contact description below.")
enterDescriptionEntry := widget.NewMultiLineEntry()
enterDescriptionEntry.Wrapping = 3
if (currentContactDescription == ""){
enterDescriptionEntry.SetPlaceHolder("Enter description.")
} else {
enterDescriptionEntry.SetText(currentContactDescription)
}
enterDescriptionEntryBoxed := getWidgetBoxed(enterDescriptionEntry)
entryHeightenerA := container.NewVBox(widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel(""))
entryHeightenerB := container.NewVBox(widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel(""))
changeDescriptionButton := getWidgetCentered(widget.NewButtonWithIcon("Change Description", theme.ConfirmIcon(), func(){
newContactDescription := enterDescriptionEntry.Text
err := myContacts.EditContact(contactIdentityHash, contactName, contactCategoriesList, newContactDescription)
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
nextPage()
}))
enterDescriptionEntryWithButton := container.NewBorder(nil, changeDescriptionButton, entryHeightenerA, entryHeightenerB, enterDescriptionEntryBoxed)
page := container.NewVBox(title, backButton, widget.NewSeparator(), editDescriptionDescription, enterDescriptionEntryWithButton)
setPageContent(page, window)
}
func setConfirmDeleteContactPage(window fyne.Window, contactIdentityHash [16]byte, previousPage func(), nextPage func()){
currentPage := func(){setConfirmDeleteContactPage(window, contactIdentityHash, previousPage, nextPage)}
contactExists, contactName, contactAddedTime, _, contactDescription, err := myContacts.GetMyContactDetails(contactIdentityHash)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (contactExists == false){
setErrorEncounteredPage(window, errors.New("setConfirmDeleteContactPage called with non-contact identity hash"), previousPage)
return
}
title := getPageTitleCentered("Confirm Delete Contact")
backButton := getBackButtonCentered(previousPage)
description1 := getBoldLabelCentered("Delete contact?")
description2 := getLabelCentered("The user will be removed from your contacts.")
contactIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(contactIdentityHash)
if (err != nil){
contactIdentityHashHex := encoding.EncodeBytesToHexString(contactIdentityHash[:])
setErrorEncounteredPage(window, errors.New("setConfirmDeleteContactPage not verifying identity hash: " + contactIdentityHashHex), previousPage)
}
identityHashTitle := widget.NewLabel("Identity Hash:")
identityHashLabel := getBoldLabel(contactIdentityHashString)
identityHashRow := container.NewHBox(layout.NewSpacer(), identityHashTitle, identityHashLabel, layout.NewSpacer())
contactNameLabel := widget.NewLabel("Contact Name:")
contactNameText := getBoldLabel(contactName)
contactNameRow := container.NewHBox(layout.NewSpacer(), contactNameLabel, contactNameText, layout.NewSpacer())
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), identityHashRow, contactNameRow)
if (contactDescription != ""){
contactDescriptionLabel := widget.NewLabel("Contact Description:")
contactDescriptionTrimmed, changesOccurred, err := helpers.TrimAndFlattenString(contactDescription, 20)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
contactDescriptionTextLabel := getBoldLabel(contactDescriptionTrimmed)
if (changesOccurred == false){
contactDescriptionRow := container.NewHBox(layout.NewSpacer(), contactDescriptionLabel, contactDescriptionTextLabel, layout.NewSpacer())
page.Add(contactDescriptionRow)
} else {
viewFullDescriptionButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
setViewTextPage(window, "Viewing Contact Description", contactDescription, false, currentPage)
})
contactDescriptionRow := container.NewHBox(layout.NewSpacer(), contactDescriptionLabel, contactDescriptionTextLabel, viewFullDescriptionButton, layout.NewSpacer())
page.Add(contactDescriptionRow)
}
}
addedAgoString, err := helpers.ConvertUnixTimeToTimeAgoTranslated(contactAddedTime, false)
addedTimeAgoLabel := getItalicLabelCentered("Contact added " + addedAgoString + ".")
page.Add(addedTimeAgoLabel)
deleteContactButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm Delete", theme.DeleteIcon(), func(){
err := myContacts.DeleteContact(contactIdentityHash)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
nextPage()
}))
page.Add(deleteContactButton)
setPageContent(page, window)
}
func setCreateAContactCategoryPage(window fyne.Window, identityType string, previousPage func(), nextPage func()){
currentPage := func(){setCreateAContactCategoryPage(window, identityType, previousPage, nextPage)}
title := getPageTitleCentered("Create " + identityType + " Contact Category")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Create a " + identityType + " contact category.")
enterNameLabel := getBoldLabelCentered("Enter category name:")
descriptionsContainer := container.NewVBox(description1, enterNameLabel)
enterNameEntry := widget.NewEntry()
enterNameEntry.SetPlaceHolder("Enter category name.")
enterNameEntryBoxed := getWidgetBoxed(enterNameEntry)
enterNameEntryWithDescriptions := getContainerCentered(container.NewGridWithColumns(1, descriptionsContainer, enterNameEntryBoxed))
contactCategoriesList, err := myContacts.GetAllMyContactCategories(identityType)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
createCategoryButton := getWidgetCentered(widget.NewButtonWithIcon("Create Category", theme.ConfirmIcon(), func(){
categoryName := enterNameEntry.Text
if (categoryName == ""){
dialogTitle := translate("Name Is Empty")
dialogMessageA := getLabelCentered("You must enter a category name.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
categoryAlreadyExists := slices.Contains(contactCategoriesList, categoryName)
if (categoryAlreadyExists == true){
dialogTitle := translate("Category Already Exists")
dialogMessageA := getLabelCentered("The category you are trying to create already exists.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
err := myContacts.AddContactCategory(identityType, categoryName)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
nextPage()
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), enterNameEntryWithDescriptions, createCategoryButton)
if (len(contactCategoriesList) != 0){
page.Add(widget.NewSeparator())
existingCategoriesLabel := getItalicLabelCentered("Existing Categories:")
page.Add(existingCategoriesLabel)
for _, categoryName := range contactCategoriesList{
categoryNameLabel := getBoldLabelCentered(categoryName)
page.Add(categoryNameLabel)
}
}
setPageContent(page, window)
}
func setDeleteACategoryPage(window fyne.Window, identityType string, previousPage func(), nextPage func()){
currentPage := func(){setDeleteACategoryPage(window, identityType, previousPage, nextPage)}
title := getPageTitleCentered("Delete " + identityType + " Contact Category")
backButton := getBackButtonCentered(previousPage)
myContactCategoriesList, err := myContacts.GetAllMyContactCategories(identityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (len(myContactCategoriesList) == 0){
description1 := getBoldLabelCentered("No " + identityType + " contact categories exist.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1)
setPageContent(page, window)
return
}
description := getLabelCentered("Select the contact category to delete.")
// We sort the categories so they show up in the same order every time.
helpers.SortStringListToUnicodeOrder(myContactCategoriesList)
categorySelector := widget.NewSelect(myContactCategoriesList, nil)
categorySelector.PlaceHolder = "Select Category..."
categorySelectorCentered := getWidgetCentered(categorySelector)
deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Category", theme.DeleteIcon(), func(){
categoryToDeleteIndex := categorySelector.SelectedIndex()
if (categoryToDeleteIndex == -1){
dialogTitle := translate("No Category Selected")
dialogMessageA := getLabelCentered("You must select a category to delete.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
categoryToDelete := categorySelector.Selected
err := myContacts.DeleteContactCategory(identityType, categoryToDelete)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
nextPage()
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), categorySelectorCentered, deleteButton)
setPageContent(page, window)
}