Skip to content
代码片段 群组 项目
提交 95b91729 编辑于 作者: Raymond Liao's avatar Raymond Liao :nerd:
浏览文件

feat: #608 用户在可以通过搜索选择 Labels

上级 8e062718
分支
未找到相关标签
无相关合并请求
显示
295 个添加113 个删除
......@@ -3,6 +3,7 @@ import 'package:jihu_gitlab_app/core/domain/assignee/assignee_entity.dart';
import 'package:jihu_gitlab_app/modules/ai/message/message_entity.dart';
import 'package:jihu_gitlab_app/modules/issues/details/models/discussion/discussion_entity.dart';
import 'package:jihu_gitlab_app/modules/issues/details/models/note/note_entity.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label_entity.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/models/issue_draft_entity.dart';
import 'package:jihu_gitlab_app/modules/issues/member/member_entity.dart';
import 'package:jihu_gitlab_app/modules/projects/group_and_project.dart';
......@@ -10,7 +11,7 @@ import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DbManager {
static const int _version = 11;
static const int _version = 12;
static const String _name = "jihu.db";
static final DbManager _instance = DbManager._internal();
Database? _db;
......@@ -77,6 +78,9 @@ class DbManager {
case 11:
db.execute(MessageEntity.createTableSql);
break;
case 12:
db.execute(LabelEntity.createTableSql);
break;
default:
break;
}
......
......@@ -30,7 +30,7 @@ class AvatarAndName extends StatelessWidget {
margin: EdgeInsets.only(right: gap),
child: Avatar(avatarUrl: avatarUrl, size: avatarSize),
),
Text(username, style: nameStyle)
Flexible(child: Text(username, style: nameStyle))
],
);
}
......
import 'package:flutter/material.dart';
class LabelColorAndName extends StatelessWidget {
const LabelColorAndName({required this.colorValue, required this.name, Key? key}) : super(key: key);
final int colorValue;
final String name;
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(width: 16, height: 16, margin: const EdgeInsets.only(right: 12), decoration: BoxDecoration(color: Color(colorValue))),
Flexible(child: Text(name, style: const TextStyle(fontWeight: FontWeight.w400, fontSize: 16, color: Color(0XFF03162F))))
],
);
}
}
......@@ -3,9 +3,9 @@ import 'package:jihu_gitlab_app/core/widgets/tips_view.dart';
class SearchNoResultView extends StatelessWidget {
final String? message;
final Future<void> Function() onRefresh;
final Future<void> Function()? onRefresh;
const SearchNoResultView({super.key, required this.message, required this.onRefresh});
const SearchNoResultView({super.key, required this.message, this.onRefresh});
@override
Widget build(BuildContext context) {
......
......@@ -15,7 +15,7 @@ class ClosableView extends StatelessWidget {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
child,
Flexible(child: child),
InkWell(
onTap: onClosed,
child: Container(
......
import 'package:flutter/material.dart';
import 'package:jihu_gitlab_app/core/widgets/selector/data_provider.dart';
import 'package:jihu_gitlab_app/core/widgets/selector/selector.dart';
import 'package:jihu_gitlab_app/l10n/jihu_localizations.dart';
import 'closable_view.dart';
......@@ -13,23 +12,14 @@ typedef DataProviderProvider<T> = DataProvider<T> Function();
// TODO Rename
class Selectable<T> extends StatefulWidget {
final OnSelected<T> onSelected;
final SelectedItemBuilder<T> selectedItemBuilder;
final SelectorItemBuilder<T> selectorItemBuilder;
final DataProviderProvider<T> dataProviderProvider;
final KeyMapper<int, T> keyMapper;
final String placeHolder;
final Filter<T>? filter;
final MultiSelection? multiSelection;
final WarningNotice? warningNotice;
final List<T>? initialData;
const Selectable(
{required this.onSelected,
required this.selectedItemBuilder,
required this.selectorItemBuilder,
required this.dataProviderProvider,
required this.keyMapper,
required this.title,
required this.pageTitle,
required this.placeHolder,
this.filter,
this.multiSelection,
......@@ -38,6 +28,19 @@ class Selectable<T> extends StatefulWidget {
Key? key})
: super(key: key);
final OnSelected<T> onSelected;
final SelectedItemBuilder<T> selectedItemBuilder;
final SelectorItemBuilder<T> selectorItemBuilder;
final DataProviderProvider<T> dataProviderProvider;
final KeyMapper<int, T> keyMapper;
final String title;
final String pageTitle;
final String placeHolder;
final Filter<T>? filter;
final MultiSelection? multiSelection;
final WarningNotice? warningNotice;
final List<T>? initialData;
@override
State<Selectable> createState() => _SelectableState<T>();
}
......@@ -70,17 +73,17 @@ class _SelectableState<T> extends State<Selectable<T>> {
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(widget.placeHolder, style: const TextStyle(color: Color(0XFF03162F), fontSize: 16, fontWeight: FontWeight.w600)),
child: Text(widget.title, style: const TextStyle(color: Color(0XFF03162F), fontSize: 16, fontWeight: FontWeight.w600)),
),
_buildAssigneeSelector()
_buildSelector()
],
),
);
}
Widget _buildAssigneeSelector() {
Widget _buildSelector() {
var iconButton = IconButton(
key: const Key("goto-user-selector"),
key: const Key("gotoSelector"),
onPressed: () async {
bool multiSelection = await widget.multiSelection?.call() ?? false;
String warningNotice = await widget.warningNotice?.call() ?? '';
......@@ -96,7 +99,7 @@ class _SelectableState<T> extends State<Selectable<T>> {
icon: const Icon(Icons.add, size: 22, color: Color(0xFF87878C)));
final List<Widget> children = [];
if (hasNoData) {
children.add(Text(JiHuLocalizations.dictionary().assigneesPlaceholder, style: const TextStyle(color: Color(0XFF66696D), fontSize: 14, fontWeight: FontWeight.w400)));
children.add(Text(widget.placeHolder, style: const TextStyle(color: Color(0XFF66696D), fontSize: 14, fontWeight: FontWeight.w400)));
} else {
for (var e in _selectedData) {
children.add(_buildSelectedItem(e));
......@@ -125,7 +128,7 @@ class _SelectableState<T> extends State<Selectable<T>> {
dataProvider: widget.dataProviderProvider(),
itemBuilder: widget.selectorItemBuilder,
filter: widget.filter,
title: JiHuLocalizations.dictionary().selectAssignees,
title: widget.pageTitle,
multiSelection: multiSelection,
warningNotice: warningNotice,
keyMapper: widget.keyMapper,
......
......@@ -5,6 +5,7 @@ import 'package:jihu_gitlab_app/core/domain/url_redictor.dart';
import 'package:jihu_gitlab_app/core/widgets/common_app_bar.dart';
import 'package:jihu_gitlab_app/core/widgets/linker_text.dart';
import 'package:jihu_gitlab_app/core/widgets/search_box.dart';
import 'package:jihu_gitlab_app/core/widgets/search_no_result_view.dart';
import 'package:jihu_gitlab_app/core/widgets/selector/data_provider.dart';
import 'package:jihu_gitlab_app/l10n/jihu_localizations.dart';
......@@ -167,6 +168,10 @@ class _SelectorState<Identity, Domain> extends State<Selector<Identity, Domain>>
child: ValueListenableBuilder<List<Domain>>(
valueListenable: _notifier,
builder: (BuildContext context, List<Domain> data, Widget? child) {
String search = _textEditingController.text;
if (search.isNotEmpty && data.isEmpty) {
return SearchNoResultView(message: '${JiHuLocalizations.dictionary().noMatch}\'$search\'');
}
return ListView.separated(
itemBuilder: (context, index) => _buildListItem(index, data[index]),
separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 12, height: 12, color: Colors.white),
......@@ -185,8 +190,8 @@ class _SelectorState<Identity, Domain> extends State<Selector<Identity, Domain>>
return InkWell(
onTap: onItemTapped,
child: Container(
height: 32,
width: double.infinity,
constraints: const BoxConstraints(minHeight: 44),
margin: const EdgeInsets.only(left: 4),
child: Row(
children: <Widget>[
......@@ -208,7 +213,7 @@ class _SelectorState<Identity, Domain> extends State<Selector<Identity, Domain>>
),
),
)),
widget.itemBuilder.call(context, index, domain)
Flexible(child: widget.itemBuilder.call(context, index, domain))
],
),
),
......
......@@ -24,7 +24,7 @@ import 'package:jihu_gitlab_app/modules/community/models/community_project.dart'
import 'package:jihu_gitlab_app/modules/issues/details/discussion_view.dart';
import 'package:jihu_gitlab_app/modules/issues/details/models/discussion/discussion.dart';
import 'package:jihu_gitlab_app/modules/issues/details/models/issue_description_view.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/widgets/label.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label.dart';
import 'package:provider/provider.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
......
......@@ -2,7 +2,7 @@ import 'package:jihu_gitlab_app/core/domain/assignee/assignee.dart';
import 'package:jihu_gitlab_app/core/id.dart';
import 'package:jihu_gitlab_app/core/time.dart';
import 'package:jihu_gitlab_app/modules/issues/details/models/description_content.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/widgets/label.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label.dart';
import 'community_project.dart';
......
......@@ -9,9 +9,9 @@ import 'package:jihu_gitlab_app/core/log_helper.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/system_version_detector.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/models/issue_draft_entity.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/models/issue_draft_repository.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/widgets/label.dart';
import 'community_gq_request_body.dart';
import 'community_project.dart';
......
......@@ -5,7 +5,7 @@ import 'package:jihu_gitlab_app/core/log_helper.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/l10n/jihu_localizations.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/widgets/label.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label.dart';
import 'community_gq_request_body.dart';
......
......@@ -10,6 +10,7 @@ import 'package:jihu_gitlab_app/core/widgets/avatar_and_name.dart';
import 'package:jihu_gitlab_app/core/widgets/bottom_sheet_render.dart';
import 'package:jihu_gitlab_app/core/widgets/changeable_time.dart';
import 'package:jihu_gitlab_app/core/widgets/common_app_bar.dart';
import 'package:jihu_gitlab_app/core/widgets/label_color_and_name.dart';
import 'package:jihu_gitlab_app/core/widgets/label_view.dart';
import 'package:jihu_gitlab_app/core/widgets/loading_button.dart';
import 'package:jihu_gitlab_app/core/widgets/loading_view.dart';
......@@ -23,10 +24,10 @@ import 'package:jihu_gitlab_app/modules/issues/details/discussion_view.dart';
import 'package:jihu_gitlab_app/modules/issues/details/issue_details_param.dart';
import 'package:jihu_gitlab_app/modules/issues/details/models/discussion/discussion.dart';
import 'package:jihu_gitlab_app/modules/issues/details/models/issue_description_view.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label_provider.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/issue_update_page.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/models/assignee_selector_model.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/widgets/label.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/widgets/label_selector.dart';
import 'package:jihu_gitlab_app/modules/issues/member/member.dart';
import 'package:jihu_gitlab_app/modules/issues/member/member_provider.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
......@@ -218,21 +219,6 @@ class _IssueDetailsPageState extends State<IssueDetailsPage> {
);
}
Future<List<Member>?> _showSelector(bool multiSelection, String warningNotice, int projectId, List<Member> selectedAssignees) async {
var result = await Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return Selector<int, Member>(
dataProvider: MemberProvider(projectId: projectId),
itemBuilder: (BuildContext context, int index, Member member) => AvatarAndName(username: member.username, avatarUrl: member.avatarUrl),
filter: (keyword, e) => e.username.toUpperCase().contains(keyword.toUpperCase()),
title: JiHuLocalizations.dictionary().selectAssignees,
multiSelection: multiSelection,
warningNotice: warningNotice,
keyMapper: (member) => member.id,
selectedKeysProvider: () => selectedAssignees.map((t) => t.id).toList());
}));
return Future(() => result == null ? null : result as List<Member>);
}
Widget _buildIssueDetail() {
var issueInfo = _model.issueInfo!;
return Container(
......@@ -262,10 +248,9 @@ class _IssueDetailsPageState extends State<IssueDetailsPage> {
}
}),
_buildLabels(context, issueInfo, (selectedLabels) async {
var result = await Navigator.of(context).pushNamed(LabelSelector.routeName, arguments: {"projectId": _model.projectId, "selectedIds": selectedLabels.map((e) => e.id.id).toList()});
var result = await _showLabelsSelector(_model.projectId, selectedLabels);
if (result != null) {
List<Label> selectedLabels = result as List<Label>;
bool updateLabelsResult = await _model.updateLabels(selectedLabels.map((e) => e.name).toList());
bool updateLabelsResult = await _model.updateLabels(result.map((e) => e.name).toList());
if (updateLabelsResult) {
_refreshIssueInfo();
return;
......@@ -289,6 +274,35 @@ class _IssueDetailsPageState extends State<IssueDetailsPage> {
);
}
Future<List<Label>?> _showLabelsSelector(int projectId, List<Label> selectedLabels) async {
var result = await Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return Selector<int, Label>(
dataProvider: LabelProvider(projectId: projectId),
itemBuilder: (BuildContext context, int index, Label label) => LabelColorAndName(colorValue: label.formatColor(), name: label.name),
filter: (keyword, e) => e.name.toUpperCase().contains(keyword.toUpperCase()),
title: JiHuLocalizations.dictionary().selectLabels,
multiSelection: true,
keyMapper: (label) => label.id.id,
selectedKeysProvider: () => selectedLabels.map((t) => t.id.id).toList());
}));
return Future(() => result == null ? null : result as List<Label>);
}
Future<List<Member>?> _showSelector(bool multiSelection, String warningNotice, int projectId, List<Member> selectedAssignees) async {
var result = await Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return Selector<int, Member>(
dataProvider: MemberProvider(projectId: projectId),
itemBuilder: (BuildContext context, int index, Member member) => AvatarAndName(username: member.username, avatarUrl: member.avatarUrl),
filter: (keyword, e) => e.username.toUpperCase().contains(keyword.toUpperCase()),
title: JiHuLocalizations.dictionary().selectAssignees,
multiSelection: multiSelection,
warningNotice: warningNotice,
keyMapper: (member) => member.id,
selectedKeysProvider: () => selectedAssignees.map((t) => t.id).toList());
}));
return Future(() => result == null ? null : result as List<Member>);
}
Widget _buildIssueReplyMarkdown() {
return Align(
alignment: Alignment.bottomCenter,
......
......@@ -4,7 +4,7 @@ import 'package:jihu_gitlab_app/core/settings/settings.dart';
import 'package:jihu_gitlab_app/core/time.dart';
import 'package:jihu_gitlab_app/l10n/jihu_localizations.dart';
import 'package:jihu_gitlab_app/modules/issues/details/models/description_content.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/widgets/label.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label.dart';
class IssueInfo {
int id;
......
......@@ -2,7 +2,6 @@ export 'details/issue_details_page.dart';
export 'details/models/issue_details_model.dart';
export 'manage/issue_creation_page.dart';
export 'manage/widgets/description_template_selector.dart';
export 'manage/widgets/label_selector.dart';
export 'member/member.dart';
export 'member/member_entity.dart';
export 'member/member_provider.dart';
......
class LabelEntity {
static const tableName = "project_labels";
int? id;
int labelId;
int projectId;
String name;
String? description;
String? textColor;
String? color;
int version;
LabelEntity._internal(this.id, this.labelId, this.projectId, this.name, this.description, this.textColor, this.color, this.version);
static LabelEntity restore(Map<String, dynamic> map) {
return LabelEntity._internal(
map['id'],
map['label_id'],
map['project_id'],
map['name'],
map['description'],
map['textColor'],
map['color'],
map['version'],
);
}
static LabelEntity create(Map<String, dynamic> map, int projectId, int version) {
return LabelEntity._internal(
null,
map['id'] ?? '',
projectId,
map['name'] ?? '',
map['description'] ?? '',
map['text_color'] ?? '#FFFFFF',
map['color'] ?? '#FFFFFF',
version,
);
}
Map<String, dynamic> toMap() {
return {'id': id, 'label_id': labelId, 'project_id': projectId, 'name': name, 'description': description, 'textColor': textColor, 'color': color, 'version': version};
}
void update(LabelEntity entity) {
name = entity.name;
description = entity.description;
textColor = entity.textColor;
color = entity.color;
version = entity.version;
}
static String get createTableSql => """
create table $tableName(
id INTEGER PRIMARY KEY AUTOINCREMENT,
label_id INTEGER NOT NULL,
project_id INTEGER NOT NULL,
name TEXT NOT NULL,
description TEXT ,
textColor TEXT,
color TEXT,
version INTEGER
);
""";
@override
bool operator ==(Object other) =>
identical(this, other) || other is LabelEntity && runtimeType == other.runtimeType && labelId == other.labelId && name == other.name && description == other.description;
@override
int get hashCode => labelId.hashCode ^ name.hashCode ^ description.hashCode;
}
import 'package:jihu_gitlab_app/core/domain/global_time.dart';
import 'package:jihu_gitlab_app/core/list_comparator.dart';
import 'package:jihu_gitlab_app/core/log_helper.dart';
import 'package:jihu_gitlab_app/core/net/api.dart';
import 'package:jihu_gitlab_app/core/synchronizer.dart';
import 'package:jihu_gitlab_app/core/widgets/selector/data_provider.dart';
import 'label.dart';
import 'label_entity.dart';
import 'label_repository.dart';
class LabelProvider extends DataProvider<Label> {
int projectId;
late LabelRepository _labelRepository;
LabelProvider({required this.projectId}) {
_labelRepository = LabelRepository();
}
@override
Future<List<Label>> loadFromLocal() async {
try {
var list = await _labelRepository.queryByProject(projectId);
return list
.map((e) => Label.fromJson({
'id': e.labelId,
'name': e.name,
'description': e.description,
'text_color': e.textColor,
'color': e.color,
}))
.toList();
} catch (e) {
LogHelper.err("LabelProvider loadFromLocal error", e);
return [];
}
}
@override
Future<bool> syncFromRemote() {
int version = GlobalTime.now().millisecondsSinceEpoch;
return _getAllLabels(version);
}
Future<bool> _getAllLabels(int version) async {
return Synchronizer<dynamic>(
url: Api.join('/projects/$projectId/labels'),
dataProcessor: (data, page, pageSize) async {
List<LabelEntity> entities = (data as List).map((e) => LabelEntity.create(e, projectId, version)).toList(growable: false);
await _save(entities, version);
return entities.length >= pageSize;
}).run();
}
Future<void> _save(List<LabelEntity> entities, int version) async {
List<int> labelIds = entities.map((e) => e.labelId).toList();
List<LabelEntity> exists = await _labelRepository.query(projectId, labelIds);
ListCompareResult<LabelEntity> result = ListComparator.compare<LabelEntity>(exists, entities);
if (result.hasAdd) {
await _labelRepository.insert(result.add);
}
if (result.hasUpdate) {
var list = result.update.map((e) {
e.left.update(e.right);
return e.left;
}).toList();
await _labelRepository.update(list);
}
await _labelRepository.deleteLessThan(version, projectId);
}
}
import 'package:jihu_gitlab_app/core/db_manager.dart';
import 'package:sqflite/sqflite.dart';
import 'label_entity.dart';
class LabelRepository {
Future<List<LabelEntity>> query(int projectId, Iterable<int> labelIds) async {
String sql = "select * from ${LabelEntity.tableName} where project_id = $projectId and label_id in (${labelIds.join(", ")})";
var list = await DbManager.instance().rawFind(sql);
return list.map((e) => LabelEntity.restore(e)).toList();
}
Future<List<int>> insert(List<LabelEntity> entities) async {
// TODO: Cannot mock db.transaction, so add try catch for test
try {
return await DbManager.instance().batchInsert(LabelEntity.tableName, entities.map((e) => e.toMap()).toList());
} catch (e) {
return [];
}
}
Future<int> update(List<LabelEntity> entities) async {
return await DbManager.instance().batchUpdate(LabelEntity.tableName, entities.map((e) => e.toMap()).toList());
}
Future<List<LabelEntity>> queryByProject(int projectId) async {
Database database = await DbManager.instance().getDb();
List<Map<String, Object?>> query = await database.query(LabelEntity.tableName, where: " project_id = ? ", whereArgs: [projectId]);
return query.map((e) => LabelEntity.restore(e)).toList();
}
Future<int> deleteLessThan(int version, int projectId) async {
Database database = await DbManager.instance().getDb();
return await database.delete(LabelEntity.tableName, where: " project_id = ? and version < ? ", whereArgs: [projectId, version]);
}
}
......@@ -8,22 +8,22 @@ import 'package:jihu_gitlab_app/core/connection_provider/connection_provider.dar
import 'package:jihu_gitlab_app/core/loader.dart';
import 'package:jihu_gitlab_app/core/widgets/avatar_and_name.dart';
import 'package:jihu_gitlab_app/core/widgets/input_field_with_title.dart';
import 'package:jihu_gitlab_app/core/widgets/label_color_and_name.dart';
import 'package:jihu_gitlab_app/core/widgets/label_view.dart';
import 'package:jihu_gitlab_app/core/widgets/markdown/markdown_input_box.dart';
import 'package:jihu_gitlab_app/core/widgets/streaming_container.dart';
import 'package:jihu_gitlab_app/core/widgets/toast.dart';
import 'package:jihu_gitlab_app/l10n/jihu_localizations.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label.dart';
import 'package:jihu_gitlab_app/modules/issues/label/label_provider.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/issue_manage_page.dart';
import 'package:jihu_gitlab_app/modules/issues/manage/widgets/label.dart';
import 'package:jihu_gitlab_app/modules/issues/member/member.dart';
import 'package:jihu_gitlab_app/modules/issues/member/member_provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../../core/widgets/selector/selectable.dart';
import 'models/issue_creation_model.dart';
import 'widgets/closable_view.dart';
import 'widgets/description_template_selector.dart';
import 'widgets/label_selector.dart';
import 'widgets/selectable.dart';
part 'issue_manage_view.dart';
......
......@@ -72,8 +72,7 @@ class _IssueManageViewState extends State<IssueManageView> {
return isCommonIssue
? [
_buildAssigneeSelector(),
_buildTitle(JiHuLocalizations.dictionary().labels),
_buildSelectLabelView(),
_buildLabelSelector(),
_buildConfidentialSwitcher(),
]
: [];
......@@ -86,61 +85,19 @@ class _IssueManageViewState extends State<IssueManageView> {
);
}
Widget _buildSelectLabelView() {
var iconButton = IconButton(
key: const Key("goto-label-selector"),
onPressed: () async {
var result =
await Navigator.of(context).pushNamed(LabelSelector.routeName, arguments: {"projectId": widget.projectId, "selectedIds": widget.model.selectedLabels.map((e) => e.id.id).toSet()});
if (result != null) {
List<Label> selectedLabels = result as List<Label>;
setState(() {
widget.model.onLabelSelect(selectedLabels);
});
}
},
icon: const Icon(Icons.add, size: 22, color: Color(0xFF87878C)));
final List<Widget> children = [];
if (widget.model.selectedLabels.isEmpty) {
children.add(Text(JiHuLocalizations.dictionary().labelsPlaceholder, style: const TextStyle(color: Color(0XFF66696D), fontSize: 14, fontWeight: FontWeight.w400)));
} else {
for (var e in widget.model.selectedLabels) {
children.add(_buildLabelView(e));
}
}
children.add(Container(transform: Matrix4.translationValues(12, 0, 0), child: iconButton));
return InkWell(
onTap: () async {
var result = await Navigator.of(context).pushNamed(LabelSelector.routeName, arguments: {"projectId": widget.projectId, "selectedIds": widget.model.selectedLabels.map((e) => e.id.id).toSet()});
if (result != null) {
List<Label> selectedLabels = result as List<Label>;
setState(() {
widget.model.onLabelSelect(selectedLabels);
});
}
},
child: Container(
width: double.infinity,
decoration: const BoxDecoration(border: Border.fromBorderSide(BorderSide(color: Color(0XFFEAEAEA), width: 1)), borderRadius: BorderRadius.all(Radius.circular(4)), color: Colors.white),
padding: EdgeInsets.symmetric(vertical: widget.model.selectedLabels.isEmpty ? 0 : 8, horizontal: 8),
margin: const EdgeInsets.only(left: 16, right: 16),
child: Wrap(
spacing: 8,
runSpacing: 8,
alignment: widget.model.selectedLabels.isEmpty ? WrapAlignment.spaceBetween : WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
direction: Axis.horizontal,
children: children,
),
),
);
}
Widget _buildLabelView(Label label) {
return ClosableView(
child: LabelView(labelName: label.name, textColor: label.textColor, color: label.color),
onClosed: () => setState(() => widget.model.deleteSelectedLabel(label)),
);
Widget _buildLabelSelector() {
return Selectable<Label>(
onSelected: (labels) => widget.model.onLabelSelect(labels),
selectedItemBuilder: (label) => LabelView(labelName: label.name, textColor: label.textColor, color: label.color),
selectorItemBuilder: (BuildContext context, int index, Label label) => LabelColorAndName(colorValue: label.formatColor(), name: label.name),
dataProviderProvider: () => LabelProvider(projectId: widget.projectId),
keyMapper: (label) => label.id.id,
title: JiHuLocalizations.dictionary().labels,
pageTitle: JiHuLocalizations.dictionary().labelsPlaceholder,
placeHolder: JiHuLocalizations.dictionary().labelsPlaceholder,
multiSelection: () => Future(() => true),
initialData: widget.model.selectedLabels,
filter: (keyword, e) => e.name.toUpperCase().contains(keyword.toUpperCase()));
}
Widget _buildAssigneeSelector() {
......@@ -150,7 +107,9 @@ class _IssueManageViewState extends State<IssueManageView> {
selectorItemBuilder: (BuildContext context, int index, Member member) => AvatarAndName(username: member.username, avatarUrl: member.avatarUrl),
dataProviderProvider: () => MemberProvider(projectId: widget.projectId),
keyMapper: (member) => member.id,
placeHolder: JiHuLocalizations.dictionary().assignees,
title: JiHuLocalizations.dictionary().assignees,
pageTitle: JiHuLocalizations.dictionary().selectAssignees,
placeHolder: JiHuLocalizations.dictionary().assigneesPlaceholder,
multiSelection: () => widget.model.isMultiAssignees(),
warningNotice: () => Future(() => widget.model.warningNoticeWhenNoLicense()),
initialData: widget.model.selectedAssignees,
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
想要评论请 注册