Skip to content
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

[ABW-3712] Allow to hide/unhide resources on Profile #199

Merged
merged 18 commits into from
Aug 16, 2024

Conversation

matiasbzurovski
Copy link
Contributor

Changelog

  • Adds a new ResourcePreferences (stored under Profile.AppPreferences) that will allow user to hide fungible tokens, non-fungible tokens & pool units. Zeplin
  • Extends Security to store new flag to indicate if advanced lock is required.

Copy link

codecov bot commented Aug 14, 2024

Codecov Report

Attention: Patch coverage is 99.21260% with 1 line in your changes missing coverage. Please review.

Project coverage is 97.4%. Comparing base (cde0859) to head (03e53c4).

Files Patch % Lines
src/core/types/bool_type.rs 93.7% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@          Coverage Diff           @@
##            main    #199    +/-   ##
======================================
  Coverage   97.3%   97.4%            
======================================
  Files        912     921     +9     
  Lines      14604   14727   +123     
  Branches      64      64            
======================================
+ Hits       14224   14346   +122     
- Misses       373     374     +1     
  Partials       7       7            
Flag Coverage Δ
kotlin 98.7% <100.0%> (+<0.1%) ⬆️
rust 96.8% <99.0%> (+<0.1%) ⬆️
swift 99.3% <100.0%> (+<0.1%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
...n/Drivers/HostInfo/HostInfoDriver+DeviceInfo.swift 96.2% <ø> (ø)
...thods/Profile/AssetPreference+Wrap+Functions.swift 100.0% <100.0%> (ø)
...com/radixdlt/sargon/extensions/AssetPreferences.kt 100.0% <100.0%> (ø)
...rc/profile/v100/app_preferences/app_preferences.rs 100.0% <100.0%> (ø)
...app_preferences/asset_preferences/asset_address.rs 100.0% <100.0%> (ø)
...p_preferences/asset_preferences/asset_addresses.rs 100.0% <100.0%> (ø)
..._preferences/asset_preferences/asset_preference.rs 100.0% <100.0%> (ø)
...preferences/asset_preferences/asset_preferences.rs 100.0% <100.0%> (ø)
...s/asset_preferences/asset_preferences_uniffi_fn.rs 100.0% <100.0%> (ø)
..._preferences/asset_preferences/asset_visibility.rs 100.0% <100.0%> (ø)
... and 4 more

Copy link
Contributor

@CyonAlexRDX CyonAlexRDX left a comment

Choose a reason for hiding this comment

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

Good start!

sut.hideResource(kind: .nonFungible(.sample))
sut.hideResource(kind: .poolUnit(.sample))

XCTAssertEqual([.sampleOther, .sample], sut.hiddenResources.fungible)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I've used the other way around for all tests: lhs=actual, rhs=expected

Which is the "official" order, as set by Apple themselves, see eg test inside of XCTTest itself: https://github.com/swiftlang/swift-corelibs-xctest/blob/main/Tests/Functional/Observation/Selected/main.swift

#[derive(
Clone, Debug, PartialEq, Eq, Hash, derive_more::Display, uniffi::Enum,
)]
pub enum ResourcePreferenceKind {
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a macro you can use here! See the AddressOfAccountOrPersona, which is declared using address_union!(enum ResourcePreferenceKind: fungible, nonFungible, poolUnit);

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe enum AssetAddress ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

there is no such thing as a FungibleAddress (so it would represent fungibles as resource), nor we want a NonFungibleResourceAddress (we want NonFungibleGlobalId), so not sure this address_union! macro helps us here.

pub struct ResourcePreferences {
/// A dictionary detailing the user preferences for fungible resources.
#[serde(default)]
pub fungible: HashMap<ResourceAddress, EntityFlags>,
Copy link
Contributor

Choose a reason for hiding this comment

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

EntityFlag is wrong. The word "Entity" means "Account or Identity". At least in wallet context.

But taking one step back, what can you do more than hide? If nothing: we could use just IndexSet? Containing "hidden" addresses.

Since we are showing the different asset kinds in different sections in ui a simpler format would be to not introduce any new address union and just use:

struct AssetPreferences {
    hidden_fungibles: IndexSet<ResourceAddress>,
    hidden_pool_units: IndexSet<PoolUnitAddress>,
...
}

If we don't know of any planned other setting than hiding, this is a much more compact format and simpler.

But it is a bad format if we are indeed going to add support for another enum variant than "Hidden, NonHidden" eg: ConditionallyHiddenIfWorthMoreThan1000USd.

Ok I'm leaning towards a new AssetPreferences enum.

Copy link
Contributor

Choose a reason for hiding this comment

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

so it would be something like:

pub enum AssetVisibility {
    Hidden, 
    // Just an example of a potential future variant
    ConditionallyHiddenIfValueIsLessThan1000Usd
}
pub struct AssetPreference {
    address: AssetAddress, // fungible | nonFungible | poolUnit

