Isses bloc view

This commit is contained in:
Bazsalanszky 2022-05-22 10:53:13 +02:00
parent e437f99a79
commit 612371b4d9
Signed by: Bazsalanszky
GPG key ID: B40814F4EFE23F96
3 changed files with 136 additions and 9 deletions

View file

@ -23,7 +23,7 @@ class IssuesBloc extends Bloc<IssuesEvent, IssueState> {
if (state.hasReachedMax) return; if (state.hasReachedMax) return;
try { try {
if (state.status == IssueStatus.initial) { if (state.status == IssueStatus.initial) {
final issues = await giteaService.getRepoIssues(repoFullname: repoFullName,state: istate,page: 1, limit: 10); final issues = await giteaService.getRepoIssues(owner: repoFullName.split('/')[0],repo: repoFullName.split('/')[1],state: istate,page: 1, limit: 10);
return emit(state.copyWith( return emit(state.copyWith(
status: IssueStatus.success, status: IssueStatus.success,
issues: issues, issues: issues,
@ -32,7 +32,7 @@ class IssuesBloc extends Bloc<IssuesEvent, IssueState> {
error_message: null, error_message: null,
)); ));
} }
final issues = await giteaService.getRepoIssues(repoFullname: repoFullName,state: istate,page: state.loadedPages+1, limit: 10); final issues = await giteaService.getRepoIssues(owner: repoFullName.split('/')[0],repo: repoFullName.split('/')[1],state: istate,page: state.loadedPages+1, limit: 10);
final issueList = List.of(state.issues); final issueList = List.of(state.issues);
issues.forEach((element) { issues.forEach((element) {
if(issueList.where((selement) => selement.id == element.id).isEmpty) { if(issueList.where((selement) => selement.id == element.id).isEmpty) {

View file

@ -55,12 +55,12 @@ class GiteaService {
throw Exception('error fetching files'); throw Exception('error fetching files');
} }
Future<List<Issue>> getRepoIssues({required String repoFullname,required String state,int page = 1, int limit = 10}) async{ Future<List<Issue>> getRepoIssues({required String owner,required String repo,required String state,int page = 1, int limit = 10}) async{
if(state != "all" || state != "closed" || state != "open") { if(state != "all" && state != "closed" && state != "open") {
throw Exception("Wrong state provided!"); throw Exception("Wrong state provided: $state");
} }
var response = await http.get( var response = await http.get(
Uri.https(apiAccess.instance, "api/v1/user/repos/$repoFullname/issues", { Uri.https(apiAccess.instance, "api/v1/repos/$owner/$repo/issues", {
"token": apiAccess.token, "token": apiAccess.token,
"state": state, "state": state,
"page" : page.toString(), "page" : page.toString(),
@ -76,7 +76,7 @@ class GiteaService {
result.sort((a,b) => -1*a.updatedAt!.compareTo(b.updatedAt!)); result.sort((a,b) => -1*a.updatedAt!.compareTo(b.updatedAt!));
return result; return result;
} }
throw Exception('error fetching posts'); throw Exception('error fetching posts: ${response.statusCode}');
} }
Future<List<Repository>> getUserRepositories([int page = 1, int limit = 10]) async{ Future<List<Repository>> getUserRepositories([int page = 1, int limit = 10]) async{

View file

@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:gitea_client/cubit/file_list_load_bloc.dart'; import 'package:gitea_client/cubit/file_list_load_bloc.dart';
import 'package:gitea_client/cubit/issues_bloc.dart';
import 'package:gitea_client/model/issues.dart';
import 'package:gitea_client/model/repository.dart'; import 'package:gitea_client/model/repository.dart';
import 'package:gitea_client/model/user.dart'; import 'package:gitea_client/model/user.dart';
import 'package:gitea_client/service/gitea_service.dart'; import 'package:gitea_client/service/gitea_service.dart';
@ -308,8 +310,17 @@ class RepoIssues extends StatefulWidget {
} }
class _RepoIssues extends State<RepoIssues> { class _RepoIssues extends State<RepoIssues> {
final _scrollController = ScrollController();
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final media = MediaQuery.of(context).size;
return DefaultTabController( return DefaultTabController(
length: 2, length: 2,
child: DefaultTabController( child: DefaultTabController(
@ -334,10 +345,90 @@ class _RepoIssues extends State<RepoIssues> {
body: TabBarView( body: TabBarView(
children: <Widget>[ children: <Widget>[
Column( Column(
children: const [Text("Open Issues")], children: [
Center(
child: SizedBox(
width: (media.width > 600) ? media.width * 0.5 : media.width * 0.9,
height: media.height-kToolbarHeight-kBottomNavigationBarHeight-kTextTabBarHeight-50,
child: BlocProvider(
create: (context) {
return IssuesBloc(GiteaService(apiAccess: widget.user.apiAccess),widget.repo.fullName!,"open")
..add(IssuesFetched());
},
child: BlocBuilder<IssuesBloc, IssueState>(
builder: (context, state) {
switch (state.status) {
case IssueStatus.failure:
String error_message = state.error_message!;
return Center(child: Text('failed to fetch $error_message'));
case IssueStatus.success:
if (state.issues.isEmpty) {
return const Center(
child: Text('No issues found'));
}
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return IssuesListItem(
issue: state.issues[index],
onTap: () {},
);
},
itemCount: state.issues.length,
controller: _scrollController,
);
default:
return const Center(child: CircularProgressIndicator());
}
},
),
),
),
)
],
), ),
Column( Column(
children: const [Text("Closed Issues")], children: [
Center(
child: SizedBox(
width: (media.width > 600) ? media.width * 0.5 : media.width * 0.9,
height: media.height-kToolbarHeight-kBottomNavigationBarHeight-kTextTabBarHeight-50,
child: BlocProvider(
create: (context) {
return IssuesBloc(GiteaService(apiAccess: widget.user.apiAccess),widget.repo.fullName!,"closed")
..add(IssuesFetched());
},
child: BlocBuilder<IssuesBloc, IssueState>(
builder: (context, state) {
switch (state.status) {
case IssueStatus.failure:
String error_message = state.error_message!;
return Center(child: Text('failed to fetch $error_message'));
case IssueStatus.success:
if (state.issues.isEmpty) {
return const Center(
child: Text('No issues found'));
}
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return IssuesListItem(
issue: state.issues[index],
onTap: () {},
);
},
itemCount: state.issues.length,
controller: _scrollController,
);
default:
return const Center(child: CircularProgressIndicator());
}
},
),
),
),
)
],
) )
], ],
), ),
@ -345,6 +436,42 @@ class _RepoIssues extends State<RepoIssues> {
), ),
); );
} }
@override
void dispose() {
_scrollController
..removeListener(_onScroll)
..dispose();
super.dispose();
}
void _onScroll() {
if (_isBottom) context.read<IssuesBloc>().add(IssuesFetched());
}
bool get _isBottom {
if (!_scrollController.hasClients) return false;
final maxScroll = _scrollController.position.maxScrollExtent;
final currentScroll = _scrollController.offset;
return currentScroll >= (maxScroll * 0.9);
}
}
class IssuesListItem extends StatelessWidget {
final Issue issue;
final Function onTap;
const IssuesListItem({Key? key, required this.issue, required this.onTap})
: super(key: key);
@override
Widget build(BuildContext context) {
return ListTile(
leading: Text("#${issue.number!}"),
title: Text(issue.title!),
onTap: () => {onTap()},
);
}
} }
class RepoPullRequests extends StatefulWidget { class RepoPullRequests extends StatefulWidget {