Skip to content

Implement Contacts #469 #500

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open

Conversation

bnsports
Copy link

@bnsports bnsports commented Mar 8, 2025

Implemented Feature description

Screenshots:

To Do:

  1. Localization
  2. Icon
  3. Input validators review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bnsports, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

Summary of Changes

This pull request implements the Contacts feature, as described in issue #469. It introduces a new Contacts feature module with associated navigation, scenes, view models, and data types for managing a list of contacts and their addresses. The pull request also includes UI elements for adding, editing, and deleting contacts and their addresses, as well as integrating contact functionality into the recipient selection flow for transfers. The changes also include adding ContactService and DataValidation as new packages.

Highlights

  • New Feature: Contacts: Implements the core Contacts feature, allowing users to manage a list of contacts with names, descriptions, and associated addresses.
  • UI Implementation: Introduces new SwiftUI views and navigation flows for displaying contact lists, adding new contacts, and managing contact addresses.
  • Data Validation: Adds a new DataValidation package with validators for contact names, descriptions, and blockchain addresses.
  • Service Layer: Introduces a ContactService for managing contact data, including adding, editing, and deleting contacts and addresses.
  • Integration with Transfer Flow: Integrates contact address selection into the recipient selection flow for initiating transfers.
  • Settings Integration: Adds a Contacts entry to the Settings screen.

Changelog

