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

PE-7214: feat(primary name) #1927

Merged
merged 17 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion lib/arns/domain/arns_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ abstract class ARNSRepository {
required String processId,
bool uploadNewRevision = true,
});

Future<List<sdk.ANTRecord>> getAntRecordsForWallet(String address,
{bool update = false});
Future<List<sdk.ARNSUndername>> getARNSUndernames(sdk.ANTRecord record,
Expand All @@ -33,6 +32,7 @@ abstract class ARNSRepository {
Future<void> saveAllFilesWithAssignedNames();
Future<List<ArnsRecord>> getActiveARNSRecordsForFile(String fileId);
Future<void> waitForARNSRecordsToUpdate();
Future<String> getPrimaryName(String address, {bool update = false});

factory ARNSRepository({
required ArioSDK sdk,
Expand Down Expand Up @@ -83,11 +83,13 @@ class _ARNSRepository implements ARNSRepository {
auth.onAuthStateChanged().listen((user) {
if (user == null) {
_cachedUndernames.clear();
_cachedPrimaryName = null;
}
});
}

final Map<String, Map<String, ARNSUndername>> _cachedUndernames = {};
String? _cachedPrimaryName;

@override
Future<void> setUndernamesToFile({
Expand Down Expand Up @@ -371,6 +373,23 @@ class _ARNSRepository implements ARNSRepository {

await _getARNSUndernamesCompleter!.future;
}

@override
Future<String> getPrimaryName(String address, {bool update = false}) async {
logger.d('Getting primary name for address: $address');

if (!update && _cachedPrimaryName != null) {
return _cachedPrimaryName!;
}

final primaryName = await _sdk.getPrimaryName(address);

logger.d('Primary name: $primaryName');

_cachedPrimaryName = primaryName;

return primaryName;
}
}

AntRecord toAntRecordFromSDK(sdk.ANTRecord record) {
Expand Down
2 changes: 2 additions & 0 deletions lib/authentication/login/views/login_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:ardrive/services/authentication/biometric_authentication.dart';
import 'package:ardrive/services/authentication/biometric_permission_dialog.dart';
import 'package:ardrive/services/ethereum/provider/ethereum_provider.dart';
import 'package:ardrive/turbo/services/upload_service.dart';
import 'package:ardrive/user/name/presentation/bloc/profile_name_bloc.dart';
import 'package:ardrive/user/repositories/user_repository.dart';
import 'package:ardrive/utils/app_localizations_wrapper.dart';
import 'package:ardrive/utils/logger.dart';
Expand Down Expand Up @@ -95,6 +96,7 @@ class _LoginPageState extends State<LoginPage> {
if (loginState is LoginSuccess) {
logger.setContext(logger.context
.copyWith(userAddress: loginState.user.walletAddress));
context.read<ProfileNameBloc>().add(LoadProfileName());
}

if (loginState is PromptPassword) {
Expand Down
3 changes: 3 additions & 0 deletions lib/components/app_top_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:ardrive/pages/drive_detail/components/hover_widget.dart';
import 'package:ardrive/search/search_modal.dart';
import 'package:ardrive/search/search_text_field.dart';
import 'package:ardrive/sync/domain/cubit/sync_cubit.dart';
import 'package:ardrive/user/name/presentation/bloc/profile_name_bloc.dart';
import 'package:ardrive/utils/app_localizations_wrapper.dart';
import 'package:ardrive/utils/plausible_event_tracker/plausible_custom_event_properties.dart';
import 'package:ardrive/utils/plausible_event_tracker/plausible_event_tracker.dart';
Expand Down Expand Up @@ -124,6 +125,7 @@ class SyncButton extends StatelessWidget {
ArDriveDropdownItem(
onClick: () {
context.read<SyncCubit>().startSync(deepSync: false);
context.read<ProfileNameBloc>().add(RefreshProfileName());
PlausibleEventTracker.trackResync(type: ResyncType.resync);
},
content: ArDriveDropdownItemTile(
Expand All @@ -136,6 +138,7 @@ class SyncButton extends StatelessWidget {
ArDriveDropdownItem(
onClick: () {
context.read<SyncCubit>().startSync(deepSync: true);
context.read<ProfileNameBloc>().add(RefreshProfileName());
PlausibleEventTracker.trackResync(type: ResyncType.deepResync);
},
content: ArDriveDropdownItemTile(
Expand Down
50 changes: 41 additions & 9 deletions lib/components/profile_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:ardrive/turbo/topup/components/turbo_balance_widget.dart';
import 'package:ardrive/turbo/utils/utils.dart';
import 'package:ardrive/user/balance/user_balance_bloc.dart';
import 'package:ardrive/user/download_wallet/download_wallet_modal.dart';
import 'package:ardrive/user/name/presentation/bloc/profile_name_bloc.dart';
import 'package:ardrive/utils/app_localizations_wrapper.dart';
import 'package:ardrive/utils/open_url.dart';
import 'package:ardrive/utils/open_url_utils.dart';
Expand Down Expand Up @@ -548,15 +549,46 @@ class _ProfileCardState extends State<ProfileCard> {

Widget _buildProfileCardHeader(BuildContext context, String walletAddress) {
final typography = ArDriveTypographyNew.of(context);
return ArDriveButtonNew(
text: truncateString(walletAddress, offsetStart: 2, offsetEnd: 2),
typography: typography,
variant: ButtonVariant.outline,
maxWidth: 100,
onPressed: () {
setState(() {
_showProfileCard = !_showProfileCard;
});
return BlocBuilder<ProfileNameBloc, ProfileNameState>(
builder: (context, state) {
final primaryName = state is ProfileNameLoaded
? state.primaryName
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 this won't truncate primary names to max 20 and add ... ? Or is it handled elsewhere?

: truncateString(walletAddress, offsetStart: 2, offsetEnd: 2);
double maxWidth = 100;

if (state is ProfileNameLoaded) {
maxWidth = primaryName.length * 15;

if (maxWidth < 100) {
maxWidth = 100;
}

if (maxWidth > 200) {
maxWidth = 200;
}
}

String? tooltipMessage;

if (primaryName.length > 20) {
tooltipMessage = primaryName;
}

return ArDriveTooltip(
message: tooltipMessage ?? '',
child: ArDriveButtonNew(
text: primaryName,
typography: typography,
variant: ButtonVariant.outline,
maxWidth: maxWidth,
maxHeight: 40,
onPressed: () {
setState(() {
_showProfileCard = !_showProfileCard;
});
},
),
);
},
);
}
Expand Down
7 changes: 7 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import 'package:ardrive/theme/theme_switcher_state.dart';
import 'package:ardrive/turbo/services/payment_service.dart';
import 'package:ardrive/turbo/services/upload_service.dart';
import 'package:ardrive/turbo/turbo.dart';
import 'package:ardrive/user/name/presentation/bloc/profile_name_bloc.dart';
import 'package:ardrive/user/repositories/user_preferences_repository.dart';
import 'package:ardrive/user/repositories/user_repository.dart';
import 'package:ardrive/utils/app_flavors.dart';
Expand Down Expand Up @@ -356,6 +357,12 @@ class AppState extends State<App> {
),
),
BlocProvider<AppBannerBloc>(create: (context) => AppBannerBloc()),
BlocProvider(
create: (context) => ProfileNameBloc(
context.read<ARNSRepository>(),
context.read<ArDriveAuth>(),
),
),
];

List<SingleChildWidget> get repositoryProviders => [
Expand Down
2 changes: 1 addition & 1 deletion lib/shared/blocs/banner/app_banner_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ part 'app_banner_event.dart';
part 'app_banner_state.dart';

class AppBannerBloc extends Bloc<AppBannerEvent, AppBannerState> {
AppBannerBloc() : super(AppBannerVisible()) {
AppBannerBloc() : super(AppBannerHidden()) {
on<AppBannerEvent>((event, emit) {
if (event is AppBannerCloseEvent) {
emit(AppBannerHidden());
Expand Down
4 changes: 3 additions & 1 deletion lib/sync/domain/cubit/sync_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ class SyncCubit extends Cubit<SyncState> {
syncProgressController.add(_syncProgress);
}

if (profile is ProfileLoggedIn) _profileCubit.refreshBalance();
if (profile is ProfileLoggedIn) {
_profileCubit.refreshBalance();
}

logger.i('Transaction statuses updated');
} catch (err, stackTrace) {
Expand Down
62 changes: 62 additions & 0 deletions lib/user/name/presentation/bloc/profile_name_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'package:ardrive/arns/domain/arns_repository.dart';
import 'package:ardrive/authentication/ardrive_auth.dart';
import 'package:ardrive/utils/logger.dart';
import 'package:ario_sdk/ario_sdk.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

part 'profile_name_event.dart';
part 'profile_name_state.dart';

class ProfileNameBloc extends Bloc<ProfileNameEvent, ProfileNameState> {
final ARNSRepository _arnsRepository;
final ArDriveAuth _auth;

ProfileNameBloc(this._arnsRepository, this._auth)
: super(ProfileNameInitial(_auth.currentUser.walletAddress)) {
on<LoadProfileName>((event, emit) async {
await _loadProfileName(
walletAddress: _auth.currentUser.walletAddress,
refresh: false,
emit: emit,
);
});
on<RefreshProfileName>((event, emit) async {
await _loadProfileName(
walletAddress: _auth.currentUser.walletAddress,
refresh: true,
emit: emit,
);
});
}

Future<void> _loadProfileName({
required String walletAddress,
required bool refresh,
required Emitter<ProfileNameState> emit,
}) async {
try {
/// if we are not refreshing, we emit a loading state
if (!refresh) {
emit(ProfileNameLoading(walletAddress));
}

final primaryName =
await _arnsRepository.getPrimaryName(walletAddress, update: refresh);

emit(ProfileNameLoaded(primaryName, walletAddress));
} catch (e) {
if (e is PrimaryNameNotFoundException) {
logger.d('Primary name not found for address: $walletAddress');
} else {
logger.e('Error getting primary name.', e);
}

emit(
ProfileNameLoadedWithWalletAddress(
walletAddress,
),
);
}
}
}
12 changes: 12 additions & 0 deletions lib/user/name/presentation/bloc/profile_name_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
part of 'profile_name_bloc.dart';

sealed class ProfileNameEvent extends Equatable {
const ProfileNameEvent();

@override
List<Object> get props => [];
}

final class RefreshProfileName extends ProfileNameEvent {}

final class LoadProfileName extends ProfileNameEvent {}
44 changes: 44 additions & 0 deletions lib/user/name/presentation/bloc/profile_name_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
part of 'profile_name_bloc.dart';

sealed class ProfileNameState extends Equatable {
const ProfileNameState();

abstract final String walletAddress;

@override
List<Object> get props => [];
}

final class ProfileNameInitial extends ProfileNameState {
const ProfileNameInitial(this.walletAddress);

@override
final String walletAddress;
}

final class ProfileNameLoading extends ProfileNameState {
@override
final String walletAddress;

const ProfileNameLoading(this.walletAddress);
}

final class ProfileNameLoaded extends ProfileNameState {
final String primaryName;

const ProfileNameLoaded(this.primaryName, this.walletAddress);

@override
final String walletAddress;

@override
List<Object> get props => [primaryName, walletAddress];
}

// if fails to load primary name, show current wallet address
final class ProfileNameLoadedWithWalletAddress extends ProfileNameState {
@override
final String walletAddress;

const ProfileNameLoadedWithWalletAddress(this.walletAddress);
}
10 changes: 8 additions & 2 deletions packages/ardrive_ui/lib/src/components/button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,14 @@ class _ArDriveButtonNewState extends State<ArDriveButtonNew> {
final text = Text(widget.text,
textAlign: TextAlign.center,
style: widget.fontStyle ??
typography.paragraphLarge(
color: foregroundColor, fontWeight: ArFontWeight.semiBold));
typography
.paragraphLarge(
color: foregroundColor,
fontWeight: ArFontWeight.semiBold,
)
.copyWith(
overflow: TextOverflow.ellipsis,
));

final buttonH = widget.maxHeight ?? buttonDefaultHeight;

Expand Down
14 changes: 14 additions & 0 deletions packages/ario_sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This SDK facilitates interaction with the Ario network by providing Dart APIs fo
- [Creating an Instance](#creating-an-instance)
- [Fetching Gateways](#fetching-gateways)
- [Fetching IO Token Balance](#fetching-io-token-balance)
- [Fetching Primary Name](#fetching-primary-name)
- [Models](#models)
- [Platform Support](#platform-support)

Expand Down Expand Up @@ -72,6 +73,19 @@ Future<void> fetchIOTokens(String address) async {
}
```

### Fetching Primary Name

Fetch the primary name for a specific wallet address:

```dart
Future<void> fetchPrimaryName(String address) async {
final primaryName = await arioSDK.getPrimaryName(address);
print('Primary Name: $primaryName');
}
```

Throws a [PrimaryNameNotFoundException] if the primary name is not found.

## Models

The SDK include the Gateway model that represent the data structures used by the Ario network:
Expand Down
1 change: 1 addition & 0 deletions packages/ario_sdk/lib/ario_sdk.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
library ario_sdk;

export 'src/ario_sdk.dart';
export 'src/exceptions.dart';
export 'src/factory.dart';
export 'src/models/models.dart';
export 'src/utils/utils.dart';
5 changes: 5 additions & 0 deletions packages/ario_sdk/lib/src/ario_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ abstract class ArioSDK {
required String domain,
String undername = '@',
});

/// Get the primary name for the given address
///
/// Throws [PrimaryNameNotFoundException] if the primary name is not found
Future<String> getPrimaryName(String address);
}
4 changes: 4 additions & 0 deletions packages/ario_sdk/lib/src/exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ class GetIOTokensException extends ARIOException {
class GetGatewaysException extends ARIOException {
GetGatewaysException(super.message);
}

class PrimaryNameNotFoundException extends ARIOException {
PrimaryNameNotFoundException(super.message);
}
Loading
Loading