Skip to content
代码片段 群组 项目
提交 ce48cb92 编辑于 作者: ling zhang's avatar ling zhang 提交者: Neil Wang
浏览文件

feat: #165 用户星标子组

上级 f80f5604
No related branches found
No related tags found
无相关合并请求
显示
269 个添加53 个删除
文件已添加
...@@ -6,7 +6,7 @@ import 'package:path/path.dart'; ...@@ -6,7 +6,7 @@ import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
class DbManager { class DbManager {
static const int _version = 4; static const int _version = 5;
static const String _name = "jihu.db"; static const String _name = "jihu.db";
static final DbManager _instance = DbManager._internal(); static final DbManager _instance = DbManager._internal();
Database? _db; Database? _db;
...@@ -48,6 +48,9 @@ class DbManager { ...@@ -48,6 +48,9 @@ class DbManager {
case 4: case 4:
db.execute(GroupAndProjectEntity.createTableSql); db.execute(GroupAndProjectEntity.createTableSql);
break; break;
case 5:
db.execute(GroupAndProjectEntity.addStarredColumn);
break;
default: default:
break; break;
} }
...@@ -94,7 +97,16 @@ class DbManager { ...@@ -94,7 +97,16 @@ class DbManager {
return await db.query(tableName, distinct: distinct, columns: columns, where: where, whereArgs: whereArgs); return await db.query(tableName, distinct: distinct, columns: columns, where: where, whereArgs: whereArgs);
} }
Future<List<Map<String, dynamic>>> rawFind(String sql) async {
var db = await getDb();
return await db.rawQuery(sql);
}
Future<int> delete(String tableName, {String? where, List<Object?>? whereArgs}) async { Future<int> delete(String tableName, {String? where, List<Object?>? whereArgs}) async {
return (await getDb()).delete(tableName, where: where, whereArgs: whereArgs); return (await getDb()).delete(tableName, where: where, whereArgs: whereArgs);
} }
Future<int> update(String tableName, Map<String, Object?> values, {String? where, List<Object?>? whereArgs, ConflictAlgorithm? conflictAlgorithm}) async {
return (await getDb()).update(tableName, values, where: where, whereArgs: whereArgs, conflictAlgorithm: conflictAlgorithm);
}
} }
import 'package:path/path.dart';
class Api {
static const version = "v4";
static const prefix = "/api/$version";
static String join(String path) {
return normalize('$prefix/$path');
}
}
...@@ -11,4 +11,6 @@ class CustomIcons { ...@@ -11,4 +11,6 @@ class CustomIcons {
static const IconData toDoList = IconData(0xe802, fontFamily: _kFontFom, matchTextDirection: true); static const IconData toDoList = IconData(0xe802, fontFamily: _kFontFom, matchTextDirection: true);
static const IconData resources = IconData(0xe803, fontFamily: _kFontFom, matchTextDirection: true); static const IconData resources = IconData(0xe803, fontFamily: _kFontFom, matchTextDirection: true);
static const IconData star_1 = IconData(0xe801, fontFamily: "StarIcon", fontPackage: null);
} }
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:jihu_gitlab_app/core/net/api.dart';
import 'package:jihu_gitlab_app/core/net/http_client.dart';
import 'package:jihu_gitlab_app/core/themes/custom_icons.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart';
class Star extends StatefulWidget {
final GroupAndProject data;
const Star({required this.data, Key? key}) : super(key: key);
@override
State<Star> createState() => _StarState();
}
class _StarState extends State<Star> {
late StarModel _model;
bool _loading = false;
@override
void initState() {
super.initState();
_model = StarModel(widget.data);
}
@override
Widget build(BuildContext context) {
return Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
_buildLoadingView(),
GestureDetector(
child: Icon(CustomIcons.star_1, color: _model.starred ? Theme.of(context).primaryColor : Colors.grey),
onTap: () async {
if (widget.data.type == SubgroupItemType.project) {
setState(() => _loading = true);
}
await _model.toggleStar();
setState(() => {_loading = false});
}),
]);
}
_buildLoadingView() {
return _loading ? const SizedBox(width: 12, height: 12, child: CircularProgressIndicator(strokeWidth: 1)) : const SizedBox(width: 0);
}
}
class StarModel {
late GroupAndProjectRepository _repository;
GroupAndProject data;
StarModel(this.data) {
_repository = GroupAndProjectRepository.instance();
}
Future<bool> toggleStar() async {
bool star = !data.starred;
bool result = false;
if (data.type == SubgroupItemType.project) {
String path = Api.join("projects/${data.iid}/${star ? 'star' : 'unstar'}");
try {
await HttpClient.instance().post(path, {});
result = true;
} catch (error) {
var e = error as DioError;
if (304 == e.response?.statusCode) {
result = true;
}
}
} else {
result = true;
}
if (result) {
data.starred = star;
_repository.toggleStar(data.id!, star);
}
return Future.value(result);
}
bool get starred => data.starred;
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jihu_gitlab_app/core/widgets/star.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroup_page.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/subgroup_page.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/project/project_issues_page.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/project/project_issues_page.dart';
...@@ -34,17 +35,9 @@ class _GroupAndProjectListTileState extends State<GroupAndProjectListTile> { ...@@ -34,17 +35,9 @@ class _GroupAndProjectListTileState extends State<GroupAndProjectListTile> {
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
flex: 1, flex: 1,
child: Text( child: Text(widget.data.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(color: Color(0xFF1A1B36), fontSize: 14, fontWeight: FontWeight.w500)),
widget.data.name,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleLarge,
),
), ),
const Icon( Star(data: widget.data)
Icons.keyboard_arrow_right,
color: Colors.grey,
)
], ],
), ),
), ),
...@@ -53,10 +46,10 @@ class _GroupAndProjectListTileState extends State<GroupAndProjectListTile> { ...@@ -53,10 +46,10 @@ class _GroupAndProjectListTileState extends State<GroupAndProjectListTile> {
void _onTap(GroupAndProject item) { void _onTap(GroupAndProject item) {
if (item.type == SubgroupItemType.group || item.type == SubgroupItemType.subgroup) { if (item.type == SubgroupItemType.group || item.type == SubgroupItemType.subgroup) {
final params = <String, dynamic>{'groupId': item.id, 'name': item.name, 'relativePath': item.relativePath}; final params = <String, dynamic>{'groupId': item.iid, 'name': item.name, 'relativePath': item.relativePath};
Navigator.of(context).pushNamed(SubgroupPage.routeName, arguments: params); Navigator.of(context).pushNamed(SubgroupPage.routeName, arguments: params);
} else { } else {
final params = <String, dynamic>{'projectId': item.id, 'name': item.name, 'relativePath': item.relativePath, "groupId": item.parentId}; final params = <String, dynamic>{'projectId': item.iid, 'name': item.name, 'relativePath': item.relativePath, "groupId": item.parentId};
Navigator.of(context).pushNamed(ProjectIssuesPage.routeName, arguments: params); Navigator.of(context).pushNamed(ProjectIssuesPage.routeName, arguments: params);
} }
} }
......
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart';
class GroupAndProject { class GroupAndProject {
int id; int? id;
int iid;
String name; String name;
SubgroupItemType? type; SubgroupItemType? type;
String? relativePath; String? relativePath;
int? parentId; int? parentId;
bool starred;
GroupAndProject(this.id, this.name, this.type, this.relativePath, this.parentId); GroupAndProject(this.id, this.iid, this.name, this.type, this.relativePath, this.parentId, this.starred);
String get icon => type == SubgroupItemType.project ? 'assets/images/project_icon.png' : 'assets/images/group_icon.png'; String get icon => type == SubgroupItemType.project ? 'assets/images/project_icon.png' : 'assets/images/group_icon.png';
} }
...@@ -18,36 +20,43 @@ class GroupAndProjectEntity { ...@@ -18,36 +20,43 @@ class GroupAndProjectEntity {
int? id; int? id;
final int userId; final int userId;
final int iid; final int iid;
final String name; String name;
final String? type; final String? type;
final String? relativePath; String? relativePath;
final int? parentId; final int? parentId;
final int version; final int? starred;
int version;
GroupAndProjectEntity._internal(this.id, this.userId, this.iid, this.name, this.type, this.relativePath, this.parentId, this.version); GroupAndProjectEntity._internal(this.id, this.userId, this.iid, this.name, this.type, this.relativePath, this.parentId, this.version, this.starred);
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return {"id": id, "user_id": userId, "iid": iid, "name": name, "type": type, "relative_path": relativePath, "parent_id": parentId, "version": version}; return {"id": id, "user_id": userId, "iid": iid, "name": name, "type": type, "relative_path": relativePath, "parent_id": parentId, "version": version};
} }
static GroupAndProjectEntity createGroupEntity(Map<String, dynamic> json, int userId, int parentId, int version) { static GroupAndProjectEntity createGroupEntity(Map<String, dynamic> json, int userId, int parentId, int version) {
return GroupAndProjectEntity._internal(null, userId, json['id'], json['name'], SubgroupItemType.group.name, json['full_path'] ?? '', parentId, version); return GroupAndProjectEntity._internal(null, userId, json['id'], json['name'], SubgroupItemType.group.name, json['full_path'] ?? '', parentId, version, null);
} }
static GroupAndProjectEntity createSubgroupEntity(Map<String, dynamic> json, int userId, int parentId, int version) { static GroupAndProjectEntity createSubgroupEntity(Map<String, dynamic> json, int userId, int parentId, int version) {
return GroupAndProjectEntity._internal(null, userId, json['id'], json['name'], SubgroupItemType.subgroup.name, json['full_path'] ?? '', parentId, version); return GroupAndProjectEntity._internal(null, userId, json['id'], json['name'], SubgroupItemType.subgroup.name, json['full_path'] ?? '', parentId, version, null);
} }
static GroupAndProjectEntity createProjectEntity(Map<String, dynamic> json, int userId, int parentId, int version) { static GroupAndProjectEntity createProjectEntity(Map<String, dynamic> json, int userId, int parentId, int version) {
return GroupAndProjectEntity._internal(null, userId, json['id'], json['name'], SubgroupItemType.project.name, json['path_with_namespace'] ?? '', parentId, version); return GroupAndProjectEntity._internal(null, userId, json['id'], json['name'], SubgroupItemType.project.name, json['path_with_namespace'] ?? '', parentId, version, null);
} }
static GroupAndProjectEntity restore(Map<String, dynamic> map) { static GroupAndProjectEntity restore(Map<String, dynamic> map) {
return GroupAndProjectEntity._internal(map['id'], map['user_id'], map['iid'], map['name'], map['type'], map['relative_path'], map['parent_id'], map['version']); return GroupAndProjectEntity._internal(map['id'], map['user_id'], map['iid'], map['name'], map['type'], map['relative_path'], map['parent_id'], map['version'], map['starred'] ?? 0);
} }
GroupAndProject asDomain() { GroupAndProject asDomain() {
return GroupAndProject(iid, name, SubgroupItemType.values.where((e) => e.name == type).first, relativePath, parentId); return GroupAndProject(id, iid, name, SubgroupItemType.values.where((e) => e.name == type).first, relativePath, parentId, starred == 1);
}
void update(GroupAndProjectEntity newValue) {
name = newValue.name;
relativePath = newValue.relativePath;
version = newValue.version;
} }
@override @override
...@@ -69,4 +78,8 @@ class GroupAndProjectEntity { ...@@ -69,4 +78,8 @@ class GroupAndProjectEntity {
version INTEGER version INTEGER
); );
"""; """;
static String get addStarredColumn => """
ALTER TABLE $tableName ADD COLUMN starred INTEGER;
""";
} }
import 'package:jihu_gitlab_app/core/db_manager.dart'; import 'package:jihu_gitlab_app/core/db_manager.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart';
class GroupAndProjectRepository { class GroupAndProjectRepository {
static final GroupAndProjectRepository _instance = GroupAndProjectRepository._internal();
GroupAndProjectRepository._internal();
factory GroupAndProjectRepository.instance() => _instance;
insert(List<GroupAndProjectEntity> subgroupEntities) async { insert(List<GroupAndProjectEntity> subgroupEntities) async {
return await DbManager.instance().batchInsert(GroupAndProjectEntity.tableName, subgroupEntities.map((e) => e.toMap()).toList()); return await DbManager.instance().batchInsert(GroupAndProjectEntity.tableName, subgroupEntities.map((e) => e.toMap()).toList());
} }
...@@ -10,18 +17,21 @@ class GroupAndProjectRepository { ...@@ -10,18 +17,21 @@ class GroupAndProjectRepository {
return await DbManager.instance().batchUpdate(GroupAndProjectEntity.tableName, subgroupEntities.map((e) => e.toMap()).toList()); return await DbManager.instance().batchUpdate(GroupAndProjectEntity.tableName, subgroupEntities.map((e) => e.toMap()).toList());
} }
Future<int> deleteLessThan(int userId, int parentId, int version) async { Future<int> deleteLessThan(int userId, int parentId, SubgroupItemType type, int version) async {
return await DbManager.instance().delete(GroupAndProjectEntity.tableName, where: ' user_id = ? and parent_id = ? and version < ? ', whereArgs: [userId, parentId, version]); return await DbManager.instance().delete(GroupAndProjectEntity.tableName, where: ' user_id = ? and parent_id = ? and type = ? and version < ? ', whereArgs: [userId, parentId, type.name, version]);
} }
Future<List<GroupAndProjectEntity>> query({required int userId, required int parentId, List<int>? iidList}) async { Future<List<GroupAndProjectEntity>> query({required int userId, required int parentId, List<int>? iidList}) async {
String where = 'user_id = ? and parent_id = ? '; String sql = "select * from ${GroupAndProjectEntity.tableName} where user_id = $userId and parent_id = $parentId";
List<Object> whereArgs = [userId, parentId];
if (iidList != null) { if (iidList != null) {
where += 'and iid in (?)'; sql += " and iid in (${iidList.join(',')})";
whereArgs.add(iidList.join(","));
} }
var list = await DbManager.instance().find(GroupAndProjectEntity.tableName, where: where, whereArgs: whereArgs); sql += " order by type desc";
var list = await DbManager.instance().rawFind(sql);
return list.map((e) => GroupAndProjectEntity.restore(e)).toList(); return list.map((e) => GroupAndProjectEntity.restore(e)).toList();
} }
void toggleStar(int id, bool starred) async {
await DbManager.instance().update(GroupAndProjectEntity.tableName, {"starred": starred ? 1 : 0}, where: " id = ? ", whereArgs: [id]);
}
} }
...@@ -5,6 +5,7 @@ import 'package:jihu_gitlab_app/core/user_provider.dart'; ...@@ -5,6 +5,7 @@ import 'package:jihu_gitlab_app/core/user_provider.dart';
import 'package:jihu_gitlab_app/core/widgets/selector/data_provider.dart'; import 'package:jihu_gitlab_app/core/widgets/selector/data_provider.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart';
class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> { class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> {
late int _userId; late int _userId;
...@@ -14,7 +15,7 @@ class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> { ...@@ -14,7 +15,7 @@ class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> {
SubgroupAndProjectProvider({required int userId, required int parentId}) { SubgroupAndProjectProvider({required int userId, required int parentId}) {
_userId = userId; _userId = userId;
_parentId = parentId; _parentId = parentId;
_repository = GroupAndProjectRepository(); _repository = GroupAndProjectRepository.instance();
} }
@override @override
...@@ -35,7 +36,7 @@ class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> { ...@@ -35,7 +36,7 @@ class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> {
url: '/api/v4/groups/$_parentId/subgroups?all_available=true', url: '/api/v4/groups/$_parentId/subgroups?all_available=true',
dataProcessor: (data, page, pageSize) async { dataProcessor: (data, page, pageSize) async {
List<GroupAndProjectEntity> subgroupEntities = (data as List).map((e) => GroupAndProjectEntity.createSubgroupEntity(e, UserProvider.userId!, _parentId, version)).toList(growable: false); List<GroupAndProjectEntity> subgroupEntities = (data as List).map((e) => GroupAndProjectEntity.createSubgroupEntity(e, UserProvider.userId!, _parentId, version)).toList(growable: false);
await save(subgroupEntities, version); await save(subgroupEntities, SubgroupItemType.subgroup, version);
return subgroupEntities.length >= pageSize; return subgroupEntities.length >= pageSize;
}).run(); }).run();
} }
...@@ -46,12 +47,12 @@ class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> { ...@@ -46,12 +47,12 @@ class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> {
dataProcessor: (data, page, pageSize) async { dataProcessor: (data, page, pageSize) async {
List<GroupAndProjectEntity> subgroupEntities = List<GroupAndProjectEntity> subgroupEntities =
((data['projects'] ?? []) as List).map((e) => GroupAndProjectEntity.createProjectEntity(e, UserProvider.userId!, _parentId, version)).toList(growable: false); ((data['projects'] ?? []) as List).map((e) => GroupAndProjectEntity.createProjectEntity(e, UserProvider.userId!, _parentId, version)).toList(growable: false);
await save(subgroupEntities, version); await save(subgroupEntities, SubgroupItemType.project, version);
return subgroupEntities.length >= pageSize; return subgroupEntities.length >= pageSize;
}).run(); }).run();
} }
Future<void> save(List<GroupAndProjectEntity> subgroupEntities, int version) async { Future<void> save(List<GroupAndProjectEntity> subgroupEntities, SubgroupItemType type, int version) async {
List<int> iidList = subgroupEntities.map((e) => e.iid).toList(); List<int> iidList = subgroupEntities.map((e) => e.iid).toList();
List<GroupAndProjectEntity> exists = await _repository.query(userId: _userId, parentId: _parentId, iidList: iidList); List<GroupAndProjectEntity> exists = await _repository.query(userId: _userId, parentId: _parentId, iidList: iidList);
ListCompareResult<GroupAndProjectEntity> result = ListComparator.compare<GroupAndProjectEntity>(exists, subgroupEntities); ListCompareResult<GroupAndProjectEntity> result = ListComparator.compare<GroupAndProjectEntity>(exists, subgroupEntities);
...@@ -60,12 +61,12 @@ class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> { ...@@ -60,12 +61,12 @@ class SubgroupAndProjectProvider extends DataProvider<GroupAndProject> {
} }
if (result.hasUpdate) { if (result.hasUpdate) {
var list = result.update.map((e) { var list = result.update.map((e) {
e.right.id = e.left.id!; e.left.update(e.right);
return e.right; return e.left;
}).toList(); }).toList();
await _repository.update(list); await _repository.update(list);
} }
await _repository.deleteLessThan(_userId, _parentId, version); await _repository.deleteLessThan(_userId, _parentId, type, version);
} }
@visibleForTesting @visibleForTesting
......
...@@ -2,6 +2,7 @@ import 'package:jihu_gitlab_app/core/synchronizer.dart'; ...@@ -2,6 +2,7 @@ import 'package:jihu_gitlab_app/core/synchronizer.dart';
import 'package:jihu_gitlab_app/core/user_provider.dart'; import 'package:jihu_gitlab_app/core/user_provider.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroup_and_project_provider.dart'; import 'package:jihu_gitlab_app/modules/projects/group_details/subgroup_and_project_provider.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart';
class GroupProvider extends SubgroupAndProjectProvider { class GroupProvider extends SubgroupAndProjectProvider {
GroupProvider({required super.userId, required super.parentId}); GroupProvider({required super.userId, required super.parentId});
...@@ -18,7 +19,7 @@ class GroupProvider extends SubgroupAndProjectProvider { ...@@ -18,7 +19,7 @@ class GroupProvider extends SubgroupAndProjectProvider {
url: '/api/v4/groups?top_level_only=true', url: '/api/v4/groups?top_level_only=true',
dataProcessor: (data, page, pageSize) async { dataProcessor: (data, page, pageSize) async {
List<GroupAndProjectEntity> subgroupEntities = (data as List).map((e) => GroupAndProjectEntity.createGroupEntity(e, UserProvider.userId!, 0, version)).toList(growable: false); List<GroupAndProjectEntity> subgroupEntities = (data as List).map((e) => GroupAndProjectEntity.createGroupEntity(e, UserProvider.userId!, 0, version)).toList(growable: false);
await save(subgroupEntities, version); await save(subgroupEntities, SubgroupItemType.group, version);
return subgroupEntities.length >= pageSize; return subgroupEntities.length >= pageSize;
}).run(); }).run();
} }
......
...@@ -4,9 +4,9 @@ import 'package:sqflite/sqflite.dart'; ...@@ -4,9 +4,9 @@ import 'package:sqflite/sqflite.dart';
class MemberRepository { class MemberRepository {
Future<List<MemberEntity>> query(int projectId, Iterable<int> memberIds) async { Future<List<MemberEntity>> query(int projectId, Iterable<int> memberIds) async {
Database database = await DbManager.instance().getDb(); String sql = "select * from ${MemberEntity.tableName} where project_id = $projectId and member_id in (${memberIds.join(", ")})";
List<Map<String, Object?>> query = await database.query(MemberEntity.tableName, where: " project_id = ? and member_id in (?) ", whereArgs: [projectId, (memberIds.join(", "))]); var list = await DbManager.instance().rawFind(sql);
return query.map((e) => MemberEntity.restore(e)).toList(); return list.map((e) => MemberEntity.restore(e)).toList();
} }
Future<List<int>> insert(List<MemberEntity> memberEntities) async { Future<List<int>> insert(List<MemberEntity> memberEntities) async {
......
...@@ -3,7 +3,7 @@ description: Flutter project for JiHu GitLab. ...@@ -3,7 +3,7 @@ description: Flutter project for JiHu GitLab.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.1.0+104 # Version number + build number version: 1.2.0+104 # Version number + build number
environment: environment:
sdk: '>=2.18.4 <3.0.0' sdk: '>=2.18.4 <3.0.0'
...@@ -73,6 +73,10 @@ flutter: ...@@ -73,6 +73,10 @@ flutter:
- family: CustomIcons - family: CustomIcons
fonts: fonts:
- asset: assets/fonts/MyFlutterApp.ttf - asset: assets/fonts/MyFlutterApp.ttf
- family: StarIcon
fonts:
- asset: assets/fonts/Star.ttf
flutter_icons: flutter_icons:
image_path: "assets/images/icon.png" image_path: "assets/images/icon.png"
......
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jihu_gitlab_app/core/net/http_client.dart';
import 'package:jihu_gitlab_app/core/net/response.dart' as resp;
import 'package:jihu_gitlab_app/core/themes/custom_icons.dart';
import 'package:jihu_gitlab_app/core/widgets/star.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart';
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart';
import 'package:mockito/mockito.dart';
import '../net/http_request_test.mocks.dart';
void main() {
testWidgets("Should be able to star or unstar a project", (tester) async {
var client = MockHttpClient();
HttpClient.setInstance(client);
when(client.post("/api/v4/projects/59893/star", {})).thenAnswer((_) => Future(() => resp.Response([])));
when(client.post("/api/v4/projects/59893/unstar", {})).thenAnswer((_) => Future(() => resp.Response.of([])));
var data = GroupAndProject(1, 59893, "demo", SubgroupItemType.project, "relative_path", 59894, false);
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Center(child: Star(data: data)),
)));
await tester.pumpAndSettle();
expect(find.byType(Star), findsOneWidget);
expect(find.byIcon(CustomIcons.star_1), findsOneWidget);
await tester.ensureVisible(find.byType(Star));
await tester.tap(find.byIcon(CustomIcons.star_1));
await tester.pumpAndSettle();
verify(client.post("/api/v4/projects/59893/star", any)).called(1);
expect(data.starred, true);
await tester.tap(find.byIcon(CustomIcons.star_1));
await tester.pumpAndSettle();
verify(client.post("/api/v4/projects/59893/unstar", any)).called(1);
expect(data.starred, false);
});
testWidgets("Should be able to star a starred project", (tester) async {
var client = MockHttpClient();
HttpClient.setInstance(client);
when(client.post("/api/v4/projects/59893/star", {})).thenThrow(DioError(requestOptions: RequestOptions(path: ''), response: Response(requestOptions: RequestOptions(path: ''), statusCode: 304)));
// when(client.post("/api/v4/projects/59893/unstar", {})).thenAnswer((_) => Future(() => Response.of([])));
var data = GroupAndProject(1, 59893, "demo", SubgroupItemType.project, "relative_path", 59894, false);
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Center(child: Star(data: data)),
)));
await tester.pumpAndSettle();
expect(find.byType(Star), findsOneWidget);
expect(find.byIcon(CustomIcons.star_1), findsOneWidget);
await tester.ensureVisible(find.byType(Star));
await tester.tap(find.byIcon(CustomIcons.star_1));
await tester.pumpAndSettle();
expect(data.starred, true);
});
}
...@@ -31,7 +31,7 @@ void main() { ...@@ -31,7 +31,7 @@ void main() {
locator.registerSingleton(subgroupListModel); locator.registerSingleton(subgroupListModel);
locator.registerSingleton(projectsModel); locator.registerSingleton(projectsModel);
var provider = MockGroupProvider(); var provider = MockGroupProvider();
when(provider.loadFromLocal()).thenAnswer((_) => Future(() => [GroupAndProject(118014, "highsoft", SubgroupItemType.group, "highsof-t", 0)])); when(provider.loadFromLocal()).thenAnswer((_) => Future(() => [GroupAndProject(null, 118014, "highsoft", SubgroupItemType.group, "highsof-t", 0, false)]));
projectsModel.injectDataProviderForTesting(provider); projectsModel.injectDataProviderForTesting(provider);
testWidgets('Should sub group page can create issue', (WidgetTester tester) async { testWidgets('Should sub group page can create issue', (WidgetTester tester) async {
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
import 'dart:async' as _i3; import 'dart:async' as _i3;
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart' as _i4; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart' as _i4;
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart' as _i5; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart' as _i6;
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart' as _i5;
import 'package:jihu_gitlab_app/modules/projects/groups/group_provider.dart' as _i2; import 'package:jihu_gitlab_app/modules/projects/groups/group_provider.dart' as _i2;
import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/mockito.dart' as _i1;
...@@ -46,6 +47,7 @@ class MockGroupProvider extends _i1.Mock implements _i2.GroupProvider { ...@@ -46,6 +47,7 @@ class MockGroupProvider extends _i1.Mock implements _i2.GroupProvider {
@override @override
_i3.Future<void> save( _i3.Future<void> save(
List<_i4.GroupAndProjectEntity>? subgroupEntities, List<_i4.GroupAndProjectEntity>? subgroupEntities,
_i5.SubgroupItemType? type,
int? version, int? version,
) => ) =>
(super.noSuchMethod( (super.noSuchMethod(
...@@ -53,6 +55,7 @@ class MockGroupProvider extends _i1.Mock implements _i2.GroupProvider { ...@@ -53,6 +55,7 @@ class MockGroupProvider extends _i1.Mock implements _i2.GroupProvider {
#save, #save,
[ [
subgroupEntities, subgroupEntities,
type,
version, version,
], ],
), ),
...@@ -60,7 +63,7 @@ class MockGroupProvider extends _i1.Mock implements _i2.GroupProvider { ...@@ -60,7 +63,7 @@ class MockGroupProvider extends _i1.Mock implements _i2.GroupProvider {
returnValueForMissingStub: _i3.Future<void>.value(), returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>); ) as _i3.Future<void>);
@override @override
void injectRepositoryForTesting(_i5.GroupAndProjectRepository? groupAndProjectRepository) => super.noSuchMethod( void injectRepositoryForTesting(_i6.GroupAndProjectRepository? groupAndProjectRepository) => super.noSuchMethod(
Invocation.method( Invocation.method(
#injectRepositoryForTesting, #injectRepositoryForTesting,
[groupAndProjectRepository], [groupAndProjectRepository],
......
...@@ -26,7 +26,7 @@ void main() { ...@@ -26,7 +26,7 @@ void main() {
var subgroupListModel = SubgroupListModel(); var subgroupListModel = SubgroupListModel();
locator.registerSingleton(subgroupListModel); locator.registerSingleton(subgroupListModel);
var provider = MockSubgroupAndProjectProvider(); var provider = MockSubgroupAndProjectProvider();
when(provider.loadFromLocal()).thenAnswer((_) => Future(() => [GroupAndProject(59893, "极狐 GitLab APP 代码", SubgroupItemType.project, "ultimate-plan/jihu-gitlab-app/jihu-gitlab-app", 0)])); when(provider.loadFromLocal()).thenAnswer((_) => Future(() => [GroupAndProject(1, 59893, "极狐 GitLab APP 代码", SubgroupItemType.project, "ultimate-plan/jihu-gitlab-app/jihu-gitlab-app", 0, true)]));
subgroupListModel.injectDataProviderForTesting(provider); subgroupListModel.injectDataProviderForTesting(provider);
testWidgets('Should go in to project page', (WidgetTester tester) async { testWidgets('Should go in to project page', (WidgetTester tester) async {
......
...@@ -6,8 +6,9 @@ ...@@ -6,8 +6,9 @@
import 'dart:async' as _i3; import 'dart:async' as _i3;
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart' as _i4; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart' as _i4;
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart' as _i5; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart' as _i6;
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroup_and_project_provider.dart' as _i2; import 'package:jihu_gitlab_app/modules/projects/group_details/subgroup_and_project_provider.dart' as _i2;
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart' as _i5;
import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: type=lint // ignore_for_file: type=lint
...@@ -46,6 +47,7 @@ class MockSubgroupAndProjectProvider extends _i1.Mock implements _i2.SubgroupAnd ...@@ -46,6 +47,7 @@ class MockSubgroupAndProjectProvider extends _i1.Mock implements _i2.SubgroupAnd
@override @override
_i3.Future<void> save( _i3.Future<void> save(
List<_i4.GroupAndProjectEntity>? subgroupEntities, List<_i4.GroupAndProjectEntity>? subgroupEntities,
_i5.SubgroupItemType? type,
int? version, int? version,
) => ) =>
(super.noSuchMethod( (super.noSuchMethod(
...@@ -53,6 +55,7 @@ class MockSubgroupAndProjectProvider extends _i1.Mock implements _i2.SubgroupAnd ...@@ -53,6 +55,7 @@ class MockSubgroupAndProjectProvider extends _i1.Mock implements _i2.SubgroupAnd
#save, #save,
[ [
subgroupEntities, subgroupEntities,
type,
version, version,
], ],
), ),
...@@ -60,7 +63,7 @@ class MockSubgroupAndProjectProvider extends _i1.Mock implements _i2.SubgroupAnd ...@@ -60,7 +63,7 @@ class MockSubgroupAndProjectProvider extends _i1.Mock implements _i2.SubgroupAnd
returnValueForMissingStub: _i3.Future<void>.value(), returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>); ) as _i3.Future<void>);
@override @override
void injectRepositoryForTesting(_i5.GroupAndProjectRepository? groupAndProjectRepository) => super.noSuchMethod( void injectRepositoryForTesting(_i6.GroupAndProjectRepository? groupAndProjectRepository) => super.noSuchMethod(
Invocation.method( Invocation.method(
#injectRepositoryForTesting, #injectRepositoryForTesting,
[groupAndProjectRepository], [groupAndProjectRepository],
......
...@@ -30,7 +30,7 @@ void main() { ...@@ -30,7 +30,7 @@ void main() {
when(repository.query(userId: 9527, parentId: 59893)).thenAnswer((realInvocation) async => Future.value([GroupAndProjectEntity.restore(dbData)])); when(repository.query(userId: 9527, parentId: 59893)).thenAnswer((realInvocation) async => Future.value([GroupAndProjectEntity.restore(dbData)]));
var list = await groupAndProjectProvider.loadFromLocal(); var list = await groupAndProjectProvider.loadFromLocal();
expect(list.length, 1); expect(list.length, 1);
expect(list[0].id, 11); expect(list[0].iid, 11);
expect(list[0].name, "test_project"); expect(list[0].name, "test_project");
expect(list[0].relativePath, "path_with_namespace_test"); expect(list[0].relativePath, "path_with_namespace_test");
}); });
...@@ -57,7 +57,8 @@ void main() { ...@@ -57,7 +57,8 @@ void main() {
var result = await groupAndProjectProvider.syncFromRemote(); var result = await groupAndProjectProvider.syncFromRemote();
expect(result, true); expect(result, true);
verify(repository.deleteLessThan(9527, 59893, any)).called(2); verify(repository.deleteLessThan(9527, 59893, SubgroupItemType.subgroup, any)).called(1);
verify(repository.deleteLessThan(9527, 59893, SubgroupItemType.project, any)).called(1);
verify(repository.insert(argThat(predicate((x) { verify(repository.insert(argThat(predicate((x) {
return x is List<GroupAndProjectEntity> && x.length == 1 && x[0].iid == 33; return x is List<GroupAndProjectEntity> && x.length == 1 && x[0].iid == 33;
})))); }))));
......
...@@ -7,6 +7,7 @@ import 'dart:async' as _i4; ...@@ -7,6 +7,7 @@ import 'dart:async' as _i4;
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart' as _i3; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project.dart' as _i3;
import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart' as _i2; import 'package:jihu_gitlab_app/modules/projects/group_details/group_and_project_repository.dart' as _i2;
import 'package:jihu_gitlab_app/modules/projects/group_details/subgroups/subgroup_list_model.dart' as _i5;
import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: type=lint // ignore_for_file: type=lint
...@@ -44,6 +45,7 @@ class MockGroupAndProjectRepository extends _i1.Mock implements _i2.GroupAndProj ...@@ -44,6 +45,7 @@ class MockGroupAndProjectRepository extends _i1.Mock implements _i2.GroupAndProj
_i4.Future<int> deleteLessThan( _i4.Future<int> deleteLessThan(
int? userId, int? userId,
int? parentId, int? parentId,
_i5.SubgroupItemType? type,
int? version, int? version,
) => ) =>
(super.noSuchMethod( (super.noSuchMethod(
...@@ -52,6 +54,7 @@ class MockGroupAndProjectRepository extends _i1.Mock implements _i2.GroupAndProj ...@@ -52,6 +54,7 @@ class MockGroupAndProjectRepository extends _i1.Mock implements _i2.GroupAndProj
[ [
userId, userId,
parentId, parentId,
type,
version, version,
], ],
), ),
...@@ -77,4 +80,19 @@ class MockGroupAndProjectRepository extends _i1.Mock implements _i2.GroupAndProj ...@@ -77,4 +80,19 @@ class MockGroupAndProjectRepository extends _i1.Mock implements _i2.GroupAndProj
returnValue: _i4.Future<List<_i3.GroupAndProjectEntity>>.value(<_i3.GroupAndProjectEntity>[]), returnValue: _i4.Future<List<_i3.GroupAndProjectEntity>>.value(<_i3.GroupAndProjectEntity>[]),
returnValueForMissingStub: _i4.Future<List<_i3.GroupAndProjectEntity>>.value(<_i3.GroupAndProjectEntity>[]), returnValueForMissingStub: _i4.Future<List<_i3.GroupAndProjectEntity>>.value(<_i3.GroupAndProjectEntity>[]),
) as _i4.Future<List<_i3.GroupAndProjectEntity>>); ) as _i4.Future<List<_i3.GroupAndProjectEntity>>);
@override
void toggleStar(
int? id,
bool? starred,
) =>
super.noSuchMethod(
Invocation.method(
#toggleStar,
[
id,
starred,
],
),
returnValueForMissingStub: null,
);
} }
...@@ -15,7 +15,7 @@ void main() { ...@@ -15,7 +15,7 @@ void main() {
expect(projectEntity.type, "project"); expect(projectEntity.type, "project");
var groupAndProject = projectEntity.asDomain(); var groupAndProject = projectEntity.asDomain();
expect(groupAndProject.id, 1); expect(groupAndProject.iid, 1);
expect(groupAndProject.name, "test_project"); expect(groupAndProject.name, "test_project");
expect(groupAndProject.relativePath, "path_with_namespace_test"); expect(groupAndProject.relativePath, "path_with_namespace_test");
expect(groupAndProject.parentId, 59883); expect(groupAndProject.parentId, 59883);
...@@ -34,7 +34,7 @@ void main() { ...@@ -34,7 +34,7 @@ void main() {
expect(projectEntity.type, "subgroup"); expect(projectEntity.type, "subgroup");
var groupAndProject = projectEntity.asDomain(); var groupAndProject = projectEntity.asDomain();
expect(groupAndProject.id, 1); expect(groupAndProject.iid, 1);
expect(groupAndProject.name, "test_project"); expect(groupAndProject.name, "test_project");
expect(groupAndProject.relativePath, "path_with_namespace_test"); expect(groupAndProject.relativePath, "path_with_namespace_test");
expect(groupAndProject.parentId, 59883); expect(groupAndProject.parentId, 59883);
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册