Пачка экранов и логики #3
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4,7 +4,6 @@ import 'package:remever/common/functions.dart';
|
|||||||
import 'package:remever/database/database.dart';
|
import 'package:remever/database/database.dart';
|
||||||
import 'package:remever/database/tables.dart';
|
import 'package:remever/database/tables.dart';
|
||||||
import 'package:remever/models/create_ticket_dto.dart';
|
import 'package:remever/models/create_ticket_dto.dart';
|
||||||
import 'package:remever/models/crud_collection_dto.dart';
|
|
||||||
|
|
||||||
part 'tickets_dao.g.dart';
|
part 'tickets_dao.g.dart';
|
||||||
|
|
||||||
@@ -29,9 +28,11 @@ class TicketsDao extends DatabaseAccessor<AppDatabase> with _$TicketsDaoMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Удаление билета
|
/// Удаление билета
|
||||||
Future<void> deleteTicket(String id) async {
|
Future<void> removeTicket(String id) async {
|
||||||
try {
|
try {
|
||||||
await db.managers.tickets.filter((f) => f.id(id)).delete();
|
await db.transaction(() {
|
||||||
|
return db.managers.tickets.filter((f) => f.id(id)).delete();
|
||||||
|
});
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
logger.logError('Ошибка в методе deleteTicket', e, st);
|
logger.logError('Ошибка в методе deleteTicket', e, st);
|
||||||
}
|
}
|
||||||
@@ -67,4 +68,15 @@ class TicketsDao extends DatabaseAccessor<AppDatabase> with _$TicketsDaoMixin {
|
|||||||
logger.logError('Ошибка в методе createTicket', e, st);
|
logger.logError('Ошибка в методе createTicket', e, st);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Перенос билета с одной коллекции в другую
|
||||||
|
Future<void> transferTicket(String ticketId, String newCollectionId) async {
|
||||||
|
try {
|
||||||
|
await db.managers.tickets
|
||||||
|
.filter((f) => f.id(ticketId))
|
||||||
|
.update((o) => o(collectionId: Value<String>(newCollectionId)));
|
||||||
|
} catch (e, st) {
|
||||||
|
logger.logError('Ошибка в методе transferTicket', e, st);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import 'package:remever/components/extensions/context.dart';
|
|||||||
import 'package:remever/components/extensions/state.dart';
|
import 'package:remever/components/extensions/state.dart';
|
||||||
import 'package:remever/database/database.dart';
|
import 'package:remever/database/database.dart';
|
||||||
import 'package:remever/gen/assets.gen.dart';
|
import 'package:remever/gen/assets.gen.dart';
|
||||||
|
import 'package:remever/inject.dart';
|
||||||
import 'package:remever/screens/dialogs/alert_dialog.dart';
|
import 'package:remever/screens/dialogs/alert_dialog.dart';
|
||||||
import 'package:remever/screens/dialogs/replace_diaog.dart';
|
import 'package:remever/screens/dialogs/replace_diaog.dart';
|
||||||
|
import 'package:remever/services/tickets/tickets_interface.dart';
|
||||||
|
|
||||||
class TicketCard extends StatefulWidget {
|
class TicketCard extends StatefulWidget {
|
||||||
const TicketCard({
|
const TicketCard({
|
||||||
@@ -31,8 +33,8 @@ class TicketCard extends StatefulWidget {
|
|||||||
class _TicketCardState extends State<TicketCard> {
|
class _TicketCardState extends State<TicketCard> {
|
||||||
bool _isRolled = false;
|
bool _isRolled = false;
|
||||||
|
|
||||||
void _onDeleteTap() {
|
void _onDeleteTap() async {
|
||||||
showCuperModalBottomSheet(
|
final bool? res = await showCuperModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
height: 262.h,
|
height: 262.h,
|
||||||
builder:
|
builder:
|
||||||
@@ -42,14 +44,22 @@ class _TicketCardState extends State<TicketCard> {
|
|||||||
declineTitle: 'Нет, оставить',
|
declineTitle: 'Нет, оставить',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (res != null && res) {
|
||||||
|
await getIt<TicketsInterface>().removeTicket(widget.ticket.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onReplaceTap() {
|
void _onReplaceTap() {
|
||||||
showCuperModalBottomSheet(
|
showCuperModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
height: 394.h,
|
height: 420.h,
|
||||||
backgroundColor: AppColors.gray_bg,
|
backgroundColor: AppColors.gray_bg,
|
||||||
builder: (_) => const ReplaceDialog(),
|
builder:
|
||||||
|
(_) => ReplaceDialog(
|
||||||
|
currentCollection: widget.currentCollection,
|
||||||
|
ticket: widget.ticket,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,95 +1,132 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:remever/common/functions.dart';
|
||||||
import 'package:remever/common/resources.dart';
|
import 'package:remever/common/resources.dart';
|
||||||
|
import 'package:remever/common/utils.dart';
|
||||||
import 'package:remever/common/widgets/typography.dart';
|
import 'package:remever/common/widgets/typography.dart';
|
||||||
|
import 'package:remever/common/widgets/w_if.dart';
|
||||||
import 'package:remever/common/widgets/wspace.dart';
|
import 'package:remever/common/widgets/wspace.dart';
|
||||||
import 'package:remever/components/extensions/context.dart';
|
import 'package:remever/components/extensions/context.dart';
|
||||||
|
import 'package:remever/components/extensions/state.dart';
|
||||||
|
import 'package:remever/database/database.dart';
|
||||||
import 'package:remever/gen/assets.gen.dart';
|
import 'package:remever/gen/assets.gen.dart';
|
||||||
|
import 'package:remever/i18n/strings.g.dart';
|
||||||
|
import 'package:remever/inject.dart';
|
||||||
import 'package:remever/router.gr.dart';
|
import 'package:remever/router.gr.dart';
|
||||||
import 'package:remever/screens/collections/widgets/collection_progress_bar.dart';
|
import 'package:remever/screens/collections/widgets/collection_progress_bar.dart';
|
||||||
import 'package:remever/screens/dialogs/dialog_header.dart';
|
import 'package:remever/screens/dialogs/dialog_header.dart';
|
||||||
|
import 'package:remever/services/tickets/tickets_interface.dart';
|
||||||
|
import 'package:remever/widgets/primary_button.dart';
|
||||||
|
|
||||||
class ReplaceDialog extends StatelessWidget {
|
class ReplaceDialog extends StatefulWidget {
|
||||||
const ReplaceDialog({super.key});
|
const ReplaceDialog({
|
||||||
|
super.key,
|
||||||
|
required this.currentCollection,
|
||||||
|
required this.ticket,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Collection currentCollection;
|
||||||
|
final Ticket ticket;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ReplaceDialog> createState() => _ReplaceDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReplaceDialogState extends State<ReplaceDialog> {
|
||||||
|
Collection? _collectionToTransfer;
|
||||||
|
|
||||||
|
void _onCollectionTap() {
|
||||||
|
context.pushRoute(
|
||||||
|
CollectionSearchRoute(
|
||||||
|
onCollectionSelect: (collection) {
|
||||||
|
safeSetState(() => _collectionToTransfer = collection);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onTransferTap() async {
|
||||||
|
if (_collectionToTransfer == null) {
|
||||||
|
showErrorToast('Необходимо выбрать место для переноса');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_collectionToTransfer!.id == widget.currentCollection.id) {
|
||||||
|
showErrorToast('Карточка итак в этой коллекции, выберете другую');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await getIt<TicketsInterface>().transferTicket(
|
||||||
|
widget.ticket.id,
|
||||||
|
_collectionToTransfer!.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Material(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
const DialogHeader(title: 'Переместить карточку'),
|
children: <Widget>[
|
||||||
const HSpace(16),
|
const DialogHeader(title: 'Переместить карточку'),
|
||||||
Padding(
|
const HSpace(16),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 28).r,
|
Padding(
|
||||||
child: Column(
|
padding: const EdgeInsets.symmetric(horizontal: 28).r,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
AppTypography('из коллекции', type: Medium16px()),
|
children: <Widget>[
|
||||||
const HSpace(8),
|
AppTypography('из коллекции', type: Medium16px()),
|
||||||
_buildCollection(context),
|
const HSpace(8),
|
||||||
const HSpace(16),
|
_buildCollection(widget.currentCollection),
|
||||||
Center(
|
const HSpace(16),
|
||||||
child: SizedBox.square(
|
Center(
|
||||||
dimension: 34.r,
|
child: SizedBox.square(
|
||||||
child: DecoratedBox(
|
dimension: 34.r,
|
||||||
decoration: const BoxDecoration(
|
child: DecoratedBox(
|
||||||
shape: BoxShape.circle,
|
decoration: const BoxDecoration(
|
||||||
color: AppColors.gray,
|
shape: BoxShape.circle,
|
||||||
),
|
color: AppColors.gray,
|
||||||
child: Center(
|
),
|
||||||
child: Assets.icons.typeArrowDown.image(
|
child: Center(
|
||||||
height: 18.h,
|
child: Assets.icons.typeArrowDown.image(
|
||||||
width: 18.w,
|
height: 18.h,
|
||||||
|
width: 18.w,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const HSpace(16),
|
||||||
const HSpace(16),
|
AppTypography('в коллекцию', type: Medium16px()),
|
||||||
AppTypography('в коллекцию', type: Medium16px()),
|
const HSpace(8),
|
||||||
const HSpace(8),
|
_buildCollection(_collectionToTransfer, isSelectable: true),
|
||||||
_buildCollection(context),
|
const HSpace(16),
|
||||||
const HSpace(16),
|
_buildTransferButton(context),
|
||||||
_createBtn(context),
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _createBtn(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
// context.read<HomeCubit>().toCrudCollection(CrudType.CREATE);
|
|
||||||
context.pushRoute(CrudCollectionRoute());
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
SizedBox.square(
|
|
||||||
dimension: 20.r,
|
|
||||||
child: DecoratedBox(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: AppColors.gray,
|
|
||||||
),
|
|
||||||
child: Assets.icons.typePlus.image(color: Colors.black54),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const WSpace(4),
|
|
||||||
AppTypography('Создать и перенести в новую', type: Regular16px()),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCollection(BuildContext context) {
|
Widget _buildTransferButton(BuildContext context) {
|
||||||
|
return PrimaryButton(
|
||||||
|
child: AppTypography(
|
||||||
|
'Перенести',
|
||||||
|
type: Regular14px(),
|
||||||
|
color: AppColors.white,
|
||||||
|
),
|
||||||
|
onTap: () => _onTransferTap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCollection(Collection? collection, {bool isSelectable = false}) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {},
|
onTap: isSelectable ? () => _onCollectionTap() : null,
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(minHeight: 66.h, maxHeight: 84.h),
|
constraints: BoxConstraints(minHeight: 66.h, maxHeight: 84.h),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -97,9 +134,21 @@ class ReplaceDialog extends StatelessWidget {
|
|||||||
color: AppColors.white,
|
color: AppColors.white,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8).r,
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8).r,
|
||||||
child: Row(
|
child:
|
||||||
children: <Widget>[_buildAvatar(), const WSpace(5), _buildInfo()],
|
collection == null
|
||||||
),
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
AppTypography('Выберите коллекцию', type: Bold14px()),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
_buildAvatar(collection),
|
||||||
|
const WSpace(5),
|
||||||
|
_buildInfo(collection),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -107,17 +156,17 @@ class ReplaceDialog extends StatelessWidget {
|
|||||||
///
|
///
|
||||||
/// Построение основной информации
|
/// Построение основной информации
|
||||||
///
|
///
|
||||||
Widget _buildInfo() {
|
Widget _buildInfo(Collection collection) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 230.w,
|
width: 230.w,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buildTitle(),
|
_buildTitle(collection),
|
||||||
const HSpace(4),
|
const HSpace(4),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: [
|
||||||
Assets.icons.typeCards.image(
|
Assets.icons.typeCards.image(
|
||||||
height: 18.h,
|
height: 18.h,
|
||||||
width: 18.w,
|
width: 18.w,
|
||||||
@@ -125,7 +174,7 @@ class ReplaceDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const WSpace(2),
|
const WSpace(2),
|
||||||
AppTypography(
|
AppTypography(
|
||||||
Random().nextInt(654).toString(),
|
'${collection.likesCount.toString()} ${Utils.declOfNum(collection.likesCount, ['карточек', 'карточки', 'карточек'])}',
|
||||||
type: Regular14px(),
|
type: Regular14px(),
|
||||||
color: AppColors.disabled,
|
color: AppColors.disabled,
|
||||||
),
|
),
|
||||||
@@ -141,9 +190,9 @@ class ReplaceDialog extends StatelessWidget {
|
|||||||
///
|
///
|
||||||
/// Название коллекции
|
/// Название коллекции
|
||||||
///
|
///
|
||||||
Widget _buildTitle() {
|
Widget _buildTitle(Collection collection) {
|
||||||
return AppTypography(
|
return AppTypography(
|
||||||
'Астрономия',
|
collection.title,
|
||||||
type: Medium16px(),
|
type: Medium16px(),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
@@ -153,13 +202,24 @@ class ReplaceDialog extends StatelessWidget {
|
|||||||
///
|
///
|
||||||
/// Обложка коллекции
|
/// Обложка коллекции
|
||||||
///
|
///
|
||||||
Widget _buildAvatar() {
|
Widget _buildAvatar(Collection collection) {
|
||||||
return SizedBox.square(
|
return SizedBox.square(
|
||||||
dimension: 50.r,
|
dimension: 50.r,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(shape: BoxShape.circle, color: AppColors.bg),
|
||||||
shape: BoxShape.circle,
|
child: Wif(
|
||||||
image: DecorationImage(image: Assets.images.img.provider()),
|
condition: collection.image != null,
|
||||||
|
builder:
|
||||||
|
(context) => ClipOval(
|
||||||
|
child: Image.memory(collection.image!, fit: BoxFit.cover),
|
||||||
|
),
|
||||||
|
fallback:
|
||||||
|
(context) => Center(
|
||||||
|
child: AppTypography(
|
||||||
|
collection.title.substring(0, 1),
|
||||||
|
type: Bold34px(),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,4 +10,10 @@ abstract interface class TicketsInterface {
|
|||||||
|
|
||||||
/// Создание нового билета
|
/// Создание нового билета
|
||||||
Future<void> createTicket(CreateTicketDto dto);
|
Future<void> createTicket(CreateTicketDto dto);
|
||||||
|
|
||||||
|
/// Удаление нового билета
|
||||||
|
Future<void> removeTicket(String ticketId);
|
||||||
|
|
||||||
|
/// Перенос билета в другую коллекцию
|
||||||
|
Future<void> transferTicket(String ticketId, String newCollectionId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,4 +19,17 @@ final class TicketsService implements TicketsInterface {
|
|||||||
Future<void> createTicket(CreateTicketDto dto) async {
|
Future<void> createTicket(CreateTicketDto dto) async {
|
||||||
return await getIt<AppDatabase>().ticketsDao.createTicket(dto);
|
return await getIt<AppDatabase>().ticketsDao.createTicket(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> removeTicket(String ticketId) async {
|
||||||
|
return await getIt<AppDatabase>().ticketsDao.removeTicket(ticketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> transferTicket(String ticketId, String newCollectionId) async {
|
||||||
|
return await getIt<AppDatabase>().ticketsDao.transferTicket(
|
||||||
|
ticketId,
|
||||||
|
newCollectionId,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user