    /// This does NOT allow for other flags, it is a mutually exclusive state.
    visibility: AssetVisibility
}

struct AssetPreference {
   assets: IndexMap<AssetAddress, AssetPreference>
}

Advantages:

  • This retains insertion order of all preferences, "globally" (if ever interesting).
  • Is more compact
  • If we want preferences for more than visibility, we add a new enum for that, and a new field "next to" visibility.

This is also future proof, since we can always add more enum variants to AssetVisibility and we can alway add new enum types and fiels to AssetPreference. So... should work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree that maybe EntityFlags aren't a great representation of "flags for an address". However, the reason I used them, is because if we ever extend the configuration of what is defined for entities, it probably applies for assets as well.

Of course, this is all based on assumptions that we don't know yet how will they turn out. In your ConditionallyHiddenIfValueIsLessThan1000Usd, then it definitely makes sense to have the new resource configuration using a new preference definition (as the one you suggested). On my case, I thought a good example could have been "marking a resource/persona/account as favorite". In such case, a new EntityFlag::IsFavorite would fit for all of them.

Copy link
Contributor

Choose a reason for hiding this comment

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

Shared variants is not the same as semantically the same enum :) they should be able to evolve independently of each other. Also DeletedByUser is the intention of account, they dont want it. Initially we gave the button the name Delete, then changed to Hide. Whereas "Hide asset" feature truely is a hide, since it is stored in the immutable Ledger - thus never can be deleted! So it should be named WhatIsTheNameOfTheEnum::Hidden and not ::Deleted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but with that argument hidden accounts/personas should also be named ::Hiddensince they aren't deleted from Ledger

pub struct ResourcePreferences {
/// A dictionary detailing the user preferences for fungible resources.
#[serde(default)]
pub fungible: HashMap<ResourceAddress, EntityFlags>,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think fungibles, plural, no? It is a HashMap with settings for fungibles? (Or strictly: fungible assets plural)


impl ResourcePreferences {
pub fn get_hidden_resources(&self) -> HiddenResources {
let fungible = self
Copy link
Contributor

Choose a reason for hiding this comment

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

You can probably use a closure unlined here, being a function you reuse for the three collections (Rust does not allow nested functions, but you can use closure).

pub struct ResourcePreferences {
/// A dictionary detailing the user preferences for fungible resources.
#[serde(default)]
pub fungible: HashMap<ResourceAddress, EntityFlags>,
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use IndexMap to retain insertion order!

is_cloud_profile_sync_enabled,
is_developer_mode_enabled
is_developer_mode_enabled,
is_advanced_lock_enabled
)]
pub struct Security {
pub is_cloud_profile_sync_enabled: bool,
Copy link
Contributor

Choose a reason for hiding this comment

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

I would probably start using new types around bools now, easy to accidentally pass the wrong bool to the wrong value otherwise

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sorry, couldn't get what you meant here

Copy link
Contributor

@Sajjon Sajjon Aug 15, 2024

Choose a reason for hiding this comment

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

I mean we should change from:

pub struct Security {
   ...
    pub is_cloud_profile_sync_enabled: bool,
    pub is_developer_mode_enabled: bool,
    pub is_advanced_lock_enabled: bool,
...
}

To:

#[derive(..., )]
#[serde(transparent)]
pub struct IsCloudSyncEnabled(bool);

#[derive(..., )]
#[serde(transparent)]
pub struct IsDeveloperModeEnabled(bool);
pub struct IsAdvancedLockEnabled(bool);

uniffi::custom_newtype!(IsCloudSyncEnabled, bool);
uniffi::custom_newtype!(IsDeveloperModeEnabled, bool);
...

pub struct Security {
   ...
    pub is_cloud_profile_sync_enabled: IsCloudSyncEnabled,
    pub is_developer_mode_enabled: IsDeveloperModeEnabled,
    pub is_advanced_lock_enabled: IsAdvancedLockEnabled,
...
}

Making everything type safe :) this is using the "new type" paradigm, like Tagged package in by Pointfree

(we seems suitable to use a macro for it perhaps! decl newtype_bool!(IsDeveloperModeEnabled); ...

#[display("assets: {:#?}", self.assets)]
pub struct AssetPreferences {
#[serde(default)]
pub assets: HashMap<AssetAddress, AssetPreference>,
Copy link
Contributor

Choose a reason for hiding this comment

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

should be IndexMap else we loose insertion order.

Otherwise might be worth doing like we do with most collection: let AssetAddress impl Identifiable and use the macro to declare a collection of AssetPreferences plural. And use that here.

Copy link
Contributor

Choose a reason for hiding this comment

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

oh right this whole thing should probably be decl_identified_vec_of(AssetPreference) which creates a AssetPreferences which is serde transparent, so it will be [] in JSON. Which is pretty nice.

}

impl Hash for AssetPreferences {
fn hash<H: Hasher>(&self, state: &mut H) {
Copy link
Contributor

Choose a reason for hiding this comment

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

and using the IdentifiedVec will allow you to derive Hash instead of manually implementing it.

Copy link
Contributor

@Sajjon Sajjon left a comment

Choose a reason for hiding this comment

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

Nice, getting there! I would add doc an all new types and pub methods


#[test]
fn default() {
assert!(ExampleTrue::default().0);
Copy link
Contributor

Choose a reason for hiding this comment

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

If you implement Deref then you can write *sut instead of sut.0 which imo is a bit cleaner

}

impl AssetPreference {
pub fn set_visibility(&mut self, visibility: AssetVisibility) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably no need for a setter method. Can use pub field directly

x.set_visibility(AssetVisibility::Hidden)
}) {
if !self
.update_with(asset.id(), |x| x.visibility = AssetVisibility::Hidden)
Copy link
Contributor

Choose a reason for hiding this comment

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

I THINK you can use write update_with(asset, .. ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would need to do update_with(asset.clone(), ..) in such case since I would be borrowing the asset (which is used in next line if update fails). So guess it is cheaper to leave as is?

Copy link
Contributor

Choose a reason for hiding this comment

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

Update with takes an impl Borrow. I thought it auto borrowed, but im probably mistaken

@@ -46,6 +46,13 @@
},
"transaction": {
"defaultDepositGuarantee": "0.975"
},
"resource": {
Copy link
Contributor

Choose a reason for hiding this comment

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

this vectors needs to be updated right? @matiasbzurovski

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch! actually I am gonna revert this change since this JSON isn't used to validate the AssetsPreferences and its corresponding test can deal without any assets entry on the JSON.

sergiupuhalschi-rdx and others added 2 commits August 16, 2024 13:26
* Add kt extensions for resource preferences

* Remove packaging option

* Update asset preferences kt extensions
Copy link
Contributor

@CyonAlexRDX CyonAlexRDX left a comment

Choose a reason for hiding this comment

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

LGTM! Good job! Congratulations on your first macro!

@matiasbzurovski matiasbzurovski merged commit 5903204 into main Aug 16, 2024
10 checks passed
@matiasbzurovski matiasbzurovski deleted the ABW-3712-hidden-resources branch August 16, 2024 11:51
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.

4 participants