Click here to see the changelog
  • Features/Contacts/.gitignore
    • Added standard git ignore entries for swift packages and build artifacts.
  • Features/Contacts/Package.swift
    • Created a new Swift package for the Contacts feature, declaring dependencies on ContactService and DataValidation.
  • Features/Contacts/Sources/Navigation/AddContactAddressNavigationView.swift
    • Introduced a navigation view for adding or editing contact addresses, including chain selection and QR code scanning.
  • Features/Contacts/Sources/Navigation/AddContactNavigationView.swift
    • Introduced a navigation view for adding or editing contact details (name, description).
  • Features/Contacts/Sources/Navigation/ContactAddressListNavigationView.swift
    • Created a navigation view for displaying a list of contact addresses and providing options to add or edit them.
  • Features/Contacts/Sources/Navigation/ContactListNavigationView.swift
    • Implemented the main contact list navigation view, allowing users to view, add, edit, and delete contacts.
  • Features/Contacts/Sources/Scenes/AddContactAddressScene.swift
    • Implemented the scene for adding or editing contact addresses, including UI elements for address input, chain selection, and memo.
  • Features/Contacts/Sources/Scenes/AddContactScene.swift
    • Implemented the scene for adding or editing contact details (name, description).
  • Features/Contacts/Sources/Scenes/ContactAddressListScene.swift
    • Implemented the scene for displaying a list of contact addresses, with options to add, edit, and delete addresses.
  • Features/Contacts/Sources/Scenes/ContactListScene.swift
    • Implemented the main contact list scene, displaying a list of contacts and providing options to add, edit, and delete contacts.
  • Features/Contacts/Sources/Types/AddContactAddressInput.swift
    • Defined the AddContactAddressInput struct for managing input data when adding or editing contact addresses, including validation logic.
  • Features/Contacts/Sources/Types/AddContactInput.swift
    • Defined the AddContactInput struct for managing input data when adding or editing contact details (name, description), including validation logic.
  • Features/Contacts/Sources/Types/ContactAddressListViewItem.swift
    • Defined the ContactAddressListViewItem struct for representing contact addresses in a list view.
  • Features/Contacts/Sources/Types/ContactListViewItem.swift
    • Defined the ContactListViewItem struct for representing contacts in a list view.
  • Features/Contacts/Sources/Types/ContactViewTypes.swift
    • Defined type aliases for contact-related handlers.
  • Features/Contacts/Sources/View/ContactAddressListItemView.swift
    • Implemented the ContactAddressListItemView for displaying contact address information in a list.
  • Features/Contacts/Sources/View/ContactListItemView.swift
    • Implemented the ContactListItemView for displaying contact information in a list.
  • Features/Contacts/Sources/ViewModels/AddContactAddressViewModel.swift
    • Implemented the AddContactAddressViewModel for managing the state and logic of the add/edit contact address scene.
  • Features/Contacts/Sources/ViewModels/AddContactViewModel.swift
    • Implemented the AddContactViewModel for managing the state and logic of the add/edit contact scene.
  • Features/Contacts/Sources/ViewModels/ContactAddressListViewModel.swift
    • Implemented the ContactAddressListViewModel for managing the state and logic of the contact address list scene.
  • Features/Contacts/Sources/ViewModels/ContactListViewModel.swift
    • Implemented the ContactListViewModel for managing the state and logic of the contact list scene.
  • Features/Contacts/Tests/ContactsTests/ContactsTests.swift
    • Added an example test case for the Contacts feature.
  • Features/Settings/Sources/Scenes/SettingsScene.swift
    • Added a navigation link to the Contacts feature in the Settings scene (line 68).
  • Features/Settings/Sources/ViewModels/SettingsViewModel.swift
    • Added properties for the Contacts title and image in the Settings view model (lines 45-46).
  • Gem.xcodeproj/project.pbxproj
    • Updated the project file to include the new Contacts feature, ContactService, and DataValidation packages, and to link the Contacts feature to the main app target.
  • Gem/App-Configuration.swift
    • Registered the ContactService and ContactStore as main services and extensions.
  • Gem/Assets/Navigation/SelectAssetSceneNavigationStack.swift
    • Updated RecipientNavigationView to use RecipientViewModel.
  • Gem/Core/AppResolver/AppResolver+Services.swift
    • Added contactService to the AppResolver.
  • Gem/Core/AppResolver/ServicesFactory.swift
    • Added contactService to the ServicesFactory.
  • Gem/Core/Types/Environment.swift
    • Added contactService to the EnvironmentValues.
  • Gem/Root/Navigation/SelectedAsset/SelectedAssetNavigationStack.swift
    • Updated RecipientNavigationView to use RecipientViewModel.
  • Gem/Root/Navigation/Settings/SettingsNavigationStack.swift
    • Added navigation to the ContactListNavigationView from the settings screen.
  • Gem/Transfer/Navigation/RecipientNavigationView.swift
    • Updated RecipientNavigationView to use RecipientViewModel.
  • Gem/Transfer/Scenes/RecipientScene.swift
    • Updated RecipientScene to use RecipientViewModel and display contact addresses.
  • Gem/Transfer/ViewsModels/RecipientViewModel.swift
    • Added RecipientViewModel to manage recipient data and contact address selection.
  • Packages/DataValidation/.gitignore
    • Added standard git ignore entries for swift packages and build artifacts.
  • Packages/DataValidation/Package.swift
    • Created a new Swift package for data validation, declaring a dependency on Primitives and Keystore.
  • Packages/DataValidation/Sources/Implementations/*.swift
    • Implemented validators for blockchain addresses, chain selection, contact address memos, contact descriptions, and contact names.
  • Packages/DataValidation/Sources/Types/ValidatedInput.swift
    • Defined the ValidatedInput struct for managing validated input data.
  • Packages/DataValidation/Sources/ValidatorConvertible.swift
    • Defined the ValidatorConvertible protocol for creating validators.
  • Packages/DataValidation/Tests/DataValidationTests/*.swift
    • Added test cases for the data validators.
  • Packages/Primitives/Sources/Contact.swift
    • Defined the Contact and ContactId structs for representing contact information.
  • Packages/Primitives/Sources/ContactAddress.swift
    • Defined the ContactAddress and ContactAddressId structs for representing contact addresses.
  • Packages/Primitives/Sources/ContactAddressInfo.swift
    • Defined the ContactAddressInfo struct for representing contact address information.
  • Packages/Primitives/Sources/Errors/ValidationError.swift
    • Defined the ValidationError enum for representing validation errors.
  • Packages/Primitives/Sources/Scenes.swift
    • Added new scenes for Contacts.
  • Packages/PrimitivesComponents/Sources/Types/CreateUpdateViewType.swift
    • Defined the EntityEditorViewType enum for representing create/update view types.
  • Packages/PrimitivesComponents/Sources/ViewModels/EntityEditorViewModel.swift
    • Defined the EntityEditorViewModel class for managing entity editor view models.
  • Packages/Store/Sources/Columns.swift
    • Added columns for Contact and ContactAddress.
  • Packages/Store/Sources/Migrations.swift
    • Added migrations for Contact and ContactAddress.
  • Packages/Store/Sources/Models/*.swift
    • Added models for ContactAddressInfoRecord, ContactAddressRecord, and ContactRecord.
  • Packages/Store/Sources/Requests/*.swift
    • Added requests for ContactAddressInfoListRequest, ContactAddressListRequest, and ContactListRequest.
  • Packages/Store/Sources/Stores/*.swift
    • Added stores for ContactStore and updated StoreManager.
  • Packages/Style/Sources/Images.swift
    • Added images for contacts.
  • Packages/Style/Sources/Resources/Assets.xcassets/settings/settings_contacts.imageset/*.svg
    • Added SVG asset for contacts.
  • Packages/Style/Sources/TextStyle.swift
    • Added bodyBold text style.
  • Services/ContactService/.gitignore
    • Added standard git ignore entries for swift packages and build artifacts.
  • Services/ContactService/Package.swift
    • Created a new Swift package for the ContactService, declaring dependencies on Primitives, Store, and GemAPI.
  • Services/ContactService/Sources/ContactService.swift
    • Implemented the ContactService for managing contact data.
  • Services/ContactService/Tests/ContactServiceTests/ContactServiceTests.swift
    • Added an example test case for the ContactService.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.


Did you know?

The term 'contact list' has evolved from physical address books to digital databases, reflecting the shift from paper-based record-keeping to electronic data management.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request implements the Contacts feature, allowing users to manage a list of contacts and their associated addresses. The implementation appears well-structured and integrates with existing components. However, there are some areas that could benefit from further review and potential improvements, particularly regarding error handling and data validation.

Summary of Findings

  • Missing Error Handling: There are several places where errors are caught but simply localized and presented to the user. More robust error handling, such as logging or more specific error messages, could improve debugging and user experience.
  • Data Validation: While data validation is implemented, ensuring that validation occurs at the appropriate layers and that error messages are user-friendly is crucial. Review the validators to ensure they cover all edge cases.
  • ViewModel Initialization: Some ViewModels are initialized directly in the View structs. Consider using dependency injection or a factory pattern to improve testability and maintainability.

Merge Readiness

The pull request introduces a significant feature and requires careful review before merging. While the code appears functional, the identified issues regarding error handling and data validation should be addressed to ensure a robust and user-friendly experience. I am unable to approve this pull request, and recommend that it not be merged until the critical and high severity issues are addressed, and that others review and approve this code before merging.

Comment on lines 121 to 126
private func onSelectConfirm() {
do {
try model.confirmAddContact()
} catch {
presentingErrorMessage = error.localizedDescription
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The confirmAddContact function catches errors and presents a localized description to the user. Consider logging the error for debugging purposes or providing a more specific error message to the user.

        } catch {
            presentingErrorMessage = error.localizedDescription
            // Log the error for debugging
            print("Error adding contact: \(error)")
        }

Comment on lines 83 to 88
do {
try model.confirmAddContact()
} catch {
presentingErrorMessage = error.localizedDescription
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to AddContactAddressScene.swift, the error handling here is basic. Logging the error or providing more context to the user could be beneficial.

        } catch {
            presentingErrorMessage = error.localizedDescription
            // Log the error for debugging
            print("Error adding contact: \(error)")
        }

Comment on lines 112 to 117
do {
try model.delete(address: address)
} catch {
presentingErrorMessage = error.localizedDescription
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The didTapConfirmDelete function catches errors and presents a localized description. Consider logging the error for debugging purposes.

        } catch {
            presentingErrorMessage = error.localizedDescription
            // Log the error for debugging
            print("Error deleting address: \(error)")
        }

Comment on lines 112 to 117
private func didTapConfirmDelete(contact: Contact) {
do {
try model.delete(contact: contact)
} catch {
presentingErrorMessage = error.localizedDescription
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The didTapConfirmDelete function catches errors and presents a localized description. Consider logging the error for debugging purposes.

        } catch {
            presentingErrorMessage = error.localizedDescription
            // Log the error for debugging
            print("Error deleting contact: \(error)")
        }

…ontacts

# Conflicts:
#	Packages/Store/Sources/Migrations.swift
].allSatisfy({$0})
}

static private func createIdForNewEntity() -> String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we delegate sqlite to create ID automatically?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we want to have cross-device contact synchronization in the future we cannot use auto-incremented integer ids cause it will lead to conflicts. so we need 'random' string identifier which sqlite cannot create automatically

I can suggest to have ContactAddressRecord initializer with nullable id parameter and we can move id generation to database layer

ChainView(model: ChainViewModel(chain: model.input.chain))
}
FloatTextField(model.addressTextFieldTitle, text: $model.projectedValue.input.address.value ?? "", allowClean: false) {
HStack(spacing: .large/2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider to use default Spacing, for consistency UI

}

private func onSelectPaste() {
guard let address = UIPasteboard.general.string else { return }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the logic to viewModel

}

private func presentCreateError(_ error: Error) {
if (error as? DatabaseError)?.extendedResultCode == .SQLITE_CONSTRAINT_UNIQUE {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same, move the logic to viewModel

}
}
.overlay {
if contacts.isEmpty {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to use EmptyContentView

} catch {
presentCreateError(error)
}
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to process all the cases

@@ -19,10 +22,15 @@ enum RecipientScanResult {
case transferData(TransferData)
}

class RecipientViewModel: ObservableObject {
@Observable
class RecipientViewModel {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final class

}

public func fetch(_ db: Database) throws -> [ContactAddressData] {
do {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove do catch block, use just try ContactAddressRecord

}

public func fetch(_ db: Database) throws -> [ContactAddress] {
guard let contact else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grdb should return defaultValue ?

public init() { }

public func fetch(_ db: Database) throws -> [Contact] {
do {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove do catch


extension ContactService {
public func add(contact: Contact) throws -> ContactId {
return try contactStore.addContact(contact)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all return

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants