diff --git a/packages/app/lib/src/features/home/application/avatar_service.dart b/packages/app/lib/src/features/home/application/avatar_service.dart new file mode 100644 index 00000000..ba8c05b5 --- /dev/null +++ b/packages/app/lib/src/features/home/application/avatar_service.dart @@ -0,0 +1,15 @@ +import 'dart:typed_data'; + +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../data/avatar_repository.dart'; + +part 'avatar_service.g.dart'; + +/// Get the user's avatar. +@riverpod +FutureOr avatarService(AvatarServiceRef ref, [String? name]) { + final avatarRepo = ref.watch(avatarProvider); + + return avatarRepo.getAvatar(name: name); +} diff --git a/packages/app/lib/src/features/home/application/location_service.dart b/packages/app/lib/src/features/home/application/location_service.dart index d66c2afc..0a307d99 100644 --- a/packages/app/lib/src/features/home/application/location_service.dart +++ b/packages/app/lib/src/features/home/application/location_service.dart @@ -10,5 +10,9 @@ part 'location_service.g.dart'; FutureOr locationService(LocationServiceRef ref) { final locationRepo = ref.watch(locationRepositoryProvider); - return locationRepo.determinePosition(); + try { + return locationRepo.determinePosition(); + } catch (e) { + throw 'Hmmmmm $e'; + } } diff --git a/packages/app/lib/src/features/home/data/avatar_repository.dart b/packages/app/lib/src/features/home/data/avatar_repository.dart new file mode 100644 index 00000000..bddc0f37 --- /dev/null +++ b/packages/app/lib/src/features/home/data/avatar_repository.dart @@ -0,0 +1,37 @@ +/// This library contains the authentication feature's avatar data fetchers. +library; + +import 'dart:typed_data'; + +import 'package:appwrite/appwrite.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../../utils/api.dart'; + +part 'avatar_repository.g.dart'; + +/// A repository for getting avatars. +abstract interface class AvatarRepository { + /// Get the avatar for the current user. + Future getAvatar({String? name}); +} + +/// The default implementation of [AvatarRepository]. +base class _AppwriteAvatarRepository implements AvatarRepository { + /// Create a new instance of [_AppwriteAvatarRepository]. + const _AppwriteAvatarRepository(Avatars avatars) : _avatars = avatars; + + final Avatars _avatars; + + @override + Future getAvatar({String? name}) => + _avatars.getInitials(name: name); +} + +/// Get the current user's avatar. +@Riverpod(keepAlive: true) +AvatarRepository avatar(AvatarRef ref) { + final avatars = ref.watch(avatarsProvider); + + return _AppwriteAvatarRepository(avatars); +} diff --git a/packages/app/lib/src/features/home/presentation/home/feed.dart b/packages/app/lib/src/features/home/presentation/home/feed.dart index 20f60a3e..3cd8440b 100644 --- a/packages/app/lib/src/features/home/presentation/home/feed.dart +++ b/packages/app/lib/src/features/home/presentation/home/feed.dart @@ -27,45 +27,50 @@ class Feed extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // Maybe change to scaffold with floating action button and list view as child - return ListView.builder( - shrinkWrap: true, - prototypeItem: Post( - post: PostEntity( - headline: '', - description: '', - lat: 0, - lng: 0, - timestamp: DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true), - author: '', - authorName: '', - ), - ), - itemBuilder: (context, index) { - final response = ref.watch(singlePostProvider(feed, index)); + return Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 600), + child: ListView.builder( + shrinkWrap: true, + prototypeItem: Post( + post: PostEntity( + headline: '', + description: '', + lat: 0, + lng: 0, + timestamp: DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true), + author: '', + authorName: '', + ), + ), + itemBuilder: (context, index) { + final response = ref.watch(singlePostProvider(feed, index)); - return switch (response) { - AsyncData(:final value) when value != null => Post(post: value), + return switch (response) { + AsyncData(:final value) when value != null => Post(post: value), - // If we run out of items, return null. - AsyncData() => null, + // If we run out of items, return null. + AsyncData() => null, - // If there's an error, display it as another post. - AsyncError(:final error) => Post( - post: PostEntity( - headline: 'Error', - description: error.toString(), - author: '', - authorName: '', - lat: 0, - lng: 0, - timestamp: DateTime.timestamp(), - ), - ), + // If there's an error, display it as another post. + AsyncError(:final error) => Post( + post: PostEntity( + headline: 'Error', + description: error.toString(), + author: '', + authorName: '', + lat: 0, + lng: 0, + timestamp: DateTime.timestamp(), + ), + ), - // If we're loading, display a loading indicator. - _ => null, - }; - }, + // If we're loading, display a loading indicator. + _ => null, + }; + }, + ), + ), ); } diff --git a/packages/app/lib/src/features/home/presentation/home/post.dart b/packages/app/lib/src/features/home/presentation/home/post.dart index cec4ccd8..244eca13 100644 --- a/packages/app/lib/src/features/home/presentation/home/post.dart +++ b/packages/app/lib/src/features/home/presentation/home/post.dart @@ -1,11 +1,10 @@ // ignore_for_file: prefer_expression_function_bodies -import 'package:appwrite/appwrite.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import '../../../../utils/api.dart'; +import '../../application/avatar_service.dart'; import '../../domain/post_entity.dart'; class Post extends StatelessWidget { @@ -20,19 +19,17 @@ class Post extends StatelessWidget { @override Widget build(BuildContext context) { - // Change back to stateless return Card( + margin: const EdgeInsets.all(4), child: Container( - width: MediaQuery.sizeOf(context).width, - height: 150, // TODO(MattsAttack): Scale based on required height. - margin: const EdgeInsets.all(4), - padding: const EdgeInsets.all(4), + constraints: const BoxConstraints(minHeight: 225), + padding: const EdgeInsets.all(16), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ // Have sections of post in here. Poster info and post content - _PosterInfo( - post: post, - ), + _PosterInfo(post: post), const Divider(color: Colors.white), //TODObase on theme _PostBody(post: post), ], @@ -144,25 +141,15 @@ class _PostAvatar extends ConsumerWidget { final PostEntity post; @override Widget build(BuildContext context, WidgetRef ref) { - final avatarService = ref.watch(avatarsProvider); - return FutureBuilder( - future: getAvatar(avatarService), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const CircularProgressIndicator(); - } else if (snapshot.hasError) { - return const Text('Error loading avatar'); - } else { - return CircleAvatar( - backgroundImage: snapshot.data, - ); - } - }, - ); - } + final avatar = ref.watch(avatarServiceProvider(post.authorName)); - Future getAvatar(Avatars avatarService) async { - return MemoryImage(await avatarService.getInitials(name: post.authorName)); + return switch (avatar) { + AsyncData(:final value) => CircleAvatar( + backgroundImage: MemoryImage(value), + ), + AsyncError() => const Text('Error loading avatar'), + _ => const CircularProgressIndicator() + }; } @override @@ -184,26 +171,21 @@ class _PostBody extends StatelessWidget { @override Widget build(BuildContext context) { - return Expanded( - child: Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - post.headline, - textAlign: TextAlign.left, - style: const TextStyle(fontSize: 24), - ), // Need text styling - const Padding(padding: EdgeInsets.all(4)), - Text( - post.description, - textAlign: TextAlign.left, - ), // Get user name instead of id - ], - ), - ], - ), + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + post.headline, + textAlign: TextAlign.left, + style: const TextStyle(fontSize: 24), + ), // Need text styling + const Padding(padding: EdgeInsets.all(4)), + Text( + post.description, + textAlign: TextAlign.left, + ), // Get user name instead of id + ], ); } diff --git a/packages/app/macos/Podfile.lock b/packages/app/macos/Podfile.lock index b8006390..27178f1a 100644 --- a/packages/app/macos/Podfile.lock +++ b/packages/app/macos/Podfile.lock @@ -61,7 +61,7 @@ SPEC CHECKSUMS: flutter_web_auth_2: 2e1dc2d2139973e4723c5286ce247dd590390d70 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 geolocator_apple: 72a78ae3f3e4ec0db62117bd93e34523f5011d58 - package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c + package_info_plus: f5790acc797bf17c3e959e9d6cf162cc68ff7523 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404