mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-11-04 07:15:46 +00:00 
			
		
		
		
	* Remove unused lib/generated/i18n.dart * Use `fvm dart format .` * Add contributing guidelines * Enforce dart format * Add `dart format off` directive to generated files
		
			
				
	
	
		
			309 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import "package:flutter/material.dart";
 | 
						|
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
 | 
						|
import "package:one_context/one_context.dart";
 | 
						|
 | 
						|
import "package:inventree/api.dart";
 | 
						|
import "package:inventree/helpers.dart";
 | 
						|
import "package:inventree/l10.dart";
 | 
						|
 | 
						|
import "package:inventree/preferences.dart";
 | 
						|
import "package:inventree/widget/snacks.dart";
 | 
						|
 | 
						|
/*
 | 
						|
 * Launch a dialog allowing the user to select from a list of options
 | 
						|
 */
 | 
						|
Future<void> choiceDialog(
 | 
						|
  String title,
 | 
						|
  List<Widget> items, {
 | 
						|
  Function? onSelected,
 | 
						|
}) async {
 | 
						|
  List<Widget> choices = [];
 | 
						|
 | 
						|
  for (int idx = 0; idx < items.length; idx++) {
 | 
						|
    choices.add(
 | 
						|
      GestureDetector(
 | 
						|
        child: items[idx],
 | 
						|
        onTap: () {
 | 
						|
          OneContext().popDialog();
 | 
						|
          if (onSelected != null) {
 | 
						|
            onSelected(idx);
 | 
						|
          }
 | 
						|
        },
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  if (!hasContext()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  OneContext().showDialog(
 | 
						|
    builder: (BuildContext context) {
 | 
						|
      return AlertDialog(
 | 
						|
        title: Text(title),
 | 
						|
        content: SingleChildScrollView(child: Column(children: choices)),
 | 
						|
        actions: [
 | 
						|
          TextButton(
 | 
						|
            child: Text(L10().cancel),
 | 
						|
            onPressed: () {
 | 
						|
              Navigator.pop(context);
 | 
						|
            },
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      );
 | 
						|
    },
 | 
						|
  );
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Display a "confirmation" dialog allowing the user to accept or reject an action
 | 
						|
 */
 | 
						|
Future<void> confirmationDialog(
 | 
						|
  String title,
 | 
						|
  String text, {
 | 
						|
  Color? color,
 | 
						|
  IconData icon = TablerIcons.help_circle,
 | 
						|
  String? acceptText,
 | 
						|
  String? rejectText,
 | 
						|
  Function? onAccept,
 | 
						|
  Function? onReject,
 | 
						|
}) async {
 | 
						|
  String _accept = acceptText ?? L10().ok;
 | 
						|
  String _reject = rejectText ?? L10().cancel;
 | 
						|
 | 
						|
  if (!hasContext()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  OneContext().showDialog(
 | 
						|
    builder: (BuildContext context) {
 | 
						|
      return AlertDialog(
 | 
						|
        iconColor: color,
 | 
						|
        title: ListTile(
 | 
						|
          title: Text(title, style: TextStyle(color: color)),
 | 
						|
          leading: Icon(icon, color: color),
 | 
						|
        ),
 | 
						|
        content: text.isEmpty ? Text(text) : null,
 | 
						|
        actions: [
 | 
						|
          TextButton(
 | 
						|
            child: Text(_reject),
 | 
						|
            onPressed: () {
 | 
						|
              // Close this dialog
 | 
						|
              Navigator.pop(context);
 | 
						|
 | 
						|
              if (onReject != null) {
 | 
						|
                onReject();
 | 
						|
              }
 | 
						|
            },
 | 
						|
          ),
 | 
						|
          TextButton(
 | 
						|
            child: Text(_accept),
 | 
						|
            onPressed: () {
 | 
						|
              // Close this dialog
 | 
						|
              Navigator.pop(context);
 | 
						|
 | 
						|
              if (onAccept != null) {
 | 
						|
                onAccept();
 | 
						|
              }
 | 
						|
            },
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      );
 | 
						|
    },
 | 
						|
  );
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Construct an error dialog showing information to the user
 | 
						|
 *
 | 
						|
 * @title = Title to be displayed at the top of the dialog
 | 
						|
 * @description = Simple string description of error
 | 
						|
 * @data = Error response (e.g from server)
 | 
						|
 */
 | 
						|
Future<void> showErrorDialog(
 | 
						|
  String title, {
 | 
						|
  String description = "",
 | 
						|
  APIResponse? response,
 | 
						|
  IconData icon = TablerIcons.exclamation_circle,
 | 
						|
  Function? onDismissed,
 | 
						|
}) async {
 | 
						|
  List<Widget> children = [];
 | 
						|
 | 
						|
  if (description.isNotEmpty) {
 | 
						|
    children.add(ListTile(title: Text(description)));
 | 
						|
  } else if (response != null) {
 | 
						|
    // Look for extra error information in the provided APIResponse object
 | 
						|
    switch (response.statusCode) {
 | 
						|
      case 400: // Bad request (typically bad input)
 | 
						|
        if (response.data is Map<String, dynamic>) {
 | 
						|
          for (String field in response.asMap().keys) {
 | 
						|
            dynamic error = response.data[field];
 | 
						|
 | 
						|
            if (error is List) {
 | 
						|
              for (int ii = 0; ii < error.length; ii++) {
 | 
						|
                children.add(
 | 
						|
                  ListTile(
 | 
						|
                    title: Text(field),
 | 
						|
                    subtitle: Text(error[ii].toString()),
 | 
						|
                  ),
 | 
						|
                );
 | 
						|
              }
 | 
						|
            } else {
 | 
						|
              children.add(
 | 
						|
                ListTile(
 | 
						|
                  title: Text(field),
 | 
						|
                  subtitle: Text(response.data[field].toString()),
 | 
						|
                ),
 | 
						|
              );
 | 
						|
            }
 | 
						|
          }
 | 
						|
        } else {
 | 
						|
          children.add(
 | 
						|
            ListTile(
 | 
						|
              title: Text(L10().responseInvalid),
 | 
						|
              subtitle: Text(response.data.toString()),
 | 
						|
            ),
 | 
						|
          );
 | 
						|
        }
 | 
						|
      default:
 | 
						|
        // Unhandled server response
 | 
						|
        children.add(
 | 
						|
          ListTile(
 | 
						|
            title: Text(L10().statusCode),
 | 
						|
            subtitle: Text(response.statusCode.toString()),
 | 
						|
          ),
 | 
						|
        );
 | 
						|
 | 
						|
        children.add(
 | 
						|
          ListTile(
 | 
						|
            title: Text(L10().responseData),
 | 
						|
            subtitle: Text(response.data.toString()),
 | 
						|
          ),
 | 
						|
        );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!hasContext()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  OneContext()
 | 
						|
      .showDialog(
 | 
						|
        builder: (context) => SimpleDialog(
 | 
						|
          title: ListTile(title: Text(title), leading: Icon(icon)),
 | 
						|
          children: children,
 | 
						|
        ),
 | 
						|
      )
 | 
						|
      .then((value) {
 | 
						|
        if (onDismissed != null) {
 | 
						|
          onDismissed();
 | 
						|
        }
 | 
						|
      });
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Display a message indicating the nature of a server / API error
 | 
						|
 */
 | 
						|
Future<void> showServerError(
 | 
						|
  String url,
 | 
						|
  String title,
 | 
						|
  String description,
 | 
						|
) async {
 | 
						|
  if (!hasContext()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // We ignore error messages for certain URLs
 | 
						|
  if (url.contains("notifications")) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (title.isEmpty) {
 | 
						|
    title = L10().serverError;
 | 
						|
  }
 | 
						|
 | 
						|
  // Play a sound
 | 
						|
  final bool tones =
 | 
						|
      await InvenTreeSettingsManager().getValue(INV_SOUNDS_SERVER, true)
 | 
						|
          as bool;
 | 
						|
 | 
						|
  if (tones) {
 | 
						|
    playAudioFile("sounds/server_error.mp3");
 | 
						|
  }
 | 
						|
 | 
						|
  description += "\nURL: $url";
 | 
						|
 | 
						|
  showSnackIcon(
 | 
						|
    title,
 | 
						|
    success: false,
 | 
						|
    actionText: L10().details,
 | 
						|
    onAction: () {
 | 
						|
      showErrorDialog(
 | 
						|
        title,
 | 
						|
        description: description,
 | 
						|
        icon: TablerIcons.server,
 | 
						|
      );
 | 
						|
    },
 | 
						|
  );
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Displays an error indicating that the server returned an unexpected status code
 | 
						|
 */
 | 
						|
Future<void> showStatusCodeError(
 | 
						|
  String url,
 | 
						|
  int status, {
 | 
						|
  String details = "",
 | 
						|
}) async {
 | 
						|
  String msg = statusCodeToString(status);
 | 
						|
  String extra = url + "\n" + "${L10().statusCode}: ${status}";
 | 
						|
 | 
						|
  if (details.isNotEmpty) {
 | 
						|
    extra += "\n";
 | 
						|
    extra += details;
 | 
						|
  }
 | 
						|
 | 
						|
  showServerError(url, msg, extra);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Provide a human-readable descriptor for a particular error code
 | 
						|
 */
 | 
						|
String statusCodeToString(int status) {
 | 
						|
  switch (status) {
 | 
						|
    case 400:
 | 
						|
      return L10().response400;
 | 
						|
    case 401:
 | 
						|
      return L10().response401;
 | 
						|
    case 403:
 | 
						|
      return L10().response403;
 | 
						|
    case 404:
 | 
						|
      return L10().response404;
 | 
						|
    case 405:
 | 
						|
      return L10().response405;
 | 
						|
    case 429:
 | 
						|
      return L10().response429;
 | 
						|
    case 500:
 | 
						|
      return L10().response500;
 | 
						|
    case 501:
 | 
						|
      return L10().response501;
 | 
						|
    case 502:
 | 
						|
      return L10().response502;
 | 
						|
    case 503:
 | 
						|
      return L10().response503;
 | 
						|
    case 504:
 | 
						|
      return L10().response504;
 | 
						|
    case 505:
 | 
						|
      return L10().response505;
 | 
						|
    default:
 | 
						|
      return L10().responseInvalid + " : ${status}";
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Displays a message indicating that the server timed out on a certain request
 | 
						|
 */
 | 
						|
Future<void> showTimeoutError(String url) async {
 | 
						|
  await showServerError(url, L10().timeout, L10().noResponse);
 | 
						|
}
 |