Skip to content

Commit

Permalink
added initial search functionality, and ability to subscribe to commu…
Browse files Browse the repository at this point in the history
…nities
  • Loading branch information
hjiangsu committed Jun 14, 2023
1 parent 434c048 commit e8ab5d2
Show file tree
Hide file tree
Showing 18 changed files with 1,181 additions and 150 deletions.
3 changes: 3 additions & 0 deletions lib/account/pages/login_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ class _LoginPageState extends State<LoginPage> {
border: OutlineInputBorder(),
labelText: 'Username',
),
enableSuggestions: false,
),
const SizedBox(height: 12.0),
TextField(
controller: _passwordTextEditingController,
obscureText: !showPassword,
enableSuggestions: false,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: 'Password',
Expand All @@ -77,6 +79,7 @@ class _LoginPageState extends State<LoginPage> {
labelText: 'Instance',
hintText: 'lemmy.ml',
),
enableSuggestions: false,
),
const SizedBox(height: 32.0),
ElevatedButton(
Expand Down
216 changes: 115 additions & 101 deletions lib/community/bloc/community_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import 'dart:ui';

import 'package:bloc/bloc.dart';
import 'package:dio/dio.dart';
import 'package:equatable/equatable.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stream_transform/stream_transform.dart';

import 'package:lemmy/lemmy.dart';
import 'package:thunder/core/enums/media_type.dart';
import 'package:thunder/core/models/media.dart';
import 'package:thunder/core/models/media_extension.dart';
import 'package:thunder/core/models/pictr_media_extension.dart';
import 'package:thunder/core/models/post_view_media.dart';

import 'package:thunder/core/singletons/lemmy_client.dart';
import 'package:thunder/utils/links.dart';

part 'community_event.dart';
part 'community_state.dart';
Expand Down Expand Up @@ -65,7 +70,7 @@ class CommunityBloc extends Bloc<CommunityEvent, CommunityState> {
} on DioException catch (e) {
print(e);
if (e.type == DioExceptionType.receiveTimeout) {
emit(state.copyWith(status: CommunityStatus.networkFailure, errorMessage: 'Network timeout connecting to Lemmy instance'));
emit(state.copyWith(status: CommunityStatus.networkFailure, errorMessage: 'Error: Network timeout when attempting to vote'));
} else {
emit(state.copyWith(status: CommunityStatus.networkFailure, errorMessage: e.toString()));
}
Expand All @@ -76,113 +81,122 @@ class CommunityBloc extends Bloc<CommunityEvent, CommunityState> {
}

Future<void> _getCommunityPostsEvent(GetCommunityPostsEvent event, Emitter<CommunityState> emit) async {
try {
LemmyClient lemmyClient = LemmyClient.instance;
Lemmy lemmy = lemmyClient.lemmy;

SharedPreferences prefs = await SharedPreferences.getInstance();
String? jwt = prefs.getString('jwt');
int attemptCount = 0;

if (event.reset) {
emit(state.copyWith(status: CommunityStatus.loading));

GetPostsResponse getPostsResponse = await lemmy.getPosts(
GetPosts(
auth: jwt,
page: 1,
limit: 30,
sort: event.sortType ?? SortType.Active,
type_: event.listingType ?? ListingType.Local,
communityId: event.communityId,
),
);

List<PostViewMedia> posts = [];

getPostsResponse.posts.forEach((PostView postView) async {
List<Media> media = [];
String? url = postView.post.url;

if (url != null && PictrsMediaExtension.isPictrsURL(url)) {
media = await PictrsMediaExtension.getMediaInformation(url);
} else if (url != null) {
media.add(Media(originalUrl: url, mediaType: MediaType.link));
try {
while (attemptCount < 2) {
try {
LemmyClient lemmyClient = LemmyClient.instance;
Lemmy lemmy = lemmyClient.lemmy;

SharedPreferences prefs = await SharedPreferences.getInstance();
String? jwt = prefs.getString('jwt');

if (event.reset) {
emit(state.copyWith(status: CommunityStatus.loading));

GetPostsResponse getPostsResponse = await lemmy.getPosts(
GetPosts(
auth: jwt,
page: 1,
limit: 30,
sort: event.sortType ?? SortType.Hot,
type_: event.listingType ?? ListingType.Local,
communityId: event.communityId,
),
);

List<PostViewMedia> posts = await _parsePostViews(getPostsResponse.posts);

return emit(state.copyWith(
status: CommunityStatus.success,
postViews: posts,
page: 2,
listingType: event.listingType ?? ListingType.Local,
communityId: event.communityId,
));
} else {
GetPostsResponse getPostsResponse = await lemmy.getPosts(
GetPosts(
auth: jwt,
page: state.page,
limit: 30,
sort: event.sortType ?? SortType.Hot,
type_: state.listingType,
communityId: state.communityId,
),
);

List<PostViewMedia> posts = await _parsePostViews(getPostsResponse.posts);

List<PostViewMedia> postViews = List.from(state.postViews ?? []);
postViews.addAll(posts);

return emit(
state.copyWith(
status: CommunityStatus.success,
postViews: postViews,
page: state.page + 1,
),
);
}

posts.add(PostViewMedia(
post: postView.post,
community: postView.community,
counts: postView.counts,
creator: postView.creator,
creatorBannedFromCommunity: postView.creatorBannedFromCommunity,
creatorBlocked: postView.creatorBlocked,
saved: postView.saved,
subscribed: postView.subscribed,
read: postView.read,
unreadComments: postView.unreadComments,
media: media,
));
});

return emit(state.copyWith(
status: CommunityStatus.success,
postViews: posts,
page: 2,
listingType: event.listingType ?? ListingType.Local,
communityId: event.communityId,
));
}

GetPostsResponse getPostsResponse = await lemmy.getPosts(
GetPosts(
auth: jwt,
page: state.page,
limit: 30,
sort: event.sortType ?? SortType.Active,
type_: state.listingType,
communityId: state.communityId,
),
);

List<PostViewMedia> posts = [];

getPostsResponse.posts.forEach((PostView postView) async {
List<Media> media = [];
String? url = postView.post.url;

if (url != null && PictrsMediaExtension.isPictrsURL(url)) {
media = await PictrsMediaExtension.getMediaInformation(url);
} catch (e) {
print('re-attempting: $attemptCount');
attemptCount += 1;
}

posts.add(PostViewMedia(
post: postView.post,
community: postView.community,
counts: postView.counts,
creator: postView.creator,
creatorBannedFromCommunity: postView.creatorBannedFromCommunity,
creatorBlocked: postView.creatorBlocked,
saved: postView.saved,
subscribed: postView.subscribed,
read: postView.read,
unreadComments: postView.unreadComments,
media: media,
));
});

List<PostViewMedia> postViews = List.from(state.postViews ?? []);
postViews.addAll(posts);

emit(
state.copyWith(
status: CommunityStatus.success,
postViews: postViews,
page: state.page + 1,
),
);
}
} on DioException catch (e) {
emit(state.copyWith(status: CommunityStatus.failure, errorMessage: e.message));
} catch (e) {
emit(state.copyWith(status: CommunityStatus.failure, errorMessage: e.toString()));
}
}

Future<List<PostViewMedia>> _parsePostViews(List<PostView> postViews) async {
List<PostViewMedia> posts = [];

postViews.forEach((PostView postView) async {
List<Media> media = [];
String? url = postView.post.url;

if (url != null && PictrsMediaExtension.isPictrsURL(url)) {
media = await PictrsMediaExtension.getMediaInformation(url);
} else if (url != null) {
// For external links, attempt to fetch any media associated with it (image, title)
LinkInfo linkInfo = await getLinkInfo(url);

if (linkInfo.imageURL != null && linkInfo.imageURL!.isNotEmpty) {
try {
ImageInfo imageInfo = await MediaExtension.getImageInfo(Image.network(linkInfo.imageURL!));
int mediaHeight = imageInfo.image.height;
int mediaWidth = imageInfo.image.width;
Size size = MediaExtension.getScaledMediaSize(width: mediaWidth, height: mediaHeight);

media.add(Media(mediaUrl: linkInfo.imageURL!, mediaType: MediaType.link, originalUrl: url, height: size.height, width: size.width));
} catch (e) {
// Default back to a link
media.add(Media(mediaType: MediaType.link, originalUrl: url));
}
} else {
media.add(Media(mediaType: MediaType.link, originalUrl: url));
}
}

posts.add(PostViewMedia(
post: postView.post,
community: postView.community,
counts: postView.counts,
creator: postView.creator,
creatorBannedFromCommunity: postView.creatorBannedFromCommunity,
creatorBlocked: postView.creatorBlocked,
saved: postView.saved,
subscribed: postView.subscribed,
read: postView.read,
unreadComments: postView.unreadComments,
media: media,
));
});

return posts;
}
}
16 changes: 13 additions & 3 deletions lib/community/pages/community_page.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';

import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:lemmy/lemmy.dart';
import 'package:thunder/account/bloc/account_bloc.dart';

import 'package:thunder/account/bloc/account_bloc.dart';
import 'package:thunder/community/bloc/community_bloc.dart';
import 'package:thunder/community/pages/create_post_page.dart';
import 'package:thunder/community/widgets/post_card_list.dart';
import 'package:thunder/core/auth/bloc/auth_bloc.dart';

Expand Down Expand Up @@ -71,8 +73,8 @@ class CommunityPage extends StatefulWidget {
}

class _CommunityPageState extends State<CommunityPage> {
SortType? sortType = SortType.Active;
IconData sortTypeIcon = Icons.rocket_launch_rounded;
SortType? sortType = SortType.Hot;
IconData sortTypeIcon = Icons.local_fire_department_rounded;

final ScrollController _scrollController = ScrollController();

Expand Down Expand Up @@ -243,6 +245,14 @@ class _CommunityPageState extends State<CommunityPage> {
),
),
),
floatingActionButton: (state.communityId != null)
? FloatingActionButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => CreatePostPage(communityId: state.communityId!)));
},
child: const Icon(Icons.add),
)
: null,
body: SafeArea(child: _getBody(context, state)),
);
},
Expand Down
Loading

0 comments on commit e8ab5d2

Please sign in to comment.