Bloc for Issues
All checks were successful
ci/woodpecker/push/flutterBuild Pipeline was successful

This commit is contained in:
Bazsalanszky 2022-05-22 10:28:34 +02:00
parent c272d8c888
commit e437f99a79
Signed by: Bazsalanszky
GPG key ID: B40814F4EFE23F96
7 changed files with 486 additions and 11 deletions

View file

@ -0,0 +1,55 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:gitea_client/model/issues.dart';
import 'package:stream_transform/stream_transform.dart';
import '../service/gitea_service.dart';
part 'issues_event.dart';
part 'issues_state.dart';
class IssuesBloc extends Bloc<IssuesEvent, IssueState> {
final GiteaService giteaService;
final String istate;
final String repoFullName;
IssuesBloc(this.giteaService,this.repoFullName,this.istate) : super(const IssueState()) {
on<IssuesFetched>(_onIssuesFetched,
transformer: (events, mapper) => events.switchMap(mapper),);
}
Future<void> _onIssuesFetched(IssuesFetched event, Emitter<IssueState> emit) async {
if (state.hasReachedMax) return;
try {
if (state.status == IssueStatus.initial) {
final issues = await giteaService.getRepoIssues(repoFullname: repoFullName,state: istate,page: 1, limit: 10);
return emit(state.copyWith(
status: IssueStatus.success,
issues: issues,
loadedPages: 1,
hasReachedMax: false,
error_message: null,
));
}
final issues = await giteaService.getRepoIssues(repoFullname: repoFullName,state: istate,page: state.loadedPages+1, limit: 10);
final issueList = List.of(state.issues);
issues.forEach((element) {
if(issueList.where((selement) => selement.id == element.id).isEmpty) {
issueList.add(element);
}
});
emit(issues.isEmpty
? state.copyWith(hasReachedMax: true)
: state.copyWith(
status: IssueStatus.success,
issues: issueList,
loadedPages: state.loadedPages+1,
hasReachedMax: false,
error_message: null,
));
} on Exception catch (e) {
emit(state.copyWith(status: IssueStatus.failure,error_message: e.toString()));
}
}
}

View file

@ -0,0 +1,9 @@
part of 'issues_bloc.dart';
abstract class IssuesEvent extends Equatable {
const IssuesEvent();
@override
List<Object?> get props => [];
}
class IssuesFetched extends IssuesEvent {}

View file

@ -0,0 +1,39 @@
part of 'issues_bloc.dart';
enum IssueStatus { initial, success, failure }
class IssueState extends Equatable {
const IssueState({
this.status = IssueStatus.initial,
this.issues = const <Issue>[],
this.loadedPages = 0,
this.hasReachedMax = false,
this.error_message = null
});
final IssueStatus status;
final List<Issue> issues;
final int loadedPages;
final bool hasReachedMax;
final String? error_message;
IssueState copyWith({
IssueStatus? status,
List<Issue>? issues,
int? loadedPages,
bool? hasReachedMax,
String? error_message,
}) {
return IssueState(
status: status ?? this.status,
issues: issues ?? this.issues,
loadedPages: loadedPages ?? this.loadedPages,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
error_message: error_message ?? this.error_message,
);
}
@override
List<Object> get props => [status, issues, hasReachedMax];
}

339
lib/model/issues.dart Normal file
View file

