import 'package:badges/badges.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dash/flutter_dash.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_wisetronic/dialog/image_viewer.dart'; import 'package:flutter_wisetronic/events/eventbus.dart'; import 'package:flutter_wisetronic/events/events.dart'; import 'package:flutter_wisetronic/widgets/general/text_link.dart'; import 'package:percent_indicator/percent_indicator.dart'; import '../../constants.dart'; import '../../generated/l10n.dart'; import '../../models/ticket.dart'; import '../../models/user.dart'; import '../../routes.dart'; import '../../store/actions.dart'; import '../../store/store.dart'; import '../../utils/http_util.dart'; import '../../utils/utils.dart'; import '../../utils/util_web.dart' if (dart.library.io) '../../utils/util_io.dart'; class MobileViewTicket extends StatefulWidget { final Key key; final int ticketId; const MobileViewTicket(this.ticketId, {this.key}) : super(key: key); @override State createState() { return MobileViewTicketState(); } } class MobileViewTicketState extends State { final GlobalKey _formKey = GlobalKey(); Ticket ticket; final issueMsgController = TextEditingController(); bool onSubmitting = false; double submitProgress = 0.0; final List> galleryImagesFlag = [ {'show': false, 'progress': 0.0, 'path': ''}, {'show': false, 'progress': 0.0, 'path': ''}, {'show': false, 'progress': 0.0, 'path': ''}, ]; @override void initState() { super.initState(); onSubmitting = false; _loadTicket(); } void _loadTicket() { HttpUtil.httpGet( 'v1/get-ticket/${widget.ticketId}', businessId: Constants.BUSINESS_ID, ).then((value) { if (mounted) { setState(() { ticket = Ticket.fromJson(value); print('ticket: $ticket'); }); } }).catchError((error) { Utils.showMessageDialog(context, error, onOk: () { Routes.router.pop(context); Routes.router.pop(context); }); }); } @override Widget build(BuildContext context) { store.dispatch(UpdateContext(context)); BuildContext mainContext = context; if (ticket == null) { return Container( padding: EdgeInsets.all(50.0), child: Center( child: SpinKitThreeBounce( color: Colors.lightBlueAccent, size: 40.0, ), ), ); } Column view = Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: EdgeInsets.only(left: 16.0, top: 16.0, right: 16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( S.of(context).ticket_number_token(ticket.id), style: TextStyle( fontSize: 24.0, color: Colors.black ), ), Text( Utils.utcDatetimeStringToLocalDatetimeString(context, ticket.createdAt), style: TextStyle( fontSize: 14.0, color: Colors.black38, ), ) ], ), ), Container( width: double.maxFinite, padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 24.0), child: Text( '${ticket.issue.msg}', style: TextStyle( color: Colors.black54, fontSize: 14.0, ), ), decoration: BoxDecoration( border: Border( bottom: BorderSide( width: 0.5, color: Colors.black12, ), ), ), ), Container( width: double.maxFinite, padding: EdgeInsets.only(top: 16.0, bottom: 16.0, left: 16.0, right: 16.0), child: showGalleryImages(mainContext, ticket.issue.files), decoration: BoxDecoration( border: Border( bottom: BorderSide( width: 0.5, color: Colors.black45, ), ), ), ), Container( width: double.maxFinite, height: 10.0, padding: EdgeInsets.only(bottom: 10.0), decoration: BoxDecoration( border: Border( top: BorderSide( width: 8, color: Colors.black26, ), bottom: BorderSide( width: 0.5, color: Colors.white70, ), ), ), ), ], ); if (ticket.followUps.length > 0) { view.children.add( Container( width: double.maxFinite, padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), child: Text( S.of(context).follow_ups, style: TextStyle( fontSize: 20.0, fontWeight: FontWeight.bold, ), ), ), ); for (int i = 0; i < ticket.followUps.length; i++) { FollowUp followUp = ticket.followUps[i]; view.children.add( Container( width: double.maxFinite, padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${followUp.poster}', style: TextStyle( fontSize: 14.0, fontWeight: FontWeight.bold, color: Colors.black38, ), ), Text( Utils.utcDatetimeStringToLocalDatetimeString( context, followUp.createdAt ), style: TextStyle( fontSize: 14.0, color: Colors.black38, ), ), ], ), decoration: BoxDecoration( border: Border( bottom: BorderSide( width: 0.5, color: Colors.black12, ), ), ), ), Container( width: double.maxFinite, padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0), child: Text( '${followUp.msg}', style: TextStyle( color: Colors.black87, ), ), ), Container( padding: EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0, bottom: 20.0), child: showGalleryImages( mainContext, followUp.files, ), ), ], ), ), ); } } Widget f = Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0), child: Text( S.of(context).your_reply, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20.0, ), ), ), Container( padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0), child: TextFormField( controller: issueMsgController, keyboardType: TextInputType.multiline, textCapitalization: TextCapitalization.sentences, decoration: new InputDecoration( enabledBorder: OutlineInputBorder( borderSide: BorderSide( width: 0.5, color: Colors.black12, ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( width: 1.0, color: Colors.blue, ), ), labelText: S.of(context).your_reply, ), style: TextStyle( fontSize: 18.0 ), autofocus: false, validator: (String value) { if (value.trim().isEmpty) { return S.of(context).this_field_is_required; } return null; }, maxLines: 5, maxLength: 1000, ), ), Container( padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 4.0), child: Text( S.of(context).attach_pictures, style: TextStyle( fontSize: 17.0, color: Colors.black87, fontWeight: FontWeight.bold, ), ), ), Container( padding: EdgeInsets.only(top: 4.0, left: 16.0, right: 16.0, bottom: 8.0), child: Text( S.of(context).attach_pictures_desc, style: TextStyle( fontSize: 14.0, color: Colors.black38, ), ), ), Container( padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0, bottom: 16.0), child: galleryImages(mainContext), ), ], ), ); Widget s = Align( alignment: Alignment.centerRight, child: Container( padding: EdgeInsets.only(top: 20.0, left: 16.0, right: 16.0, bottom: 20.0), child: RaisedButton( color: Theme.of(context).primaryColor, child: Text( S.of(context).reply, style: TextStyle( color: Colors.white, ), ), onPressed: () { _submit(); }, ), ), ); if (ticket.followUps.length > 0) { view.children.add( Container( width: double.maxFinite, height: 10.0, padding: EdgeInsets.only(bottom: 10.0), decoration: BoxDecoration( border: Border( top: BorderSide( width: 8, color: Colors.black26, ), bottom: BorderSide( width: 0.5, color: Colors.white70, ), ), ), ), ); } if (ticket.isClosed) { view.children.add( Container( padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0), child: Text( S.of(context).the_ticket_is_closed_desc, style: TextStyle( fontSize: 15.0, color: Colors.black87, ), ), ), ); view.children.add( Container( padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 0.0, bottom: 30.0), child: TextLink( S.of(context).new_ticket, '/new-ticket/${ticket.store.id}', ), ), ); } else { view.children.add( f, ); view.children.add( s, ); } return SingleChildScrollView( child: Stack( children: [ Visibility( visible: onSubmitting, child: Container( height: MediaQuery.of(context).size.height - MediaQuery.of(context).padding.bottom - MediaQuery.of(context).padding.top, color: Colors.grey.withOpacity(0.6), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ CircularProgressIndicator( strokeWidth: 10, backgroundColor: Colors.green, valueColor: new AlwaysStoppedAnimation(Colors.red), value: submitProgress, ), Container( margin: EdgeInsets.only(top: 16.0), child: Text( S.of(context).submitting_please_wait, style: TextStyle( color: Colors.white, ), ), ), ], ), ), ), ), Visibility( visible: !onSubmitting, child: view, ), ], ), ); } Widget showGalleryImages(BuildContext mainContext, List files) { if (files.length <= 0) { return SizedBox.shrink(); } Row row = Row( mainAxisAlignment: MainAxisAlignment.start, children: [], ); for (int i = 0; i < files.length; i++) { TicketFile image = files[i]; Widget imageWidget; imageWidget = GestureDetector( child: Container( margin: EdgeInsets.only(right: 20.0), child: Util.showImage( 'https:${image.url}', width: 80.0, height: 80.0, fit: BoxFit.fill ), decoration: BoxDecoration( border: Border( top: BorderSide( width: 0.5, color: Colors.black12, ), bottom: BorderSide( width: 0.5, color: Colors.black12, ), left: BorderSide( width: 0.5, color: Colors.black12, ), right: BorderSide( width: 0.5, color: Colors.black12, ), ), ), ), onTap: () { showDialog( context: mainContext, builder: (BuildContext context) { return AlertDialog( title: Text('${image.fileName}'), content: ImageViewer( NetworkImage( 'https:${image.url}', ), ), actions: [ TextButton( onPressed: () { Routes.router.pop(context); }, child: Text(S.of(context).close) ), ], ); }, ); }, ); row.children.add(imageWidget); } return row; } Widget galleryImages(BuildContext mainContext) { Row row = Row( mainAxisAlignment: MainAxisAlignment.start, children: [], ); for (int i = 0; i < 3; i++) { Map image = galleryImagesFlag[i]; Widget imageWidget; imageWidget = GestureDetector( child: Stack( alignment: Alignment.center, children: [ Container( margin: EdgeInsets.only(right: 20.0), child: Util.showImage( '${image['path']}', width: 80.0, height: 80.0, fit: BoxFit.fill ), decoration: BoxDecoration( border: Border( top: BorderSide( width: 0.5, color: Colors.black12, ), bottom: BorderSide( width: 0.5, color: Colors.black12, ), left: BorderSide( width: 0.5, color: Colors.black12, ), right: BorderSide( width: 0.5, color: Colors.black12, ), ), ), ), Visibility( visible: galleryImagesFlag[i]['show'], child: Container( padding: EdgeInsets.only(right: 20.0), child: CircularPercentIndicator( radius: 60.0, lineWidth: 10.0, percent: galleryImagesFlag[i]['progress'], center: new Text( '${(galleryImagesFlag[i]['progress'] * 100.0).toStringAsFixed(1)}%', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15.0, color: Colors.white, ), ), circularStrokeCap: CircularStrokeCap.round, progressColor: Colors.orange, ), ), ), ], ), onTap: () { showDialog( context: mainContext, builder: (BuildContext context) { FocusScopeNode currentFocus = FocusScope.of(context); if (!currentFocus.hasPrimaryFocus) { currentFocus.unfocus(); } return Util().getPicture2(mainContext, i, (int imageId, String path){ setState(() { galleryImagesFlag[imageId]['path'] = path; }); }); return null; } ); }, ); row.children.add(Badge( position: BadgePosition.topEnd(top: -10.0, end: 10.0), badgeContent: Container( child: GestureDetector( child: Icon( Icons.clear, color: Colors.white, size: 14.0, ), onTap: () { _deleteImage(mainContext, i); }, ), ), showBadge: image['path'].isNotEmpty ? true : false, child: imageWidget, )); } return row; } void _deleteImage(BuildContext mainContext, int imageId) { showDialog(context: mainContext, builder: (BuildContext context) { return AlertDialog( title: Text(S.of(context).warning), content: Text(S.of(context).are_you_sure_to_remove_the_picture), actions: [ FlatButton( child: Text(S.of(context).cancel), onPressed: () { Navigator.of(context).pop(); }, ), RaisedButton( child: Text(S.of(context).yes_i_am_sure), color: Theme.of(context).primaryColor, onPressed: () { Navigator.of(context).pop(); setState(() { galleryImagesFlag[imageId]['path'] = ''; }); }, ), ], ); }, ); } @override void didChangeDependencies() { super.didChangeDependencies(); eventBus.on().listen((event) { if (mounted) { setState(() { onSubmitting = event.showProgress; submitProgress = event.progress; }); } }); } void _submit() { final FormState form = _formKey.currentState; if (form.validate()) { setState(() { onSubmitting = true; }); Util().createTicket(context, issueMsgController.text.trim(), galleryImagesFlag, (response){ showDialog(context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text(S.of(context).success), content: Text(S.of(context).ticket_created_success), actions: [ FlatButton( child: Text(S.of(context).ok), onPressed: () { Routes.router.navigateTo(context, '/my-support/${ticket.store.id}', replace: true, ); }, ), ], ); }); }, (error) { Utils.showMessageDialog(context, error, onOk: () { Routes.router.pop(context); Routes.router.pop(context); }); }, id: widget.ticketId); } } }