Пачка экранов и логики #3

Merged
Dimkov966 merged 8 commits from feature/collection into develop 2025-04-02 20:13:12 +00:00
13 changed files with 194 additions and 1231 deletions
Showing only changes of commit 588c026f27 - Show all commits

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

View File

@@ -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);
}
}
} }

View File

@@ -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,
),
); );
} }

View File

@@ -1,23 +1,74 @@
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(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
const DialogHeader(title: 'Переместить карточку'), const DialogHeader(title: 'Переместить карточку'),
@@ -29,7 +80,7 @@ class ReplaceDialog extends StatelessWidget {
children: <Widget>[ children: <Widget>[
AppTypography('из коллекции', type: Medium16px()), AppTypography('из коллекции', type: Medium16px()),
const HSpace(8), const HSpace(8),
_buildCollection(context), _buildCollection(widget.currentCollection),
const HSpace(16), const HSpace(16),
Center( Center(
child: SizedBox.square( child: SizedBox.square(
@@ -51,45 +102,31 @@ class ReplaceDialog extends StatelessWidget {
const HSpace(16), const HSpace(16),
AppTypography('в коллекцию', type: Medium16px()), AppTypography('в коллекцию', type: Medium16px()),
const HSpace(8), const HSpace(8),
_buildCollection(context), _buildCollection(_collectionToTransfer, isSelectable: true),
const HSpace(16), const HSpace(16),
_createBtn(context), _buildTransferButton(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,8 +134,20 @@ 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(),
),
),
), ),
), ),
); );

View File

@@ -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);
} }

View File

@@ -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,
);
}
} }