From 532c2e91f33a8381ba87c8ee6b00b77d9f9fb1f6 Mon Sep 17 00:00:00 2001 From: nain93 Date: Tue, 1 Aug 2023 20:11:27 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=ED=95=B4=EC=8B=9C=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=EB=B7=B0=20api=20=ED=83=80=EC=9E=85=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/providers/hashtag_provider.dart | 1 - lib/providers/hashtag_view_provider.dart | 17 ++++++- lib/repositories/hashtag_repository.dart | 24 +++++++-- lib/screens/home/home.dart | 33 ++++++++----- .../home/tab_view/hashtag_tab_view.dart | 49 ++++++++++++------- .../{hashtag_card.dart => content_card.dart} | 24 ++++----- lib/utils/utils.dart | 5 +- .../moa_widgets/dynamic_grid_list.dart | 4 +- 8 files changed, 109 insertions(+), 48 deletions(-) rename lib/screens/home/widgets/{hashtag_card.dart => content_card.dart} (77%) diff --git a/lib/providers/hashtag_provider.dart b/lib/providers/hashtag_provider.dart index 9e16d23..c566305 100644 --- a/lib/providers/hashtag_provider.dart +++ b/lib/providers/hashtag_provider.dart @@ -26,7 +26,6 @@ class Hashtag extends _$Hashtag { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { - // await ItemRepository.instance.addItem(item: item); var data = await fetchItem(); return [hashtag, ...data]; }); diff --git a/lib/providers/hashtag_view_provider.dart b/lib/providers/hashtag_view_provider.dart index e567d6d..57f9bc6 100644 --- a/lib/providers/hashtag_view_provider.dart +++ b/lib/providers/hashtag_view_provider.dart @@ -33,7 +33,7 @@ class HashtagView extends _$HashtagView { timer?.cancel(); }); - var data = HashtagRepository.instance.getHashtagView(); + var data = await HashtagRepository.instance.getHashtagView(); return data; } @@ -41,4 +41,19 @@ class HashtagView extends _$HashtagView { Future<(List, int)> build() async { return fetchItem(); } + + Future<(List, int)> loadMoreData({ + int? page, + int? size, + }) async { + state = const AsyncValue.loading(); + + state = await AsyncValue.guard(() async { + var data = await HashtagRepository.instance + .getHashtagView(page: page, size: size); + return data; + }); + + return state.value!; + } } diff --git a/lib/repositories/hashtag_repository.dart b/lib/repositories/hashtag_repository.dart index edb0a02..14f6399 100644 --- a/lib/repositories/hashtag_repository.dart +++ b/lib/repositories/hashtag_repository.dart @@ -5,7 +5,10 @@ import 'package:moa_app/repositories/token_repository.dart'; import 'package:moa_app/utils/api.dart'; abstract class IHashtagRepository { - Future<(List, int)> getHashtagView(); + Future<(List, int)> getHashtagView({ + int? page, + int? size, + }); Future> getHashtagList(); } @@ -14,11 +17,14 @@ class HashtagRepository implements IHashtagRepository { static HashtagRepository instance = const HashtagRepository._(); @override - Future<(List, int)> getHashtagView() async { + Future<(List, int)> getHashtagView({ + int? page = 0, + int? size = 10, + }) async { var token = await TokenRepository.instance.getToken(); var res = await dio.get( - '/api/v1/hashtag/view', + '/api/v1/hashtag/view?page=$page&size=$size', options: Options( headers: { 'Authorization': 'Bearer $token', @@ -26,9 +32,19 @@ class HashtagRepository implements IHashtagRepository { ), ); + /// 백엔드 ContentModel 타입이 통일되지 않았으므로 임시로 타입 변환해서 넣어줌 return ( res.data['data'] - .map((e) => ContentModel.fromJson(e)) + .map( + (e) => ContentModel.fromJson({ + 'contentId': e['contentId'], + 'contentImageUrl': e['imageUrl'], + 'contentUrl': e['contentUrl'] ?? '', + 'contentMemo': e['memo'], + 'contentName': e['name'], + 'contentHashTag': e['hashTags'], + }), + ) .toList() as List, res.data['count'] as int ); diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart index 731c5c9..fbf8460 100644 --- a/lib/screens/home/home.dart +++ b/lib/screens/home/home.dart @@ -11,6 +11,7 @@ import 'package:moa_app/models/folder_model.dart'; import 'package:moa_app/models/user_model.dart'; import 'package:moa_app/providers/button_click_provider.dart'; import 'package:moa_app/providers/folder_view_provider.dart'; +import 'package:moa_app/providers/hashtag_view_provider.dart'; import 'package:moa_app/repositories/hashtag_repository.dart'; import 'package:moa_app/repositories/user_repository.dart'; import 'package:moa_app/screens/home/tab_view/folder_tab_view.dart'; @@ -24,7 +25,7 @@ class Home extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { var folderAsync = ref.watch(folderViewProvider.notifier); - // var hashtagAsync = ref.watch(hashtagViewProvider.notifier); + var hashtagAsync = ref.watch(hashtagViewProvider.notifier); var isClick = ref.watch(buttonClickStateProvider); var tabIdx = useState(0); TabController tabController = useTabController(initialLength: 2); @@ -117,7 +118,7 @@ class Home extends HookConsumerWidget { contentCount: contentCount, ), TabViewItem( - futureList: Future.value([]), + futureList: hashtagAsync, uniqueKey: const Key('hashtagTab'), folderCount: folderCount, contentCount: contentCount, @@ -318,18 +319,22 @@ class FolderSource extends LoadingMoreBase { } class HashtagSource extends LoadingMoreBase { - HashtagSource({required this.contentCount}); + HashtagSource({required this.contentCount, required this.futureList}); final ValueNotifier contentCount; + final HashtagView futureList; int pageIndex = 1; + int size = 10; bool _hasMore = true; bool forceRefresh = false; + + var contentList = []; @override - bool get hasMore => (_hasMore && length < 30) || forceRefresh; + bool get hasMore => _hasMore || forceRefresh; @override Future refresh([bool notifyStateChanged = false]) async { _hasMore = true; - pageIndex = 1; + pageIndex = 0; //force to refresh list when you don't want clear list before request //for the case, if your list already has 20 items. forceRefresh = !notifyStateChanged; @@ -343,14 +348,19 @@ class HashtagSource extends LoadingMoreBase { bool isSuccess = false; try { - var (list, count) = await HashtagRepository.instance.getHashtagView(); - contentCount.value = count; - for (ContentModel content in list) { + var (list, _) = await HashtagRepository.instance + .getHashtagView(page: pageIndex, size: 10); + if (contentList.length < 10) { + var (_, count) = await futureList.future; + contentCount.value = count; + } + contentList.addAll(list); + for (ContentModel content in contentList) { if (!contains(content) && _hasMore) { add(content); } } - _hasMore = false; + _hasMore = list.length >= 10; pageIndex++; isSuccess = true; } catch (e) { @@ -371,7 +381,7 @@ class TabViewItem extends StatefulWidget { required this.contentCount, }); final Key uniqueKey; - final Future> futureList; + final dynamic futureList; final ValueNotifier folderCount; final ValueNotifier contentCount; @@ -382,10 +392,11 @@ class TabViewItem extends StatefulWidget { class TabViewItemState extends State with AutomaticKeepAliveClientMixin { late final FolderSource folderSource = FolderSource( - futureList: widget.futureList, + futureList: widget.futureList as Future>, folderCount: widget.folderCount, ); late final HashtagSource hashtagSource = HashtagSource( + futureList: widget.futureList as HashtagView, contentCount: widget.contentCount, ); diff --git a/lib/screens/home/tab_view/hashtag_tab_view.dart b/lib/screens/home/tab_view/hashtag_tab_view.dart index 8b19811..84cd817 100644 --- a/lib/screens/home/tab_view/hashtag_tab_view.dart +++ b/lib/screens/home/tab_view/hashtag_tab_view.dart @@ -6,10 +6,12 @@ import 'package:loading_more_list/loading_more_list.dart'; import 'package:moa_app/constants/app_constants.dart'; import 'package:moa_app/constants/file_constants.dart'; import 'package:moa_app/models/content_model.dart'; +import 'package:moa_app/screens/home/content_view.dart'; import 'package:moa_app/screens/home/home.dart'; -import 'package:moa_app/screens/home/widgets/hashtag_card.dart'; +import 'package:moa_app/screens/home/widgets/content_card.dart'; import 'package:moa_app/screens/home/widgets/type_header.dart'; import 'package:moa_app/utils/router_provider.dart'; +import 'package:moa_app/utils/utils.dart'; import 'package:moa_app/widgets/button.dart'; import 'package:moa_app/widgets/edit_text.dart'; import 'package:moa_app/widgets/loading_indicator.dart'; @@ -32,6 +34,7 @@ class HashtagTabView extends HookWidget { void goContentView(String contentId) { context.go( '${GoRoutes.content.fullPath}/$contentId', + extra: ContentView(id: contentId, folderName: 'folderName'), ); } @@ -74,15 +77,18 @@ class HashtagTabView extends HookWidget { uniqueKey: uniqueKey, child: RefreshIndicator( onRefresh: () { - // return source.refresh(true); - return Future.delayed( - const Duration(seconds: 2), - () { - source.refresh(false); - }, - ); + return source.refresh(true); }, child: LoadingMoreList( + onScrollNotification: (notification) { + if (notification is ScrollEndNotification) { + if (notification.metrics.pixels == + notification.metrics.maxScrollExtent) { + source.loadMore(); + } + } + return false; + }, ListConfig( addRepaintBoundaries: true, padding: const EdgeInsets.only( @@ -94,7 +100,6 @@ class HashtagTabView extends HookWidget { extendedListDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount( crossAxisCount: width > Breakpoints.md ? 3 : 2, - // crossAxisCount: 2, mainAxisSpacing: 20.0, crossAxisSpacing: 12.0, ), @@ -107,14 +112,24 @@ class HashtagTabView extends HookWidget { return const SizedBox(); }, itemBuilder: (c, item, index) { - // todo image width, height 계선해서 aspectRatio 주기 - return AspectRatio( - aspectRatio: 0.7, - child: ContentCard( - onPressContent: () => goContentView('5'), - content: item, - onPressHashtag: (tag) => goHashtagDetailView(tag), - ), + return FutureBuilder( + future: getImageSize(imageURL: item.contentImageUrl), + builder: (context, snapshot) { + var rate = snapshot.data?.toDouble() ?? 1.4; + + return AspectRatio( + aspectRatio: rate == 1.9 + ? 0.6 + : rate == 1.2 + ? 0.95 + : 0.7, + child: ContentCard( + onPressContent: () => goContentView(item.contentId), + content: item, + onPressHashtag: (tag) => goHashtagDetailView(tag), + ), + ); + }, ); }, ), diff --git a/lib/screens/home/widgets/hashtag_card.dart b/lib/screens/home/widgets/content_card.dart similarity index 77% rename from lib/screens/home/widgets/hashtag_card.dart rename to lib/screens/home/widgets/content_card.dart index e3ec048..85f1ea4 100644 --- a/lib/screens/home/widgets/hashtag_card.dart +++ b/lib/screens/home/widgets/content_card.dart @@ -30,17 +30,19 @@ class ContentCard extends HookWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: Container( - width: double.infinity, - decoration: BoxDecoration( - border: Border.all(color: AppColors.moaOpacity30), - borderRadius: BorderRadius.circular(10), - image: DecorationImage( - image: NetworkImage(content.contentImageUrl), - fit: BoxFit.contain, - ), - ), - ), + child: content.contentImageUrl == '' + ? const Text('이미지 없을 경우 모아 이미지로 대체') + : Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border.all(color: AppColors.moaOpacity30), + borderRadius: BorderRadius.circular(10), + image: DecorationImage( + image: NetworkImage(content.contentImageUrl), + fit: BoxFit.contain, + ), + ), + ), ), const SizedBox(height: 10), Text( diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index de06f31..0eb085d 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -12,6 +12,9 @@ Future xFileToBase64(XFile xFile) async { } Future getImageSize({required String imageURL}) async { + if (imageURL == '') { + return 1.4; + } Image image = Image.network(imageURL); Completer completer = Completer(); image.image.resolve(const ImageConfiguration()).addListener( @@ -24,7 +27,7 @@ Future getImageSize({required String imageURL}) async { ? 1.9 : imageRate < 1.2 ? 1.2 - : imageRate; + : 1.4; } bool isStringEncoded(String value) { diff --git a/lib/widgets/moa_widgets/dynamic_grid_list.dart b/lib/widgets/moa_widgets/dynamic_grid_list.dart index e91cd89..5b78518 100644 --- a/lib/widgets/moa_widgets/dynamic_grid_list.dart +++ b/lib/widgets/moa_widgets/dynamic_grid_list.dart @@ -5,7 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:moa_app/constants/app_constants.dart'; import 'package:moa_app/models/content_model.dart'; import 'package:moa_app/screens/home/content_view.dart'; -import 'package:moa_app/screens/home/widgets/hashtag_card.dart'; +import 'package:moa_app/screens/home/widgets/content_card.dart'; import 'package:moa_app/utils/router_provider.dart'; import 'package:moa_app/utils/utils.dart'; @@ -35,7 +35,7 @@ class DynamicGridList extends HookWidget { child: SingleChildScrollView( padding: const EdgeInsets.only(bottom: kBottomNavigationBarHeight), child: StaggeredGrid.count( - axisDirection: AxisDirection.down, // <----- Add this line + axisDirection: AxisDirection.down, crossAxisCount: width > Breakpoints.md ? 3 : 2, crossAxisSpacing: 10, mainAxisSpacing: 20, From 3b122c1812d38ff59dcd64e1802a666f366da77e Mon Sep 17 00:00:00 2001 From: nain93 Date: Tue, 1 Aug 2023 20:35:46 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=ED=95=B4=EC=8B=9C=ED=83=9C=EA=B7=B8?= =?UTF-8?q?=EB=B7=B0=20loadMore=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/home/home.dart | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart index fbf8460..74455d4 100644 --- a/lib/screens/home/home.dart +++ b/lib/screens/home/home.dart @@ -348,19 +348,25 @@ class HashtagSource extends LoadingMoreBase { bool isSuccess = false; try { - var (list, _) = await HashtagRepository.instance - .getHashtagView(page: pageIndex, size: 10); - if (contentList.length < 10) { - var (_, count) = await futureList.future; + if (pageIndex == 0) { + var (initialList, count) = await futureList.future; + // 최초렌더시 컨텐츠 전체 개수 가져오기 contentCount.value = count; + + contentList.addAll(initialList); + } else { + var (list, _) = await HashtagRepository.instance + .getHashtagView(page: pageIndex, size: size); + contentList.addAll(list); + _hasMore = list.length >= 10; } - contentList.addAll(list); + for (ContentModel content in contentList) { if (!contains(content) && _hasMore) { add(content); } } - _hasMore = list.length >= 10; + pageIndex++; isSuccess = true; } catch (e) {