@ -0,0 +1,339 @@
import 'package:gitea_client/model/user.dart';
class Issue {
int? id;
String? url;
String? htmlUrl;
int? number;
_User? user;
String? originalAuthor;
int? originalAuthorId;
String? title;
String? body;
String? ref;
List<Labels>? labels;
Milestone? milestone;
_User? assignee;
List<_User>? assignees;
String? state;
bool? isLocked;
int? comments;
String? createdAt;
String? updatedAt;
String? closedAt;
String? dueDate;
String? pullRequest;
_Repository? repository;
Issue(
{this.id,
this.url,
this.htmlUrl,
this.number,
this.user,
this.originalAuthor,
this.originalAuthorId,
this.title,
this.body,
this.ref,
this.labels,
this.milestone,
this.assignee,
this.assignees,
this.state,
this.isLocked,
this.comments,
this.createdAt,
this.updatedAt,
this.closedAt,
this.dueDate,
this.pullRequest,
this.repository});
Issue.fromJson(Map<String, dynamic> json) {
id = json['id'];
url = json['url'];
htmlUrl = json['html_url'];
number = json['number'];
user = json['user'] != null ? _User.fromJson(json['user']) : null;
originalAuthor = json['original_author'];
originalAuthorId = json['original_author_id'];
title = json['title'];
body = json['body'];
ref = json['ref'];
if (json['labels'] != null) {
labels = <Labels>[];
json['labels'].forEach((v) {
labels!.add(Labels.fromJson(v));
});
}
milestone = json['milestone'] != null
? Milestone.fromJson(json['milestone'])
: null;
assignee =
json['assignee'] != null ? _User.fromJson(json['assignee']) : null;
if (json['assignees'] != null) {
assignees = <_User>[];
json['assignees'].forEach((v) {
assignees!.add(_User.fromJson(v));
});
}
state = json['state'];
isLocked = json['is_locked'];
comments = json['comments'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
closedAt = json['closed_at'];
dueDate = json['due_date'];
pullRequest = json['pull_request'];
repository = json['repository'] != null
? _Repository.fromJson(json['repository'])
: null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['url'] = url;
data['html_url'] = htmlUrl;
data['number'] = number;
if (user != null) {
data['user'] = user!.toJson();
}
data['original_author'] = originalAuthor;
data['original_author_id'] = originalAuthorId;
data['title'] = title;
data['body'] = body;
data['ref'] = ref;
if (labels != null) {
data['labels'] = labels!.map((v) => v.toJson()).toList();
}
if (milestone != null) {
data['milestone'] = milestone!.toJson();
}
if (assignee != null) {
data['assignee'] = assignee!.toJson();
}
if (assignees != null) {
data['assignees'] = assignees!.map((v) => v.toJson()).toList();
}
data['state'] = state;
data['is_locked'] = isLocked;
data['comments'] = comments;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
data['closed_at'] = closedAt;
data['due_date'] = dueDate;
data['pull_request'] = pullRequest;
if (repository != null) {
data['repository'] = repository!.toJson();
}
return data;
}
}
class _User {
int? id;
String? login;
String? fullName;
String? email;
String? avatarUrl;
String? language;
bool? isAdmin;
String? lastLogin;
String? created;
bool? restricted;
bool? active;
bool? prohibitLogin;
String? location;
String? website;
String? description;
String? visibility;
int? followersCount;
int? followingCount;
int? starredReposCount;
String? username;
_User(
{this.id,
this.login,
this.fullName,
this.email,
this.avatarUrl,
this.language,
this.isAdmin,
this.lastLogin,
this.created,
this.restricted,
this.active,
this.prohibitLogin,
this.location,
this.website,
this.description,
this.visibility,
this.followersCount,
this.followingCount,
this.starredReposCount,
this.username});
_User.fromJson(Map<String, dynamic> json) {
id = json['id'];
login = json['login'];
fullName = json['full_name'];
email = json['email'];
avatarUrl = json['avatar_url'];
language = json['language'];
isAdmin = json['is_admin'];
lastLogin = json['last_login'];
created = json['created'];
restricted = json['restricted'];
active = json['active'];
prohibitLogin = json['prohibit_login'];
location = json['location'];
website = json['website'];
description = json['description'];
visibility = json['visibility'];
followersCount = json['followers_count'];
followingCount = json['following_count'];
starredReposCount = json['starred_repos_count'];
username = json['username'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['login'] = login;
data['full_name'] = fullName;
data['email'] = email;
data['avatar_url'] = avatarUrl;
data['language'] = language;
data['is_admin'] = isAdmin;
data['last_login'] = lastLogin;
data['created'] = created;
data['restricted'] = restricted;
data['active'] = active;
data['prohibit_login'] = prohibitLogin;
data['location'] = location;
data['website'] = website;
data['description'] = description;
data['visibility'] = visibility;
data['followers_count'] = followersCount;
data['following_count'] = followingCount;
data['starred_repos_count'] = starredReposCount;
data['username'] = username;
return data;
}
}
class Labels {
int id;
String name;
String color;
String description;
String url;
Labels(
{required this.id,
required this.name,
required this.color,
required this.description,
required this.url});
Labels.fromJson(Map<String, dynamic> json)
: id = json['id'],
name = json['name'],
color = json['color'],
description = json['description'],
url = json['url'];
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
data['color'] = color;
data['description'] = description;
data['url'] = url;
return data;
}
}
class Milestone {
int id;
String title;
String description;
String state;
int openIssues;
int closedIssues;
String createdAt;
String updatedAt;
String? closedAt;
String? dueOn;
Milestone(
{required this.id,
required this.title,
required this.description,
required this.state,
required this.openIssues,
required this.closedIssues,
required this.createdAt,
required this.updatedAt,
this.closedAt,
this.dueOn});
Milestone.fromJson(Map<String, dynamic> json)
: id = json['id'],
title = json['title'],
description = json['description'],
state = json['state'],
openIssues = json['open_issues'],
closedIssues = json['closed_issues'],
createdAt = json['created_at'],
updatedAt = json['updated_at'] {
closedAt = json['closed_at'];
dueOn = json['due_on'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['title'] = title;
data['description'] = description;
data['state'] = state;
data['open_issues'] = openIssues;
data['closed_issues'] = closedIssues;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
data['closed_at'] = closedAt;
data['due_on'] = dueOn;
return data;
}
}
class _Repository {
int id;
String name;
String owner;
String fullName;
_Repository(
{required this.id,
required this.name,
required this.owner,
required this.fullName});
_Repository.fromJson(Map<String, dynamic> json)
: id = json['id'],
name = json['name'],
owner = json['owner'],
fullName = json['full_name'];
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
data['owner'] = owner;
data['full_name'] = fullName;
return data;
}
}

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:gitea_client/model/ApiAccess.dart';
import 'package:gitea_client/model/issues.dart';
import 'package:gitea_client/model/repository.dart';
import 'package:gitea_client/model/user.dart';
import 'package:http/http.dart' as http;
@ -53,7 +54,31 @@ class GiteaService {
}
throw Exception('error fetching files');
}
//
Future<List<Issue>> getRepoIssues({required String repoFullname,required String state,int page = 1, int limit = 10}) async{
if(state != "all" || state != "closed" || state != "open") {
throw Exception("Wrong state provided!");
}
var response = await http.get(
Uri.https(apiAccess.instance, "api/v1/user/repos/$repoFullname/issues", {
"token": apiAccess.token,
"state": state,
"page" : page.toString(),
"limit" : limit.toString(),
}),
);
if (response.statusCode == 200) {
final body = json.decode(response.body) as List;
var result = body.map((dynamic json) {
return Issue.fromJson(json);
}).toList();
result.sort((a,b) => -1*a.updatedAt!.compareTo(b.updatedAt!));
return result;
}
throw Exception('error fetching posts');
}
Future<List<Repository>> getUserRepositories([int page = 1, int limit = 10]) async{
var response = await http.get(
Uri.https(apiAccess.instance, "api/v1/user/repos", {
@ -71,7 +96,7 @@ class GiteaService {
result.sort((a,b) => -1*a.updatedAt!.compareTo(b.updatedAt!));
return result;
}
throw Exception('error fetching posts');
throw Exception('error fetching repos');
}
Future<SearchResult> searchRepo({required String query,int page = 1, int limit = 10}) async{

View file

@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View file

@ -105,7 +105,7 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
version: "1.16.0"
convert:
dependency: transitive
description:
@ -147,7 +147,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0"
ffi:
dependency: transitive
description:
@ -258,7 +258,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
version: "0.6.4"
json_annotation:
dependency: transitive
description:
@ -307,7 +307,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
version: "0.1.4"
meta:
dependency: transitive
description:
@ -335,7 +335,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
path_drawing:
dependency: transitive
description:
@ -501,7 +501,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
stack_trace:
dependency: transitive
description:
@ -543,7 +543,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.8"
version: "0.4.9"
typed_data:
dependency: transitive
description:
@ -613,7 +613,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.1.2"
watcher:
dependency: transitive
description:
@ -650,5 +650,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.16.2 <3.0.0"
dart: ">=2.17.0-0 <3.0.0"
flutter: ">=2.10.0"